1 /* Part of SWI-Prolog
2
3 Author: Jan Wielemaker
4 E-mail: J.Wielemaker@vu.nl
5 WWW: http://www.swi-prolog.org
6 Copyright (c) 1985-2020, University of Amsterdam
7 VU University Amsterdam
8 CWI, Amsterdam
9 All rights reserved.
10
11 Redistribution and use in source and binary forms, with or without
12 modification, are permitted provided that the following conditions
13 are met:
14
15 1. Redistributions of source code must retain the above copyright
16 notice, this list of conditions and the following disclaimer.
17
18 2. Redistributions in binary form must reproduce the above copyright
19 notice, this list of conditions and the following disclaimer in
20 the documentation and/or other materials provided with the
21 distribution.
22
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
26 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
27 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
28 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
29 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
30 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
31 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
33 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
34 POSSIBILITY OF SUCH DAMAGE.
35 */
36
37 /*#define O_DEBUG 1*/
38 #include "pl-incl.h"
39 #include "pl-comp.h"
40 #include "pl-dbref.h"
41 #include "pl-event.h"
42 #include "pl-tabling.h"
43
44 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
45 General handling of procedures: creation; adding/removing clauses;
46 finding source files, etc.
47 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
48
49 #undef LD
50 #define LD LOCAL_LD
51
52 static void resetProcedure(Procedure proc, bool isnew);
53 static atom_t autoLoader(Definition def);
54 static Procedure visibleProcedure(functor_t f, Module m ARG_LD);
55 static void freeClauseRef(ClauseRef cref);
56 static int setDynamicDefinition_unlocked(Definition def, bool isdyn);
57 static void registerDirtyDefinition(Definition def ARG_LD);
58 static void unregisterDirtyDefinition(Definition def);
59 static gen_t ddi_oldest_generation(DirtyDefInfo ddi);
60
61 #ifdef O_DEBUG
62 static int isProtectedCRef(ClauseRef cref);
63 #else
64 #define unprotectCRef(cref) (void)0
65 #define protectCRef(cref) (void)0
66 #endif
67
68 /* Enforcing this limit demands we propagate NULL from lookupProcedure()
69 through the whole system. This is not done
70 */
71 #define O_PROGLIMIT_INCL_PRED 0
72 #define SIZEOF_PROC (sizeof(struct procedure) + sizeof(struct definition))
73
74 Procedure
lookupProcedure(functor_t f,Module m)75 lookupProcedure(functor_t f, Module m)
76 { GET_LD
77 Procedure proc, oproc;
78 Definition def;
79
80 if ( (proc = lookupHTable(m->procedures, (void *)f)) )
81 { DEBUG(MSG_PROC, Sdprintf("lookupProcedure(%s) --> %s\n",
82 PL_atom_chars(m->name),
83 procedureName(proc)));
84 return proc;
85 }
86
87 #if O_PROGLIMIT_INCL_PRED
88 if ( m->code_limit &&
89 m->code_size + SIZEOF_PROC > m->code_limit )
90 { PL_error(NULL, 0, NULL, ERR_RESOURCE, ATOM_program_space);
91 return NULL;
92 }
93 #endif
94
95 proc = (Procedure) allocHeapOrHalt(sizeof(struct procedure));
96 def = (Definition) allocHeapOrHalt(sizeof(struct definition));
97 proc->definition = def;
98 proc->flags = 0;
99 proc->source_no = 0;
100
101 memset(def, 0, sizeof(*def));
102 def->functor = valueFunctor(f);
103 def->module = m;
104 def->shared = 1;
105 if ( def->functor->arity > 0 )
106 { def->impl.any.args = allocHeapOrHalt(sizeof(arg_info)*def->functor->arity);
107 memset(def->impl.any.args, 0, sizeof(arg_info)*def->functor->arity);
108 } else
109 { def->impl.any.args = NULL;
110 }
111 resetProcedure(proc, TRUE);
112
113 DEBUG(MSG_PROC_COUNT, Sdprintf("Created %s\n", procedureName(proc)));
114 ATOMIC_INC(&GD->statistics.predicates);
115 ATOMIC_ADD(&m->code_size, SIZEOF_PROC);
116
117 if ( (oproc=addHTable(m->procedures, (void *)f, proc)) == proc )
118 { return proc;
119 } else
120 { unallocProcedure(proc);
121 return oproc;
122 }
123 }
124
125
126 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
127 lingerDefinition() deals with (undefined) definitions that are replaced
128 due to importing. These definitions can be in use with other threads.
129 This needs be be improved, possibly using a technique similar to the RDF
130 database. For now, we merely collect them in a single place, so we know
131 what is going on. In addition, we can collect lingering definitions when
132 destroying a module, resulting in leak-free temporary modules.
133 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
134
135 void
lingerDefinition(Definition def)136 lingerDefinition(Definition def)
137 { ListCell c = allocHeapOrHalt(sizeof(*c));
138 Module m = def->module;
139 ListCell o;
140
141 c->value = def;
142 do
143 { o = m->lingering;
144 c->next = o;
145 } while( !COMPARE_AND_SWAP_PTR(&m->lingering, o, c) );
146
147 DEBUG(MSG_PROC_COUNT, Sdprintf("Linger %s\n", predicateName(def)));
148 ATOMIC_SUB(&m->code_size, sizeof(*def));
149 ATOMIC_DEC(&GD->statistics.predicates);
150
151 /*GC_LINGER(def);*/
152 }
153
154
155 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
156 destroyDefinition() is called to destroy predicates from destroyModule()
157 as well as destroying thread-local instantiations while a thread is
158 being terminated. In both cases is the predicate known to be not
159 referenced.
160
161 However, we cannot simply discard everything as the predicate may be
162 involved in clause-GC. Therefore we need to leave the entire cleaning to
163 clause-GC. This is somewhat slower than the old way around. The good
164 news is the it works towards more general garbage collection for code,
165 e.g., eventually we may be able to destroy modules even if we cannot
166 guarantee they are not in use.
167 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
168
169 void
destroyDefinition(Definition def)170 destroyDefinition(Definition def)
171 { ATOMIC_DEC(&GD->statistics.predicates);
172 ATOMIC_SUB(&def->module->code_size, sizeof(*def));
173
174 DEBUG(MSG_CGC_PRED,
175 Sdprintf("destroyDefinition(%s)\n", predicateName(def)));
176
177 freeCodesDefinition(def, FALSE);
178
179 if ( false(def, P_FOREIGN|P_THREAD_LOCAL) ) /* normal Prolog predicate */
180 { GET_LD
181
182 deleteIndexesDefinition(def);
183 removeClausesPredicate(def, 0, FALSE);
184 registerDirtyDefinition(def PASS_LD);
185 DEBUG(MSG_PROC_COUNT, Sdprintf("Erased %s\n", predicateName(def)));
186 def->module = NULL;
187 set(def, P_ERASED);
188
189 return;
190 } else /* foreign and thread-local */
191 { DEBUG(MSG_PROC_COUNT, Sdprintf("Unalloc foreign/thread-local: %s\n",
192 predicateName(def)));
193 #ifdef O_PLMT
194 if ( true(def, P_THREAD_LOCAL) )
195 destroyLocalDefinitions(def);
196 #endif
197 }
198
199 if ( def->tabling )
200 freeHeap(def->tabling, sizeof(*def->tabling));
201
202 DEBUG(MSG_PROC_COUNT, Sdprintf("Unalloc %s\n", predicateName(def)));
203 freeHeap(def, sizeof(*def));
204 }
205
206
207 void
unallocProcedure(Procedure proc)208 unallocProcedure(Procedure proc)
209 { Definition def = proc->definition;
210 Module m = def->module;
211
212 if ( unshareDefinition(def) == 0 )
213 { DEBUG(MSG_PROC, Sdprintf("Reclaiming %s\n", predicateName(def)));
214 destroyDefinition(def);
215 }
216 if ( proc->source_no )
217 releaseSourceFileNo(proc->source_no);
218 freeHeap(proc, sizeof(*proc));
219 if ( m )
220 ATOMIC_SUB(&m->code_size, sizeof(*proc));
221 }
222
223
224 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
225 Add (import) a definition to a module. Used by loadImport() for loading
226 states and QLF files. Must be merged with import/1.
227 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
228
229 int
importDefinitionModule(Module m,Definition def,int flags)230 importDefinitionModule(Module m, Definition def, int flags)
231 { GET_LD
232 functor_t functor = def->functor->functor;
233 Procedure proc;
234 int rc = TRUE;
235
236 LOCKMODULE(m);
237 if ( (proc = lookupHTable(m->procedures, (void *)functor)) )
238 { if ( proc->definition != def )
239 { if ( !isDefinedProcedure(proc) )
240 { Definition odef = proc->definition;
241
242 shareDefinition(def);
243 proc->definition = def;
244 if ( unshareDefinition(odef) == 0 )
245 lingerDefinition(odef);
246 } else
247 { if ( !(flags&PROC_WEAK) )
248 rc = warning("Failed to import %s into %s",
249 predicateName(def), PL_atom_chars(m->name));
250 }
251 }
252 } else
253 { proc = (Procedure) allocHeapOrHalt(sizeof(struct procedure));
254 shareDefinition(def);
255 proc->definition = def;
256 proc->flags = flags;
257 proc->source_no = 0;
258 addNewHTable(m->procedures, (void *)functor, proc);
259 }
260 UNLOCKMODULE(m);
261
262 return rc;
263 }
264
265
266
267 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
268 resetProcedure() is called by lookupProcedure() for new ones, and
269 abolishProcedure() by abolish/2.
270
271 There are two cases where a complete reset is safe: if this is an
272 unreferenced dynamic predicate and if this is a predicate that has no
273 clause-list. Such predicates can't be active and can't become active as
274 that requires clauses which, even under MT, can only be added after
275 locking the L_PREDICATE mutex.
276 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
277
278 static void
resetProcedure(Procedure proc,bool isnew)279 resetProcedure(Procedure proc, bool isnew)
280 { Definition def = proc->definition;
281
282 if ( (true(def, P_DYNAMIC) /*&& def->references == 0*/) ||
283 !def->impl.any.defined )
284 isnew = TRUE;
285
286 if ( true(def, P_DIRTYREG) )
287 ATOMIC_SUB(&GD->clauses.dirty, def->impl.clauses.number_of_clauses);
288
289 def->flags ^= def->flags & ~(SPY_ME|P_DIRTYREG);
290 if ( stringAtom(def->functor->name)[0] != '$' )
291 set(def, TRACE_ME);
292 def->impl.clauses.number_of_clauses = 0;
293 if ( def->events )
294 destroy_event_list(&def->events);
295 if ( def->tabling )
296 tbl_reset_tabling_attributes(def);
297
298 if ( isnew )
299 { deleteIndexes(&def->impl.clauses, TRUE);
300 freeCodesDefinition(def, FALSE);
301 } else
302 freeCodesDefinition(def, TRUE); /* carefully sets to S_VIRGIN */
303 }
304
305
306 Procedure
isCurrentProcedure__LD(functor_t f,Module m ARG_LD)307 isCurrentProcedure__LD(functor_t f, Module m ARG_LD)
308 { return lookupHTable(m->procedures, (void *)f);
309 }
310
311
312 ClauseRef
hasClausesDefinition(Definition def)313 hasClausesDefinition(Definition def)
314 { if ( false(def, P_FOREIGN|P_THREAD_LOCAL) &&
315 def->impl.clauses.first_clause )
316 { GET_LD
317 ClauseRef c;
318 gen_t generation = global_generation();
319
320 acquire_def(def);
321 for(c = def->impl.clauses.first_clause; c; c = c->next)
322 { Clause cl = c->value.clause;
323
324 if ( visibleClauseCNT(cl, generation) )
325 break;
326 }
327 release_def(def);
328
329 return c;
330 }
331
332 return NULL;
333 }
334
335
336 bool
isDefinedProcedure(Procedure proc)337 isDefinedProcedure(Procedure proc)
338 { Definition def = proc->definition;
339
340 if ( true(def, PROC_DEFINED) )
341 succeed;
342
343 return hasClausesDefinition(def) ? TRUE : FALSE;
344 }
345
346
347 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
348 Find a procedure for defining it. Here we check whether the procedure
349 to be defined is a system predicate.
350 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
351
352 Procedure
isStaticSystemProcedure(functor_t fd)353 isStaticSystemProcedure(functor_t fd)
354 { GET_LD
355 Procedure proc;
356
357 if ( !SYSTEM_MODE &&
358 MODULE_system &&
359 (proc=isCurrentProcedure(fd, MODULE_system)) &&
360 true(proc->definition, P_LOCKED) &&
361 false(proc->definition, P_DYNAMIC) )
362 return proc;
363
364 return NULL;
365 }
366
367
368 int
checkModifySystemProc(functor_t fd)369 checkModifySystemProc(functor_t fd)
370 { Procedure proc;
371
372 if ( (proc = isStaticSystemProcedure(fd)) &&
373 true(proc->definition, P_ISO) )
374 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
375
376 succeed;
377 }
378
379
380 int
overruleImportedProcedure(Procedure proc,Module target)381 overruleImportedProcedure(Procedure proc, Module target)
382 { GET_LD
383 Definition def = proc->definition; /* we do *not* want a thread-local version */
384
385 if ( true(def, P_AUTOLOAD) )
386 return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
387 ATOM_redefine, ATOM_imported_procedure, proc);
388
389 if ( def->module == target )
390 return TRUE;
391
392 if ( true(def->module, M_SYSTEM) )
393 { return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
394 ATOM_redefine, ATOM_built_in_procedure, proc);
395 } else
396 { if ( proc->flags & PROC_WEAK )
397 { if ( truePrologFlag(PLFLAG_WARN_OVERRIDE_IMPLICIT_IMPORT) )
398 { term_t pi;
399
400 if ( !(pi=PL_new_term_ref()) ||
401 !PL_unify_predicate(pi, proc, GP_NAMEARITY) ||
402 !printMessage(ATOM_warning,
403 PL_FUNCTOR_CHARS, "ignored_weak_import", 2,
404 PL_ATOM, target->name,
405 PL_TERM, pi) )
406 return FALSE;
407 }
408
409 abolishProcedure(proc, target);
410 return TRUE;
411 }
412 }
413
414 return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
415 ATOM_redefine, ATOM_imported_procedure, proc);
416 }
417
418
419 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
420 lookupProcedureToDefine() locates the proc for a functor in a module
421 with the aim of providing a definition for this procedure, e.g., to
422 declare it as a meta-predicate, dynamic, etc.
423 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
424
425 Procedure
lookupProcedureToDefine(functor_t def,Module m)426 lookupProcedureToDefine(functor_t def, Module m)
427 { GET_LD
428 Procedure proc;
429
430 if ( (proc = isCurrentProcedure(def, m)) )
431 { if ( !overruleImportedProcedure(proc, m) )
432 return NULL;
433
434 return proc;
435 }
436
437 if ( checkModifySystemProc(def) )
438 return lookupProcedure(def, m);
439
440 return NULL;
441 }
442
443
444 Procedure
getDefinitionProc(Definition def)445 getDefinitionProc(Definition def)
446 { GET_LD
447 Procedure proc = isCurrentProcedure(def->functor->functor, def->module);
448 assert(proc);
449 return proc;
450 }
451
452
453 void
shareDefinition(Definition def)454 shareDefinition(Definition def)
455 { int shared = ATOMIC_INC(&def->shared);
456 assert(shared > 0);
457 (void)shared;
458 }
459
460
461 int
unshareDefinition(Definition def)462 unshareDefinition(Definition def)
463 { return ATOMIC_DEC(&def->shared);
464 }
465
466
467 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
468 get_functor() translates term of the format +Name/+Arity into the
469 internal functor represenation. It fails and raises an exception on the
470 various possible format or representation errors. ISO compliant.
471 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
472
473 static int
get_arity(term_t t,int extra,int maxarity,int * arity)474 get_arity(term_t t, int extra, int maxarity, int *arity)
475 { int a;
476
477 if ( !PL_get_integer_ex(t, &a) )
478 fail;
479 if ( a < 0 )
480 return PL_error(NULL, 0, NULL, ERR_DOMAIN,
481 ATOM_not_less_than_zero, t);
482 a += extra;
483 if ( maxarity >= 0 && a > maxarity )
484 { char buf[100];
485
486 Ssprintf(buf, "limit is %d, request = %d", maxarity, a);
487
488 return PL_error(NULL, 0, buf,
489 ERR_REPRESENTATION, ATOM_max_arity);
490 }
491
492 *arity = a;
493
494 return TRUE;
495 }
496
497
498 int
get_functor(term_t descr,functor_t * fdef,Module * m,term_t h,int how)499 get_functor(term_t descr, functor_t *fdef, Module *m, term_t h, int how)
500 { GET_LD
501 term_t head;
502 int dcgpi=FALSE;
503
504 if ( !(how&GP_NOT_QUALIFIED) )
505 { head = PL_new_term_ref();
506 if ( !PL_strip_module(descr, m, head) )
507 return FALSE;
508 } else
509 { head = descr;
510 }
511
512 if ( PL_is_functor(head, FUNCTOR_divide2) ||
513 (dcgpi=PL_is_functor(head, FUNCTOR_gdiv2)) )
514 { term_t a = PL_new_term_ref();
515 atom_t name;
516 int arity = 0;
517
518 _PL_get_arg(1, head, a);
519 if ( !PL_get_atom_ex(a, &name) )
520 fail;
521 _PL_get_arg(2, head, a);
522 if ( !get_arity(a,
523 (dcgpi ? 2 : 0),
524 (how&GF_PROCEDURE) ? MAXARITY : -1,
525 &arity ) )
526 fail;
527 *fdef = PL_new_functor(name, arity);
528 if ( h )
529 PL_put_term(h, head);
530
531 succeed;
532 } else if ( !(how&GF_NAMEARITY) && PL_get_functor(head, fdef) )
533 { if ( h )
534 PL_put_term(h, head);
535
536 succeed;
537 } else
538 { if ( how & GP_TYPE_QUIET )
539 fail;
540 else
541 return PL_error(NULL, 0, NULL, ERR_TYPE,
542 ATOM_predicate_indicator, head);
543 }
544 }
545
546
547 int
get_head_functor(term_t head,functor_t * fdef,int how ARG_LD)548 get_head_functor(term_t head, functor_t *fdef, int how ARG_LD)
549 { FunctorDef fd;
550
551 if ( !PL_get_functor(head, fdef) )
552 { if ( how&GP_TYPE_QUIET )
553 fail;
554 else
555 return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_callable, head);
556 }
557
558 fd = valueFunctor(*fdef);
559
560 if ( fd->arity > MAXARITY )
561 { if ( how&GP_TYPE_QUIET )
562 { fail;
563 } else
564 { char buf[100];
565
566 Ssprintf(buf, "limit is %d, request = %d", MAXARITY, fd->arity);
567
568 return PL_error(NULL, 0, buf,
569 ERR_REPRESENTATION, ATOM_max_arity);
570 }
571 }
572
573 if ( !isCallableAtom(fd->name) )
574 { if ( how&GP_TYPE_QUIET )
575 { fail;
576 } else
577 { return PL_error(NULL, 0, NULL,
578 ERR_TYPE, ATOM_callable, head);
579 }
580 }
581
582 succeed;
583 }
584
585
586 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
587 Get the specified procedure from a Prolog argument. This argument is
588 either a head or a term of the form module:head. If `create' is TRUE, a
589 procedure is created in the module. Otherwise, the system traverses the
590 module-inheritance chain to find the existing procedure.
591 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
592
593 static Module
get_module(atom_t mname,int how ARG_LD)594 get_module(atom_t mname, int how ARG_LD)
595 { if ( mname )
596 { switch(how&GP_HOW_MASK)
597 { case GP_CREATE:
598 case GP_DEFINE:
599 return lookupModule(mname);
600 case GP_FIND:
601 case GP_FINDHERE:
602 case GP_RESOLVE:
603 { Module m;
604 if ( (m=isCurrentModule(mname)) )
605 return m;
606 return MODULE_user;
607 }
608 }
609 }
610
611 return (environment_frame ? contextModule(environment_frame)
612 : MODULE_user);
613 }
614
615
616 int
get_procedure(term_t descr,Procedure * proc,term_t h,int how)617 get_procedure(term_t descr, Procedure *proc, term_t h, int how)
618 { GET_LD
619 atom_t mname = 0;
620 Module m = NULL;
621 functor_t fdef;
622 Procedure p;
623
624 if ( (how&GP_NAMEARITY) )
625 { if ( !get_functor(descr, &fdef, &m, h,
626 GF_PROCEDURE|(how&GP_TYPE_QUIET)) )
627 fail;
628 } else
629 { term_t head = PL_new_term_ref();
630 Word p;
631
632 if ( !(p=stripModuleName(valTermRef(descr), &mname PASS_LD)) )
633 return FALSE;
634 *valTermRef(head) = linkVal(p);
635
636 if ( !(m = get_module(mname, how PASS_LD)) )
637 return FALSE;
638
639 if ( h )
640 PL_put_term(h, head);
641
642 if ( !get_head_functor(head, &fdef, how PASS_LD) )
643 fail;
644 }
645
646 switch( how & GP_HOW_MASK )
647 { case GP_CREATE:
648 *proc = lookupBodyProcedure(fdef, m);
649 break;
650 case GP_FINDHERE:
651 if ( (p = isCurrentProcedure(fdef, m)) )
652 { *proc = p;
653 break;
654 }
655 goto notfound;
656 case GP_FIND:
657 if ( (p = visibleProcedure(fdef, m PASS_LD)) )
658 { *proc = p;
659 goto out;
660 }
661 goto notfound;
662 case GP_DEFINE:
663 if ( (p = lookupProcedureToDefine(fdef, m)) )
664 { *proc = p;
665 break;
666 }
667 fail; /* permission error */
668 case GP_RESOLVE:
669 if ( (p = resolveProcedure(fdef, m)) )
670 { *proc = p;
671 break;
672 }
673 goto notfound;
674 default:
675 assert(0);
676 }
677 out:
678 succeed;
679
680 notfound:
681 if ( (how & GP_EXISTENCE_ERROR) )
682 return PL_error(NULL, 0, NULL, ERR_EXISTENCE, ATOM_procedure, descr);
683 fail;
684 }
685
686 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
687 This function implements $c_current_predicate/2. current_predicate/2
688 itself is written in Prolog, based on this function. Having dynamic
689 linking from super modules and dynamic loading from the libraries, the
690 definition of current predicate has become a difficult issue. Normally
691 it is used for meta-programming and program analysis. I think it should
692 succeed for each predicate that can be called. The current
693 implementation is VERY slow due to all Prolog overhead. This should be
694 reconsidered and probably a large part of this function should be moved
695 to C.
696 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
697
698 word
pl_current_predicate(term_t name,term_t spec,control_t h)699 pl_current_predicate(term_t name, term_t spec, control_t h)
700 { GET_LD
701 TableEnum e;
702 atom_t n;
703 functor_t f;
704 Module m = (Module) NULL;
705 Procedure proc;
706 term_t functor = PL_new_term_ref();
707
708 if ( ForeignControl(h) == FRG_CUTTED )
709 { e = ForeignContextPtr(h);
710 freeTableEnum(e);
711 succeed;
712 }
713
714 if ( !PL_strip_module__LD(spec, &m, functor, SM_NOCREATE PASS_LD) )
715 fail;
716
717 if ( !PL_get_atom(name, &n) )
718 { if ( PL_is_variable(name) )
719 n = NULL_ATOM;
720 else
721 fail;
722 }
723 if ( !PL_get_functor(functor, &f) )
724 { if ( PL_is_variable(functor) )
725 f = 0;
726 else
727 fail;
728 }
729
730 if ( ForeignControl(h) == FRG_FIRST_CALL)
731 { if ( f )
732 { if ( (proc = isCurrentProcedure(f, m)) )
733 return PL_unify_atom(name, nameFunctor(f));
734 fail;
735 }
736 e = newTableEnum(m->procedures);
737 } else
738 e = ForeignContextPtr(h);
739
740 while( advanceTableEnum(e, NULL, (void**)&proc) )
741 { FunctorDef fdef;
742
743 fdef = proc->definition->functor;
744
745 if ( (n && n != fdef->name) ||
746 !PL_unify_atom(name, fdef->name) ||
747 !PL_unify_functor(functor, fdef->functor) )
748 continue;
749
750 ForeignRedoPtr(e);
751 }
752
753 freeTableEnum(e);
754 fail;
755 }
756
757 /*******************************
758 * ISO CURRENT-PREDICATE/1 *
759 *******************************/
760
761 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
762 Patterns: ?Name/?Arity
763 ?Module:(?Name/?Arity)
764 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
765
766 typedef struct
767 { functor_t functor; /* Functor we are looking for */
768 atom_t name; /* Name of target pred */
769 int arity; /* arity of target pred */
770 int macq; /* Module is acquired */
771 Module module; /* Module to search in */
772 Module super; /* Walking along super-chain */
773 TableEnum epred; /* Predicate enumerator */
774 ModuleEnum emod; /* Module enumerator */
775 } cur_enum;
776
777
778 static int
isDefinedOrAutoloadProcedure(Procedure proc)779 isDefinedOrAutoloadProcedure(Procedure proc)
780 { Definition def = proc->definition;
781
782 if ( true(def, PROC_DEFINED|P_AUTOLOAD) )
783 succeed;
784
785 return hasClausesDefinition(def) ? TRUE : FALSE;
786 }
787
788
789 static Procedure
visibleProcedure(functor_t f,Module m ARG_LD)790 visibleProcedure(functor_t f, Module m ARG_LD)
791 { ListCell c;
792 Procedure p;
793
794 for(;;)
795 { next:
796
797 if ( (p = isCurrentProcedure(f, m)) && isDefinedOrAutoloadProcedure(p) )
798 return p;
799
800 for(c=m->supers; c; c=c->next)
801 { if ( c->next )
802 { if ( (p=visibleProcedure(f, c->value PASS_LD)) )
803 return p;
804 } else
805 { m = c->value;
806 goto next;
807 }
808 }
809
810 return NULL;
811 }
812 }
813
814
815 static
816 PRED_IMPL("current_predicate", 1, current_predicate,
817 PL_FA_TRANSPARENT|PL_FA_NONDETERMINISTIC|PL_FA_ISO)
818 { PRED_LD
819 cur_enum e0;
820 cur_enum *e;
821 int rval = FALSE;
822 term_t mt = 0; /* module-term */
823 term_t nt = 0; /* name-term */
824 term_t at = 0; /* arity-term */
825 unsigned int aextra = 0;
826 term_t spec = A1;
827
828 if ( CTX_CNTRL != FRG_CUTTED )
829 { term_t pi = PL_copy_term_ref(spec);
830
831 nt = PL_new_term_ref();
832 at = PL_new_term_ref();
833
834 while( PL_is_functor(pi, FUNCTOR_colon2) )
835 { if ( !mt )
836 mt = PL_new_term_ref();
837 _PL_get_arg(1, pi, mt);
838 _PL_get_arg(2, pi, pi);
839 }
840
841 if ( PL_is_functor(pi, FUNCTOR_divide2) )
842 { _PL_get_arg(1, pi, nt);
843 _PL_get_arg(2, pi, at);
844 } else if ( PL_is_functor(pi, FUNCTOR_gdiv2) )
845 { _PL_get_arg(1, pi, nt);
846 _PL_get_arg(2, pi, at);
847 aextra = 2;
848 } else if ( PL_is_variable(pi) )
849 { term_t a;
850
851 if ( !(a=PL_new_term_ref()) ||
852 !PL_cons_functor(a, FUNCTOR_divide2, nt, at) ||
853 !PL_unify(pi, a) )
854 return FALSE; /* resource error */
855 } else
856 goto typeerror;
857 }
858
859 switch( CTX_CNTRL )
860 { case FRG_FIRST_CALL:
861 { e = &e0;
862 memset(e, 0, sizeof(*e));
863
864 if ( !PL_get_atom(nt, &e->name) )
865 { if ( !PL_is_variable(nt) )
866 goto typeerror;
867 }
868 if ( PL_get_integer(at, &e->arity) )
869 { if ( e->arity < 0 )
870 return PL_error(NULL, 0, NULL, ERR_DOMAIN,
871 ATOM_not_less_than_zero, at);
872
873 e->arity += aextra;
874 } else
875 { if ( !PL_is_variable(at) )
876 goto typeerror;
877 e->arity = -1;
878 }
879
880 if ( e->name && e->arity >= 0 )
881 e->functor = PL_new_functor(e->name, e->arity);
882
883 if ( mt )
884 { atom_t mname;
885
886 if ( PL_is_variable(mt) )
887 { if ( !(e->emod = newModuleEnum(0)) )
888 return PL_no_memory();
889
890 if ( !(e->module=advanceModuleEnum(e->emod)) )
891 return FALSE;
892 } else if ( PL_get_atom_ex(mt, &mname) )
893 { if ( !(e->module = acquireModule(mname)) )
894 return FALSE;
895 e->macq = TRUE;
896 } else
897 { return FALSE;
898 }
899 } else
900 { if ( environment_frame )
901 e->module = contextModule(environment_frame);
902 else
903 e->module = MODULE_user;
904 e->super = e->module;
905 }
906
907 if ( e->functor )
908 { if ( !e->emod ) /* fully specified */
909 return (visibleProcedure(e->functor, e->module PASS_LD) != NULL);
910 } else
911 { e->epred = newTableEnum(e->module->procedures);
912 }
913
914 e = allocForeignState(sizeof(*e));
915 *e = e0;
916 break;
917 }
918 case FRG_REDO:
919 e = CTX_PTR;
920 break;
921 case FRG_CUTTED:
922 { e = CTX_PTR;
923 rval = TRUE;
924 goto clean;
925 }
926 default:
927 { e = NULL;
928 assert(0);
929 }
930 }
931
932 for(;;)
933 { if ( e->functor ) /* _M:foo/2 */
934 { if ( visibleProcedure(e->functor, e->module PASS_LD) )
935 { PL_unify_atom(mt, e->module->name);
936
937 if ( (e->module=advanceModuleEnum(e->emod)) )
938 { ForeignRedoPtr(e);
939 } else
940 { rval = TRUE;
941 goto clean;
942 }
943 }
944 } else
945 { functor_t f;
946 Procedure proc;
947 while( advanceTableEnum(e->epred, (void**)&f, (void**)&proc) )
948 { FunctorDef fd = valueFunctor(f);
949
950 if ( (!e->name || e->name == fd->name) &&
951 (e->arity < 0 || (unsigned int)e->arity == fd->arity) &&
952 fd->arity >= aextra &&
953 isDefinedProcedure(proc) )
954 { if ( mt )
955 PL_unify_atom(mt, e->module->name);
956 if ( !e->name )
957 PL_unify_atom(nt, fd->name);
958 if ( e->arity < 0 )
959 PL_unify_integer(at, fd->arity-aextra);
960
961 ForeignRedoPtr(e);
962 }
963 }
964 }
965
966 if ( e->emod ) /* enumerate all modules */
967 { Module m;
968 while( (m=advanceModuleEnum(e->emod)) )
969 {
970 /* skip hidden modules */
971 if ( SYSTEM_MODE ||
972 m->name == ATOM_system ||
973 m->class != ATOM_system )
974 break;
975 }
976 if ( m )
977 e->super = e->module = m;
978 else
979 break;
980 } else if ( !e->functor && e->super && e->super->supers )
981 { e->super = e->super->supers->value; /* advance to user-modules */
982 /* TBD: handle multiple supers */
983 } else
984 break; /* finished all modules */
985
986 if ( !e->functor )
987 { freeTableEnum(e->epred);
988 e->epred = newTableEnum(e->super->procedures);
989 }
990 }
991
992 clean:
993 if ( e )
994 { if ( e->epred )
995 freeTableEnum(e->epred);
996 if ( e->emod )
997 freeModuleEnum(e->emod);
998 if ( e->module && e->macq )
999 releaseModule(e->module);
1000 freeForeignState(e, sizeof(*e));
1001 }
1002
1003 return rval;
1004
1005 typeerror:
1006 return PL_error(NULL, 0, NULL, ERR_TYPE,
1007 ATOM_predicate_indicator, spec);
1008 }
1009
1010
1011 /*******************************
1012 * CLAUSE REFERENCES *
1013 *******************************/
1014
1015 #ifdef O_DEBUG
1016 static Table retracted_clauses = NULL;
1017
1018 static void
registerRetracted(Clause cl)1019 registerRetracted(Clause cl)
1020 { DEBUG(MSG_CGC_CREF_PL, Sdprintf("/**/ r(%p).\n", cl));
1021 DEBUG(MSG_CGC_CREF_TRACK,
1022 { if ( !retracted_clauses )
1023 retracted_clauses = newHTable(1024);
1024 addNewHTable(retracted_clauses, cl, (void*)1);
1025 });
1026 }
1027
1028 static void
reclaimRetracted(Clause cl)1029 reclaimRetracted(Clause cl)
1030 { DEBUG(MSG_CGC_CREF_TRACK,
1031 { void *v = deleteHTable(retracted_clauses, cl);
1032 if ( v != (void*)1 && GD->cleaning == CLN_NORMAL )
1033 { Definition def = cl->predicate;
1034 Sdprintf("reclaim not retracted from %s\n", predicateName(def));
1035 }
1036 });
1037 }
1038
1039 void
listNotReclaimed(void)1040 listNotReclaimed(void)
1041 { if ( retracted_clauses )
1042 { for_table(retracted_clauses, n, v,
1043 { Clause cl = n;
1044 Definition def = cl->predicate;
1045
1046 Sdprintf("%p from %s\n", cl, predicateName(def));
1047 });
1048 }
1049 }
1050
1051 #else
1052
1053 #define registerRetracted(cl) (void)0
1054 #define reclaimRetracted(cl) (void)0
1055
1056 #endif
1057
1058
1059 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1060 Clause references are used to link clauses from the main predicate
1061 clause list as well as from additional indexes. They form a linked list
1062 of clauses, indexed according to a specific key. This key is deduced
1063 from the first argument for the main predicate clause list or from
1064 alternative arguments for secondary clause lists.
1065
1066 Traversing a list of clause references traverses the ->next pointer,
1067 possibly matches the key and then looks into the associated ->clause for
1068 the born/died generations. If a clause erased, cleanDefinition() removes
1069 the references to it from the linked lists and adds them to
1070 GD->lingering_clauses, which uses d.gnext to link them together rather
1071 then ->next because ->next might be used by some other thread traversing
1072 the clause chain.
1073 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1074
1075 void
acquire_clause(Clause cl)1076 acquire_clause(Clause cl)
1077 { ATOMIC_INC(&cl->references);
1078 }
1079
1080 void
release_clause(Clause cl)1081 release_clause(Clause cl)
1082 { if ( ATOMIC_DEC(&cl->references) == 0 )
1083 { size_t size = sizeofClause(cl->code_size) + SIZEOF_CREF_CLAUSE;
1084
1085 ATOMIC_SUB(&GD->clauses.erased_size, size);
1086 ATOMIC_DEC(&GD->clauses.erased);
1087
1088 reclaimRetracted(cl);
1089 freeClause(cl);
1090 }
1091 }
1092
1093
1094 ClauseRef
newClauseRef(Clause clause,word key)1095 newClauseRef(Clause clause, word key)
1096 { ClauseRef cref = allocHeapOrHalt(SIZEOF_CREF_CLAUSE);
1097
1098 DEBUG(MSG_CGC_CREF_PL,
1099 Sdprintf("/**/ a(%p, %p, %d, '%s').\n",
1100 cref, clause, clause->references,
1101 predicateName(clause->predicate)));
1102
1103 cref->next = NULL;
1104 cref->d.key = key;
1105 cref->value.clause = clause;
1106 acquire_clause(clause);
1107
1108 return cref;
1109 }
1110
1111
1112 static void
freeClauseRef(ClauseRef cref)1113 freeClauseRef(ClauseRef cref)
1114 { Clause cl = cref->value.clause;
1115
1116 DEBUG(MSG_CGC_CREF_PL,
1117 Sdprintf("/**/ d(%p, %p, %d).\n",
1118 cref, cl, (int)cl->references));
1119
1120 release_clause(cl);
1121
1122 freeHeap(cref, SIZEOF_CREF_CLAUSE);
1123 }
1124
1125
1126 void
lingerClauseRef(ClauseRef cref)1127 lingerClauseRef(ClauseRef cref)
1128 { ClauseRef o;
1129
1130 DEBUG(0,
1131 if ( isProtectedCRef(cref) )
1132 { Clause cl = cref->value.clause;
1133 Sdprintf("Ditching clause for %s %lld-%lld\n",
1134 predicateName(cl->predicate),
1135 cl->generation.created,
1136 cl->generation.erased);
1137 assert(0);
1138 });
1139
1140 do
1141 { o = GD->clauses.lingering;
1142 cref->d.gnext = o;
1143 } while(!COMPARE_AND_SWAP_PTR(&GD->clauses.lingering, o, cref) );
1144
1145 ATOMIC_INC(&GD->clauses.lingering_count);
1146 }
1147
1148
1149 static int activePredicate(const Definition *defs, const Definition def);
1150
1151 static void
gcClauseRefs(void)1152 gcClauseRefs(void)
1153 { ClauseRef cref;
1154
1155 if ( !(cref = GD->clauses.lingering) ||
1156 !COMPARE_AND_SWAP_PTR(&GD->clauses.lingering, cref, NULL) )
1157 return; /* no work or someone else doing it */
1158 GD->clauses.lingering_count = 0;
1159
1160 if ( cref )
1161 { ClauseRef next;
1162 Definition *active_defs = predicates_in_use();
1163 int freed = 0;
1164 int kept = 0;
1165
1166 for( ; cref; cref = next)
1167 { Definition def;
1168
1169 next = cref->d.gnext;
1170 def = cref->value.clause->predicate;
1171 if ( !activePredicate(active_defs, def) )
1172 { freeClauseRef(cref);
1173 freed++;
1174 } else
1175 { lingerClauseRef(cref);
1176 kept++;
1177 }
1178 }
1179
1180 if ( active_defs )
1181 PL_free(active_defs);
1182
1183 DEBUG(MSG_CGC_CREF, Sdprintf("GC clause references: freed %d, kept %d\n",
1184 freed, kept));
1185 }
1186 }
1187
1188 static int
activePredicate(const Definition * defs,const Definition def)1189 activePredicate(const Definition *defs, const Definition def)
1190 { if ( defs )
1191 { for( ; *defs; defs++)
1192 { if ( *defs == def )
1193 return TRUE;
1194 }
1195 }
1196
1197 return FALSE;
1198 }
1199
1200 static void
setLastModifiedPredicate(Definition def,gen_t gen)1201 setLastModifiedPredicate(Definition def, gen_t gen)
1202 { Module m = def->module;
1203 gen_t lmm;
1204
1205 def->last_modified = gen;
1206
1207 do
1208 { lmm = m->last_modified;
1209 } while ( lmm < gen &&
1210 !COMPARE_AND_SWAP_UINT64(&m->last_modified, lmm, gen) );
1211 }
1212
1213
1214 /*******************************
1215 * ASSERT *
1216 *******************************/
1217
1218 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1219 Assert a clause to a procedure. Where askes to assert either at the head
1220 or at the tail of the clause list.
1221
1222 The `where` argument is one of
1223
1224 - CL_START (asserta)
1225 - CL_END (assertz)
1226 - The clause reference before which the clause must be inserted.
1227 This is used by reconsult.
1228
1229 (*) This function updates the indexing information. If we have a static
1230 procedure, it deletes the supervisor. This is probably a bit rough, but
1231 deals with -for example- clauses for term_expansion/2. After the first
1232 definition this will be called and an S_TRUSTME supervisor will be
1233 installed, causing further clauses to have no effect.
1234 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1235
1236 ClauseRef
assertDefinition(Definition def,Clause clause,ClauseRef where ARG_LD)1237 assertDefinition(Definition def, Clause clause, ClauseRef where ARG_LD)
1238 { word key;
1239 ClauseRef cref;
1240
1241 if ( def->events &&
1242 !predicate_update_event(def,
1243 where == CL_START ? ATOM_asserta : ATOM_assertz,
1244 clause PASS_LD) )
1245 return NULL;
1246
1247 argKey(clause->codes, 0, &key);
1248 cref = newClauseRef(clause, key);
1249
1250 LOCKDEF(def);
1251 acquire_def(def);
1252 if ( !def->impl.clauses.last_clause )
1253 { def->impl.clauses.first_clause = def->impl.clauses.last_clause = cref;
1254 } else if ( where == CL_START || where == def->impl.clauses.first_clause )
1255 { cref->next = def->impl.clauses.first_clause;
1256 def->impl.clauses.first_clause = cref;
1257 } else if ( where == CL_END )
1258 { ClauseRef last = def->impl.clauses.last_clause;
1259
1260 last->next = cref;
1261 def->impl.clauses.last_clause = cref;
1262 } else /* insert before */
1263 { ClauseRef cr;
1264
1265 for(cr = def->impl.clauses.first_clause; cr; cr = cr->next)
1266 { if ( cr->next == where )
1267 { cref->next = where;
1268 cr->next = cref;
1269 break;
1270 }
1271 }
1272 assert(cr);
1273 }
1274
1275 def->impl.clauses.number_of_clauses++;
1276 if ( false(clause, UNIT_CLAUSE) )
1277 def->impl.clauses.number_of_rules++;
1278 if ( true(def, P_DIRTYREG) )
1279 ATOMIC_INC(&GD->clauses.dirty);
1280 #ifdef O_LOGICAL_UPDATE
1281 clause->generation.created = next_global_generation();
1282 clause->generation.erased = GEN_MAX; /* infinite */
1283 setLastModifiedPredicate(def, clause->generation.created);
1284 #endif
1285
1286 if ( false(def, P_DYNAMIC|P_LOCKED_SUPERVISOR) ) /* see (*) above */
1287 freeCodesDefinition(def, TRUE);
1288
1289 addClauseToIndexes(def, clause, where);
1290 release_def(def);
1291 DEBUG(CHK_SECURE, checkDefinition(def));
1292 UNLOCKDEF(def);
1293
1294 return cref;
1295 }
1296
1297
1298 ClauseRef
assertProcedure(Procedure proc,Clause clause,ClauseRef where ARG_LD)1299 assertProcedure(Procedure proc, Clause clause, ClauseRef where ARG_LD)
1300 { Definition def = getProcDefinition(proc);
1301
1302 return assertDefinition(def, clause, where PASS_LD);
1303 }
1304
1305
1306 /* Abolish a procedure. Referenced clauses are unlinked and left
1307 dangling in the dark until the procedure referencing it deletes it.
1308
1309 Since we have a foreign language interface we will allow to abolish
1310 foreign predicates as well. Permission testing should be done by
1311 the caller.
1312
1313 ** Sun Apr 17 16:18:50 1988 jan@swivax.UUCP (Jan Wielemaker) */
1314
1315 bool
abolishProcedure(Procedure proc,Module module)1316 abolishProcedure(Procedure proc, Module module)
1317 { GET_LD
1318 Definition def = proc->definition;
1319
1320 DEBUG(MSG_PROC, Sdprintf("abolishProcedure(%s)\n", predicateName(def)));
1321
1322 startCritical;
1323 LOCKDEF(def);
1324 if ( def->module != module ) /* imported predicate; remove link */
1325 { Definition ndef = allocHeapOrHalt(sizeof(*ndef));
1326
1327 memset(ndef, 0, sizeof(*ndef));
1328 ndef->functor = def->functor; /* should be merged with */
1329 ndef->impl.any.args = allocHeapOrHalt(sizeof(*ndef->impl.any.args)*
1330 def->functor->arity);
1331 ndef->module = module; /* lookupProcedure()!! */
1332 ndef->codes = SUPERVISOR(virgin);
1333 proc->definition = ndef;
1334 ATOMIC_INC(&GD->statistics.predicates);
1335 ATOMIC_ADD(&module->code_size, sizeof(*ndef));
1336 resetProcedure(proc, TRUE);
1337 } else if ( true(def, P_FOREIGN) ) /* foreign: make normal */
1338 { def->impl.clauses.first_clause = def->impl.clauses.last_clause = NULL;
1339 resetProcedure(proc, TRUE);
1340 } else if ( true(def, P_THREAD_LOCAL) )
1341 { UNLOCKDEF(def);
1342 if ( !endCritical )
1343 return FALSE;
1344 return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
1345 ATOM_modify, ATOM_thread_local_procedure, proc);
1346 } else /* normal Prolog procedure */
1347 { removeClausesPredicate(def, 0, FALSE);
1348 setDynamicDefinition_unlocked(def, FALSE);
1349 resetProcedure(proc, FALSE);
1350 }
1351
1352 DEBUG(CHK_SECURE, checkDefinition(def));
1353 UNLOCKDEF(def);
1354
1355 return endCritical;
1356 }
1357
1358
1359 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1360 Remove (mark for deletion) all clauses that come from the given
1361 source-file or any sourcefile. Note that thread-local predicates don't
1362 have clauses from files, so we don't need to bother. Returns number of
1363 clauses that has been deleted.
1364
1365 MT: Caller must hold L_PREDICATE
1366 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1367
1368 size_t
removeClausesPredicate(Definition def,int sfindex,int fromfile)1369 removeClausesPredicate(Definition def, int sfindex, int fromfile)
1370 { GET_LD
1371 ClauseRef c;
1372 size_t deleted = 0;
1373 size_t memory = 0;
1374 gen_t update = global_generation()+1;
1375
1376 if ( true(def, P_THREAD_LOCAL) )
1377 return 0;
1378
1379 acquire_def(def);
1380 for(c = def->impl.clauses.first_clause; c; c = c->next)
1381 { Clause cl = c->value.clause;
1382
1383 if ( (sfindex == 0 || sfindex == cl->owner_no) &&
1384 (!fromfile || cl->line_no > 0) &&
1385 false(cl, CL_ERASED) )
1386 { set(cl, CL_ERASED);
1387 #ifdef O_LOGICAL_UPDATE
1388 cl->generation.erased = update;
1389 #endif
1390 deleted++;
1391 memory += sizeofClause(cl->code_size) + SIZEOF_CREF_CLAUSE;
1392 def->impl.clauses.number_of_clauses--;
1393 def->impl.clauses.erased_clauses++;
1394 if ( false(cl, UNIT_CLAUSE) )
1395 def->impl.clauses.number_of_rules--;
1396 deleteActiveClauseFromIndexes(def, cl);
1397 registerRetracted(cl);
1398 }
1399 }
1400 release_def(def);
1401
1402 if ( global_generation() < update )
1403 next_global_generation();
1404
1405 if ( deleted )
1406 { ATOMIC_SUB(&def->module->code_size, memory);
1407 ATOMIC_ADD(&GD->clauses.erased_size, memory);
1408 ATOMIC_ADD(&GD->clauses.erased, deleted);
1409 if( true(def, P_DIRTYREG) )
1410 ATOMIC_SUB(&GD->clauses.dirty, deleted);
1411
1412 registerDirtyDefinition(def PASS_LD);
1413 DEBUG(CHK_SECURE, checkDefinition(def));
1414 }
1415
1416 return deleted;
1417 }
1418
1419
1420 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1421 Retract a clause from a dynamic procedure. Called from erase/1,
1422 retract/1 and retractall/1. Returns FALSE if the clause was already
1423 retracted or retract is vetoed by the update event handling. This is
1424 also used by trie_gen_compiled/3 to get rid of the clauses that
1425 represent tries.
1426 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1427
1428 int
retractClauseDefinition(Definition def,Clause clause)1429 retractClauseDefinition(Definition def, Clause clause)
1430 { GET_LD
1431 size_t size = sizeofClause(clause->code_size) + SIZEOF_CREF_CLAUSE;
1432
1433 if ( def->events &&
1434 !predicate_update_event(def, ATOM_retract, clause PASS_LD) )
1435 return FALSE;
1436
1437 LOCKDEF(def);
1438 if ( true(clause, CL_ERASED) )
1439 { UNLOCKDEF(def);
1440 return FALSE;
1441 }
1442
1443 DEBUG(CHK_SECURE, checkDefinition(def));
1444 set(clause, CL_ERASED);
1445 deleteActiveClauseFromIndexes(def, clause); /* just updates "dirtyness" */
1446 def->impl.clauses.number_of_clauses--;
1447 def->impl.clauses.erased_clauses++;
1448 if ( false(clause, UNIT_CLAUSE) )
1449 def->impl.clauses.number_of_rules--;
1450 #ifdef O_LOGICAL_UPDATE
1451 clause->generation.erased = next_global_generation();
1452 setLastModifiedPredicate(def, clause->generation.erased);
1453 #endif
1454 DEBUG(CHK_SECURE, checkDefinition(def));
1455 UNLOCKDEF(def);
1456
1457 /* update stats */
1458 registerRetracted(clause);
1459 if ( true(clause, DBREF_CLAUSE) )
1460 ATOMIC_INC(&GD->clauses.db_erased_refs);
1461
1462 ATOMIC_SUB(&def->module->code_size, size);
1463 ATOMIC_ADD(&GD->clauses.erased_size, size);
1464 ATOMIC_INC(&GD->clauses.erased);
1465 if( true(def, P_DIRTYREG) )
1466 ATOMIC_DEC(&GD->clauses.dirty);
1467
1468 registerDirtyDefinition(def PASS_LD);
1469
1470 return TRUE;
1471 }
1472
1473
1474 void
unallocClause(Clause c)1475 unallocClause(Clause c)
1476 { ATOMIC_SUB(&GD->statistics.codes, c->code_size);
1477 ATOMIC_DEC(&GD->statistics.clauses);
1478 if ( c->source_no ) /* set by assert_term() */
1479 { if ( c->owner_no != c->source_no )
1480 releaseSourceFileNo(c->owner_no);
1481 releaseSourceFileNo(c->source_no);
1482 }
1483
1484 #ifdef ALLOC_DEBUG
1485 #define ALLOC_FREE_MAGIC 0xFB
1486 size_t size = sizeofClause(c->code_size);
1487 memset(c, ALLOC_FREE_MAGIC, size);
1488 #endif
1489
1490 PL_free(c);
1491 }
1492
1493
1494 #ifdef O_DEBUG_ATOMGC
1495 void
unregister_atom_clause(atom_t a)1496 unregister_atom_clause(atom_t a)
1497 { PL_unregister_atom(a);
1498 }
1499
1500 void
register_atom_clause(atom_t a)1501 register_atom_clause(atom_t a)
1502 { PL_register_atom(a);
1503 }
1504 #endif
1505
1506 void
freeClause(Clause c)1507 freeClause(Clause c)
1508 {
1509 #ifdef O_ATOMGC
1510 #ifdef O_DEBUG_ATOMGC
1511 forAtomsInClause(c, unregister_atom_clause);
1512 #else
1513 forAtomsInClause(c, PL_unregister_atom);
1514 #endif
1515 #endif
1516
1517 if ( true(c, DBREF_CLAUSE) ) /* will be freed from symbol */
1518 set(c, DBREF_ERASED_CLAUSE);
1519 else
1520 unallocClause(c);
1521 }
1522
1523
1524
1525 static int WUNUSED /* FALSE if there was an error */
announceErasedClause(Clause clause)1526 announceErasedClause(Clause clause)
1527 { return ( clearBreakPointsClause(clause) >= 0 &&
1528 callEventHook(PLEV_ERASED_CLAUSE, clause) );
1529 }
1530
1531
1532 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1533 cleanDefinition()
1534 This function has two tasks. If the predicate needs to be rehashed,
1535 this is done and all erased clauses from the predicate are returned
1536 as a linked list.
1537
1538 We cannot delete the clauses immediately as the debugger requires a
1539 call-back and we have the L_PREDICATE mutex when running this code.
1540
1541 find_prev() finds the real previous clause. The not-locked loop of
1542 cleanDefinition() keep track of this, but in the meanwhile the previous
1543 may change due to an assert. Now that we are in the locked region we can
1544 search for the real previous, using the one from cleanDefinition() as
1545 the likely candidate.
1546
1547 The `ddi->oldest_generation` contains the latest marked generation that
1548 was found or GEN_MAX if the predicate is not active. The `start`
1549 generation contains the generation when pl_garbage_collect_clauses() was
1550 started.
1551 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1552
1553 static int mustCleanDefinition(const Definition def);
1554
1555 static ClauseRef
find_prev(Definition def,ClauseRef prev,ClauseRef cref)1556 find_prev(Definition def, ClauseRef prev, ClauseRef cref)
1557 { if ( (!prev && def->impl.clauses.first_clause == cref) ||
1558 ( prev && prev->next == cref) )
1559 return prev;
1560
1561 DEBUG(MSG_PROC, Sdprintf("Fixing prev\n"));
1562 for(prev = def->impl.clauses.first_clause; prev; prev = prev->next)
1563 { if ( prev->next == cref )
1564 return prev;
1565 }
1566
1567 assert(0);
1568 return NULL;
1569 }
1570
1571
1572
1573 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1574 (*) This used to be acquire_def(def), but announceErasedClause may call
1575 Prolog, leading to nested acquired definition. This is not needed anyway
1576 as the acquired definition is only used by clause GC, we are inside
1577 clause GC and clause GC calls cannot run in parallel.
1578 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1579
1580 static size_t
cleanDefinition(Definition def,DirtyDefInfo ddi,gen_t start,int * rcp)1581 cleanDefinition(Definition def, DirtyDefInfo ddi, gen_t start, int *rcp)
1582 { size_t removed = 0;
1583
1584 DEBUG(CHK_SECURE,
1585 LOCKDEF(def);
1586 checkDefinition(def);
1587 UNLOCKDEF(def));
1588
1589 if ( mustCleanDefinition(def) && true(ddi, DDI_MARKING) )
1590 { ClauseRef cref, prev = NULL;
1591 #if O_DEBUG
1592 int left = 0;
1593 #endif
1594
1595 assert(GD->clauses.cgc_active); /* See (*) */
1596
1597 for(cref = def->impl.clauses.first_clause;
1598 cref && def->impl.clauses.erased_clauses;
1599 cref=cref->next)
1600 { Clause cl = cref->value.clause;
1601
1602 if ( true(cl, CL_ERASED) && ddi_is_garbage(ddi, start, cl) )
1603 { if ( !announceErasedClause(cl) )
1604 *rcp = FALSE;
1605
1606 LOCKDEF(def);
1607 prev = find_prev(def, prev, cref);
1608 if ( !prev )
1609 { def->impl.clauses.first_clause = cref->next;
1610 if ( !cref->next )
1611 def->impl.clauses.last_clause = NULL;
1612 } else
1613 { prev->next = cref->next;
1614 if ( cref->next == NULL)
1615 def->impl.clauses.last_clause = prev;
1616 }
1617 removed++;
1618 def->impl.clauses.erased_clauses--;
1619 UNLOCKDEF(def);
1620
1621 lingerClauseRef(cref);
1622 } else
1623 { prev = cref;
1624 DEBUG(MSG_PROC, left++);
1625 }
1626 }
1627 if ( removed )
1628 { LOCKDEF(def);
1629 cleanClauseIndexes(def, &def->impl.clauses, ddi, start);
1630 UNLOCKDEF(def);
1631 }
1632 gen_t active = ddi_oldest_generation(ddi);
1633 if ( start < active )
1634 active = start;
1635 free_lingering(&def->lingering, active);
1636
1637 DEBUG(CHK_SECURE,
1638 LOCKDEF(def);
1639 checkDefinition(def);
1640 UNLOCKDEF(def));
1641
1642 DEBUG(MSG_PROC,
1643 Sdprintf("cleanDefinition(%s): removed %d, left %d, erased %d\n",
1644 predicateName(def), removed, left,
1645 def->impl.clauses.erased_clauses));
1646 }
1647
1648 return removed;
1649 }
1650
1651
1652 static int
mustCleanDefinition(const Definition def)1653 mustCleanDefinition(const Definition def)
1654 { return ( def->impl.clauses.erased_clauses > 0 );
1655 }
1656
1657
1658 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1659 Finalize a reloaded predicate. This (nearly) atomically makes the new
1660 definition visible.
1661
1662 (*) Updating the generation to one in the future and incrementing at the
1663 end makes the transaction truely atomic. In the current implementation
1664 though, another thread may increment the generation as well, making our
1665 changes not entirely atomic. The lock-free retry mechanism won't work to
1666 fix this. Only a true lock for modifying the generation can fix this.
1667 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1668
1669 void
reconsultFinalizePredicate(sf_reload * rl,Definition def,p_reload * r ARG_LD)1670 reconsultFinalizePredicate(sf_reload *rl, Definition def, p_reload *r ARG_LD)
1671 { if ( true(r, P_MODIFIED) )
1672 { ClauseRef cref;
1673 gen_t update = global_generation()+1; /* see (*) */
1674 size_t deleted = 0;
1675 size_t added = 0;
1676 size_t memory = 0;
1677
1678 acquire_def(def);
1679 for(cref = def->impl.clauses.first_clause; cref; cref=cref->next)
1680 { Clause cl = cref->value.clause;
1681
1682 if ( cl->generation.erased == rl->reload_gen && false(cl, CL_ERASED) )
1683 { set(cl, CL_ERASED);
1684 cl->generation.erased = update;
1685 deleted++;
1686 memory += sizeofClause(cl->code_size) + SIZEOF_CREF_CLAUSE;
1687 def->impl.clauses.number_of_clauses--;
1688 def->impl.clauses.erased_clauses++;
1689 if ( false(cl, UNIT_CLAUSE) )
1690 def->impl.clauses.number_of_rules--;
1691 if ( true(def, P_DYNAMIC) )
1692 deleteActiveClauseFromIndexes(def, cl);
1693 registerRetracted(cl);
1694 } else if ( cl->generation.created == rl->reload_gen )
1695 { cl->generation.created = update;
1696 added++;
1697 }
1698 }
1699 release_def(def);
1700
1701 if ( global_generation() < update ) /* see (*) */
1702 next_global_generation();
1703
1704 DEBUG(MSG_RECONSULT_CLAUSE,
1705 Sdprintf("%s: added %ld, deleted %ld clauses "
1706 "at gen=%ld, GD->gen = %lld\n",
1707 predicateName(def), (long)added, (long)deleted,
1708 (long)update, (int64_t)global_generation()));
1709
1710 if ( added || deleted )
1711 setLastModifiedPredicate(def, update);
1712
1713 if ( deleted )
1714 { ATOMIC_SUB(&def->module->code_size, memory);
1715 ATOMIC_ADD(&GD->clauses.erased_size, memory);
1716 ATOMIC_ADD(&GD->clauses.erased, deleted);
1717 if( true(def, P_DIRTYREG) )
1718 ATOMIC_SUB(&GD->clauses.dirty, deleted);
1719
1720 registerDirtyDefinition(def PASS_LD);
1721 }
1722
1723 DEBUG(CHK_SECURE, checkDefinition(def));
1724 }
1725 }
1726
1727
1728 /*******************************
1729 * META PREDICATE *
1730 *******************************/
1731
1732 /** meta_predicate :HeadList is det.
1733
1734 Declaration for meta-predicates. The declaration fills the meta_info
1735 field of a definition as well as the P_META and P_TRANSPARENT flags.
1736 P_META indicates that meta_info is valid. P_TRANSPARENT indicates that
1737 the declaration contains at least one meta-argument (: or 0..9).
1738
1739 @param HeadList Comma separated list of predicates heads, where each
1740 predicate head has arguments 0..9, :,+,-,?
1741 */
1742
1743 int
isTransparentMetamask(Definition def,arg_info * args)1744 isTransparentMetamask(Definition def, arg_info *args)
1745 { size_t i, arity = def->functor->arity;
1746 int transparent = FALSE;
1747
1748 for(i=0; i<arity && !transparent; i++)
1749 { int ma = args[i].meta;
1750 if ( MA_NEEDS_TRANSPARENT(ma) )
1751 transparent = TRUE;
1752 }
1753
1754 return transparent;
1755 }
1756
1757
1758 void
setMetapredicateMask(Definition def,arg_info * args)1759 setMetapredicateMask(Definition def, arg_info *args)
1760 { size_t i, arity = def->functor->arity;
1761
1762 for(i=0; i<arity; i++)
1763 def->impl.any.args[i].meta = args[i].meta;
1764
1765 if ( isTransparentMetamask(def, args) )
1766 set(def, P_TRANSPARENT);
1767 else
1768 clear(def, P_TRANSPARENT);
1769 set(def, P_META);
1770 }
1771
1772
1773 static int
meta_declaration(term_t spec)1774 meta_declaration(term_t spec)
1775 { GET_LD
1776 term_t head = PL_new_term_ref();
1777 term_t arg = PL_new_term_ref();
1778 Procedure proc;
1779 atom_t name;
1780 size_t i, arity;
1781
1782 if ( !get_procedure(spec, &proc, head, GP_DEFINE) ||
1783 !PL_get_name_arity(head, &name, &arity) )
1784 return FALSE;
1785
1786 #ifdef _MSC_VER
1787 arg_info *args = alloca(arity*sizeof(*args));
1788 #else
1789 arg_info args[arity]; /* GCC dynamic allocation */
1790 #endif
1791
1792 for(i=0; i<arity; i++)
1793 { atom_t ma;
1794
1795 _PL_get_arg(i+1, head, arg);
1796
1797 if ( PL_is_integer(arg) )
1798 { int e;
1799
1800 if ( !PL_get_integer_ex(arg, &e) )
1801 return FALSE;
1802 if ( e < 0 || e > 9 )
1803 { domain_error:
1804 return PL_error(NULL, 0, "0..9",
1805 ERR_DOMAIN, ATOM_meta_argument_specifier, arg);
1806 }
1807 args[i].meta = e;
1808 } else if ( PL_get_atom(arg, &ma) )
1809 { int m;
1810
1811 if ( ma == ATOM_plus ) m = MA_NONVAR;
1812 else if ( ma == ATOM_minus ) m = MA_VAR;
1813 else if ( ma == ATOM_question_mark ) m = MA_ANY;
1814 else if ( ma == ATOM_star ) m = MA_ANY; /* * mapped to ? */
1815 else if ( ma == ATOM_colon ) m = MA_META;
1816 else if ( ma == ATOM_hat ) m = MA_HAT;
1817 else if ( ma == ATOM_gdiv ) m = MA_DCG;
1818 else goto domain_error;
1819
1820 args[i].meta = m;
1821 } else
1822 { return PL_error(NULL, 0, "0..9",
1823 ERR_TYPE, ATOM_meta_argument_specifier, arg);;
1824 }
1825 }
1826
1827 if ( ReadingSource )
1828 { SourceFile sf = lookupSourceFile(source_file_name, TRUE);
1829 int rc = setMetapredicateSource(sf, proc, args PASS_LD);
1830 releaseSourceFile(sf);
1831 return rc;
1832 } else
1833 { setMetapredicateMask(proc->definition, args);
1834 return TRUE;
1835 }
1836 }
1837
1838
1839 static
1840 PRED_IMPL("meta_predicate", 1, meta_predicate, PL_FA_TRANSPARENT)
1841 { PRED_LD
1842 term_t tail = PL_copy_term_ref(A1);
1843 term_t head = PL_new_term_ref();
1844
1845 while ( PL_is_functor(tail, FUNCTOR_comma2) )
1846 { _PL_get_arg(1, tail, head);
1847 if ( !meta_declaration(head) )
1848 return FALSE;
1849 _PL_get_arg(2, tail, tail);
1850 }
1851
1852 if ( !meta_declaration(tail) )
1853 return FALSE;
1854
1855 return TRUE;
1856 }
1857
1858
1859 static int
unify_meta_argument(term_t head,Definition def,int i ARG_LD)1860 unify_meta_argument(term_t head, Definition def, int i ARG_LD)
1861 { term_t arg = PL_new_term_ref();
1862 int m = def->impl.any.args[i].meta;
1863
1864 _PL_get_arg(i+1, head, arg);
1865 if ( m < 10 )
1866 { return PL_unify_integer(arg, m);
1867 } else
1868 { atom_t a;
1869
1870 switch(m)
1871 { case MA_META: a = ATOM_colon; break;
1872 case MA_VAR: a = ATOM_minus; break;
1873 case MA_ANY: a = ATOM_question_mark; break;
1874 case MA_NONVAR: a = ATOM_plus; break;
1875 case MA_HAT: a = ATOM_hat; break;
1876 case MA_DCG: a = ATOM_gdiv; break;
1877 default: a = NULL_ATOM; assert(0);
1878 }
1879
1880 return PL_unify_atom(arg, a);
1881 }
1882 }
1883
1884
1885 static int
unify_meta_pattern(Procedure proc,term_t head)1886 unify_meta_pattern(Procedure proc, term_t head)
1887 { GET_LD
1888 Definition def = proc->definition;
1889
1890 if ( PL_unify_functor(head, def->functor->functor) )
1891 { int arity = def->functor->arity;
1892 int i;
1893
1894 for(i=0; i<arity; i++)
1895 { if ( !unify_meta_argument(head, def, i PASS_LD) )
1896 return FALSE;
1897 }
1898
1899 return TRUE;
1900 }
1901
1902 return FALSE;
1903 }
1904
1905
1906 int
PL_meta_predicate(predicate_t proc,const char * spec_s)1907 PL_meta_predicate(predicate_t proc, const char *spec_s)
1908 { Definition def = proc->definition;
1909 int arity = def->functor->arity;
1910 int i;
1911 int mask = 0;
1912 int transparent = FALSE;
1913 const unsigned char *s = (const unsigned char*)spec_s;
1914
1915 for(i=0; i<arity; i++, s++)
1916 { int spec_c = *s;
1917 int spec;
1918
1919 switch(spec_c)
1920 { case '+':
1921 spec = MA_NONVAR;
1922 break;
1923 case '-':
1924 spec = MA_VAR;
1925 break;
1926 case '?':
1927 spec = MA_ANY;
1928 break;
1929 case ':':
1930 spec = MA_META;
1931 break;
1932 case '^':
1933 spec = MA_HAT;
1934 break;
1935 case '/':
1936 if ( s[1] == '/' )
1937 { spec = MA_DCG;
1938 s++;
1939 break;
1940 } else
1941 { goto invalid;
1942 }
1943 default:
1944 if ( spec_c >= '0' && spec_c <= '9' )
1945 { spec = spec_c - '0';
1946 break;
1947 }
1948 invalid:
1949 fatalError("Invalid meta-argument for %s: %s\n", procedureName(proc), spec_s);
1950 return FALSE;
1951 }
1952
1953 def->impl.any.args[i].meta = spec;
1954 mask |= spec<<(i*4);
1955 if ( MA_NEEDS_TRANSPARENT(spec) )
1956 transparent = TRUE;
1957 }
1958
1959 if ( transparent )
1960 set(def, P_TRANSPARENT);
1961 else
1962 clear(def, P_TRANSPARENT);
1963 set(def, P_META);
1964
1965 return TRUE;
1966 }
1967
1968
1969 void
clear_meta_declaration(Definition def)1970 clear_meta_declaration(Definition def)
1971 { int i;
1972
1973 for(i=0; i<def->functor->arity; i++)
1974 def->impl.any.args[i].meta = MA_ANY;
1975
1976 clear(def, P_META|P_TRANSPARENT);
1977 }
1978
1979 #ifdef O_CLAUSEGC
1980 /*******************************
1981 * CLAUSE-GC *
1982 *******************************/
1983
1984 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1985 Retracted clauses are reclaimed using the clause garbage collector
1986 (CGC). Retract itself merely sets the erased generation of the clause
1987 and marks related clause indexes as `dirty'. CGC needs to run both to
1988 reclaim the memory and to remove ClauseRef objects that point to dead
1989 clauses and thus slow down the search for clauses. This logic is in
1990 considerClauseGC().
1991
1992 CGC builds on the following components and invariants:
1993
1994 - Dynamic predicates and static predicates with removed clauses are
1995 in the table GD->procedures.dirty.
1996 - CGC does:
1997 - Set the dirty generation of all dirty predicates to GEN_MAX
1998 - markPredicatesInEnvironments() finds all referenced predicates
1999 from frames and pushed explicitly by pushPredicateAccess()
2000 - Remove all ClauseRefs pointing at clauses removed before the
2001 oldest active generation from the clause list. Keep them using
2002 lingerClauseRef() as someone may be traversing the clause list.
2003 - Call gcClauseRefs(), which
2004 - Finds all predicates whose clause-list is being traversed as
2005 monitored using acquire_def()/release_ref().
2006 - Call freeClauseRef() for each clause-ref associated with a
2007 not-being-traversed predicate. Re-add the others to the
2008 lingering clause reference list.
2009 - If freeClauseRef() lowers the clause reference count to zero,
2010 destroy the clause.
2011 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2012
2013 static int
considerClauseGC(ARG1_LD)2014 considerClauseGC(ARG1_LD)
2015 { size_t pending = GD->clauses.erased_size - GD->clauses.erased_size_last;
2016 size_t codesize = GD->statistics.codes*sizeof(code);
2017 cgc_stats stats = {0};
2018
2019 if ( GD->clauses.cgc_space_factor > 0 &&
2020 pending > codesize/GD->clauses.cgc_space_factor &&
2021 GD->cleaning == CLN_NORMAL )
2022 { DEBUG(MSG_CGC_CONSIDER,
2023 Sdprintf("CGC? too much garbage: %lld bytes in %lld clauses\n",
2024 (int64_t)GD->clauses.erased_size,
2025 (int64_t)GD->clauses.erased));
2026 return TRUE;
2027 }
2028
2029 if ( LD->statistics.inferences > LD->clauses.cgc_inferences )
2030 { int rgc;
2031
2032 LD->clauses.cgc_inferences = LD->statistics.inferences + 500;
2033
2034 stats.dirty_pred_clauses = GD->clauses.dirty;
2035 if ( stats.dirty_pred_clauses == (size_t)-1 )
2036 return FALSE; /* already clicked in */
2037
2038 if ( !cgc_thread_stats(&stats PASS_LD) )
2039 return FALSE;
2040
2041 rgc = ( (double)stats.erased_skipped >
2042 (double)stats.local_size*GD->clauses.cgc_stack_factor +
2043 (double)stats.dirty_pred_clauses*GD->clauses.cgc_clause_factor );
2044 rgc = rgc && (GD->cleaning == CLN_NORMAL);
2045 DEBUG(MSG_CGC_CONSIDER,
2046 Sdprintf("GCG? [%s] %ld skipped; lsize=%ld; clauses=%ld\n",
2047 rgc ? "Y" : " ",
2048 (long)stats.erased_skipped,
2049 (long)stats.local_size,
2050 (long)stats.dirty_pred_clauses));
2051
2052 return rgc;
2053 }
2054
2055 return FALSE;
2056 }
2057
2058 /** '$cgc_params'(-OldSpace, -OldStack, -OldClause,
2059 * +NewSpace, +NewStack, +NewClause)
2060 *
2061 * Query and set the clause GC parameters.
2062 */
2063
2064 static
2065 PRED_IMPL("$cgc_params", 6, cgc_params, 0)
2066 { PRED_LD
2067
2068 return ( PL_unify_integer(A1, GD->clauses.cgc_space_factor) &&
2069 PL_unify_float(A2, GD->clauses.cgc_stack_factor) &&
2070 PL_unify_float(A3, GD->clauses.cgc_clause_factor) &&
2071 PL_get_integer_ex(A4, &GD->clauses.cgc_space_factor) &&
2072 PL_get_float_ex(A5, &GD->clauses.cgc_stack_factor) &&
2073 PL_get_float_ex(A6, &GD->clauses.cgc_clause_factor) );
2074 }
2075
2076
2077 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2078 Dirty Definition Info handling. This should respect
2079
2080 - If a predicate is registered dirty after the preparation step
2081 calling ddi_reset(), none of its clauses may be collected.
2082 - If a DDI has seen ddi_reset(), the DDI info is valid and can be
2083 used to safely reclaim clauses.
2084
2085 ddi_add_access_gen() adds access generations to the dirty def. We
2086 maintain two strategies:
2087
2088 - Upto PROC_DIRTY_GENS, we simply add the predicates to the array.
2089 - Above, the array is an array of intervals (generation pairs)
2090
2091
2092 A clause can be collected if it is invisible in all registered access
2093 generations.
2094
2095 TBD: Use multiple intervals.
2096 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2097
2098 #ifdef O_DEBUG
2099 static char *
ddi_generation_name(DirtyDefInfo ddi)2100 ddi_generation_name(DirtyDefInfo ddi)
2101 { char s[1024];
2102 char *o = s;
2103 *o++ = '{';
2104
2105 if ( false(ddi, DDI_INTERVALS) )
2106 { int i;
2107
2108 for(i=0; i<ddi->count; i++)
2109 { Ssprintf(o, "%s%lld", i==0?"":" ", ddi->access[i]);
2110 o += strlen(o);
2111 }
2112 } else
2113 { int i;
2114
2115 for(i=0; i<ddi->count; i++)
2116 { gen_t f = ddi->access[i++];
2117 gen_t t = ddi->access[i++];
2118
2119 Ssprintf(o, "%s%lld-%lld", i==2?"":" ", f, t);
2120 o += strlen(o);
2121 }
2122 }
2123 *o++ = '}';
2124 *o = EOS;
2125
2126 return buffer_string(s, BUF_DISCARDABLE);
2127 }
2128 #endif /*O_DEBUG*/
2129
2130 static DirtyDefInfo
ddi_new(Definition def)2131 ddi_new(Definition def)
2132 { DirtyDefInfo ddi = PL_malloc(sizeof(*ddi));
2133
2134 ddi->predicate = def;
2135 ddi->flags = 0;
2136 return ddi;
2137 }
2138
2139 static void
ddi_reset(DirtyDefInfo ddi)2140 ddi_reset(DirtyDefInfo ddi)
2141 { ddi->count = 0;
2142 ddi->flags = DDI_MARKING;
2143 }
2144
2145 int
ddi_contains_gen(DirtyDefInfo ddi,gen_t access)2146 ddi_contains_gen(DirtyDefInfo ddi, gen_t access)
2147 { if ( false(ddi, DDI_INTERVALS) )
2148 { int i;
2149
2150 for(i=0; i<ddi->count; i++)
2151 { if ( ddi->access[i] == access )
2152 return TRUE;
2153 }
2154 } else
2155 { int i;
2156
2157 for(i=0; i<ddi->count; )
2158 { if ( access >= ddi->access[i++] &&
2159 access <= ddi->access[i++] )
2160 return TRUE;
2161 }
2162 }
2163
2164 return FALSE;
2165 }
2166
2167
2168 static void
ddi_to_intervals(DirtyDefInfo ddi,gen_t access)2169 ddi_to_intervals(DirtyDefInfo ddi, gen_t access)
2170 { gen_t min = access;
2171 gen_t max = access;
2172 int i;
2173
2174 DEBUG(MSG_CGC_GENERATION,
2175 Sdprintf("DDI for %s to interval: %s\n",
2176 predicateName(ddi->predicate),
2177 ddi_generation_name(ddi)));
2178
2179 for(i=0; i<ddi->count; i++)
2180 { gen_t a = ddi->access[i];
2181
2182 if ( a < min ) min = a;
2183 if ( a > max ) max = a;
2184 }
2185
2186 ddi->access[0] = min;
2187 ddi->access[1] = max;
2188 ddi->count = 2;
2189 set(ddi, DDI_INTERVALS);
2190 }
2191
2192
2193 static void
ddi_interval_add_access_gen(DirtyDefInfo ddi,gen_t access)2194 ddi_interval_add_access_gen(DirtyDefInfo ddi, gen_t access)
2195 { if ( access < ddi->access[0] ) ddi->access[0] = access;
2196 if ( access > ddi->access[1] ) ddi->access[1] = access;
2197 }
2198
2199
2200 void
ddi_add_access_gen(DirtyDefInfo ddi,gen_t access)2201 ddi_add_access_gen(DirtyDefInfo ddi, gen_t access)
2202 { if ( true(ddi, DDI_MARKING) )
2203 { PL_LOCK(L_CGCGEN);
2204 if ( !ddi_contains_gen(ddi, access) )
2205 { if ( false(ddi, DDI_INTERVALS) )
2206 { if ( ddi->count < PROC_DIRTY_GENS )
2207 ddi->access[ddi->count++] = access;
2208 else
2209 ddi_to_intervals(ddi, access);
2210 } else
2211 { ddi_interval_add_access_gen(ddi, access);
2212 }
2213 }
2214 PL_UNLOCK(L_CGCGEN);
2215 }
2216 }
2217
2218 int
ddi_is_garbage(DirtyDefInfo ddi,gen_t start,Clause cl)2219 ddi_is_garbage(DirtyDefInfo ddi, gen_t start, Clause cl)
2220 { assert(true(ddi, DDI_MARKING));
2221
2222 if ( cl->generation.erased >= start )
2223 return FALSE;
2224
2225 if ( false(ddi, DDI_INTERVALS) )
2226 { int i;
2227
2228 for(i=0; i<ddi->count; i++)
2229 { if ( GLOBALLY_VISIBLE_CLAUSE(cl, ddi->access[i]) )
2230 return FALSE;
2231 }
2232 } else
2233 { int i;
2234
2235 assert(ddi->count == 2);
2236
2237 for(i=0; i<ddi->count; )
2238 { gen_t f = ddi->access[i++];
2239 gen_t t = ddi->access[i++];
2240
2241 if ( !(cl->generation.erased < f ||
2242 cl->generation.created > t) )
2243 return FALSE;
2244 }
2245 }
2246
2247 return TRUE;
2248 }
2249
2250 static gen_t
ddi_oldest_generation(DirtyDefInfo ddi)2251 ddi_oldest_generation(DirtyDefInfo ddi)
2252 { gen_t oldest = GEN_MAX;
2253
2254 if ( false(ddi, DDI_INTERVALS) )
2255 { int i;
2256
2257 for(i=0; i<ddi->count; i++)
2258 { if ( ddi->access[i] < oldest )
2259 oldest = ddi->access[i];
2260 }
2261 } else
2262 { int i;
2263
2264 for(i=0; i<ddi->count; i += 2)
2265 { gen_t f = ddi->access[i];
2266
2267 if ( f < oldest )
2268 oldest = f;
2269 }
2270 }
2271
2272 return oldest;
2273 }
2274
2275 #ifdef O_DEBUG
2276 static Table protectedCRefs = NULL;
2277
2278 static void
protectCRef(ClauseRef cref)2279 protectCRef(ClauseRef cref)
2280 { GET_LD
2281 void *k;
2282
2283 if ( !protectedCRefs )
2284 protectedCRefs = newHTable(64);
2285
2286 if ( (k=lookupHTable(protectedCRefs, cref)) )
2287 { k = (void*)((intptr_t)k+1);
2288 updateHTable(protectedCRefs, cref, k);
2289 // Sdprintf("Protect %p --> %zd\n", cref, (intptr_t)k);
2290 } else
2291 { addNewHTable(protectedCRefs, cref, (void*)1);
2292 // Sdprintf("Protect %p\n", cref);
2293 }
2294 }
2295
2296 static void
unprotectCRef(ClauseRef cref)2297 unprotectCRef(ClauseRef cref)
2298 { GET_LD
2299 void *k;
2300
2301 if ( (k=lookupHTable(protectedCRefs, cref)) )
2302 { k = (void*)((intptr_t)k-1);
2303 if ( k )
2304 { updateHTable(protectedCRefs, cref, k);
2305 // Sdprintf("UnProtect %p --> %zd\n", cref, (intptr_t)k);
2306 } else
2307 { deleteHTable(protectedCRefs, cref);
2308 // Sdprintf("UnProtect %p\n", cref);
2309 }
2310 } else
2311 { assert(0);
2312 }
2313 }
2314
2315 static int
isProtectedCRef(ClauseRef cref)2316 isProtectedCRef(ClauseRef cref)
2317 { GET_LD
2318
2319 return ( protectedCRefs &&
2320 lookupHTable(protectedCRefs, cref) );
2321 }
2322 #endif /*O_DEBUG*/
2323
2324 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2325 (*) We set the initial oldest_generation to "very old" (0). This ensures
2326 that if a predicate is registered dirty before clause-gc starts, the
2327 oldest generation is 0 and thus no clause reference will be collected.
2328 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2329
2330 static void
registerDirtyDefinition(Definition def ARG_LD)2331 registerDirtyDefinition(Definition def ARG_LD)
2332 { if ( false(def, P_DIRTYREG) )
2333 { DirtyDefInfo ddi = ddi_new(def);
2334
2335 if ( addHTable(GD->procedures.dirty, def, ddi) == ddi )
2336 { set(def, P_DIRTYREG);
2337 ATOMIC_ADD(&GD->clauses.dirty, def->impl.clauses.number_of_clauses);
2338 } else
2339 { PL_free(ddi); /* someone else did this */
2340 }
2341 }
2342 if ( !isSignalledGCThread(SIG_CLAUSE_GC PASS_LD) && /* already asked for */
2343 !GD->clauses.cgc_active && /* currently running */
2344 considerClauseGC(PASS_LD1) )
2345 { if ( GD->clauses.db_erased_refs > GD->clauses.erased / 10 )
2346 { DEBUG(MSG_CGC_CONSIDER,
2347 Sdprintf("CGC: %ld of %ld erased clauses has a clause ref; "
2348 "asking for AGC\n",
2349 (long)GD->clauses.db_erased_refs,
2350 (long)GD->clauses.erased));
2351 signalGCThread(SIG_ATOM_GC);
2352 }
2353 signalGCThread(SIG_CLAUSE_GC);
2354 }
2355 }
2356
2357 static void
unregisterDirtyDefinition(Definition def)2358 unregisterDirtyDefinition(Definition def)
2359 { DirtyDefInfo ddi;
2360
2361 if ( (ddi=deleteHTable(GD->procedures.dirty, def)) )
2362 { PL_free(ddi);
2363 clear(def, P_DIRTYREG);
2364 ATOMIC_SUB(&GD->clauses.dirty, def->impl.clauses.number_of_clauses);
2365 }
2366 }
2367
2368
2369 static void
maybeUnregisterDirtyDefinition(Definition def)2370 maybeUnregisterDirtyDefinition(Definition def)
2371 { if ( true(def, P_DIRTYREG) &&
2372 def->impl.clauses.erased_clauses == 0 )
2373 { unregisterDirtyDefinition(def);
2374 }
2375
2376 if ( true(def, P_ERASED) )
2377 { DEBUG(MSG_PROC_COUNT, Sdprintf("Delayed unalloc %s\n", predicateName(def)));
2378 assert(def->module == NULL);
2379 if ( def->impl.clauses.first_clause == NULL )
2380 { if ( def->lingering )
2381 { static int done = FALSE;
2382 if ( !done )
2383 { Sdprintf("maybeUnregisterDirtyDefinition(%s): lingering data\n",
2384 predicateName(def));
2385 done = TRUE;
2386 }
2387 }
2388 unregisterDirtyDefinition(def);
2389 deleteIndexes(&def->impl.clauses, TRUE);
2390 freeHeap(def->impl.any.args, sizeof(arg_info)*def->functor->arity);
2391 if ( def->tabling )
2392 freeHeap(def->tabling, sizeof(*def->tabling));
2393 freeHeap(def, sizeof(*def));
2394 }
2395 }
2396 }
2397
2398
2399 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2400 (*) We set the initial generation to GEN_MAX to know which predicates
2401 have been marked. We can only reclaim clauses that were erased before
2402 the start generation of the clause garbage collector.
2403 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2404
2405 foreign_t
pl_garbage_collect_clauses(void)2406 pl_garbage_collect_clauses(void)
2407 { GET_LD
2408 int rc = TRUE;
2409
2410 if ( GD->procedures.dirty->size > 0 &&
2411 COMPARE_AND_SWAP_INT(&GD->clauses.cgc_active, FALSE, TRUE) )
2412 { size_t removed = 0;
2413 size_t erased_pending = GD->clauses.erased_size;
2414 double gct, t0 = ThreadCPUTime(LD, CPU_USER);
2415 gen_t start_gen = global_generation();
2416 int verbose = truePrologFlag(PLFLAG_TRACE_GC) && !LD->in_print_message;
2417
2418 if ( verbose )
2419 { if ( (rc=printMessage(ATOM_informational,
2420 PL_FUNCTOR_CHARS, "cgc", 1,
2421 PL_CHARS, "start")) == FALSE )
2422 goto out;
2423 }
2424
2425 DEBUG(MSG_CGC, Sdprintf("CGC @ %lld ... ", start_gen));
2426 DEBUG(MSG_CGC_STACK,
2427 { Sdprintf("CGC @ %lld ... ", start_gen);
2428 PL_backtrace(5,0);
2429 });
2430
2431 /* sanity-check */
2432 for_table(GD->procedures.dirty, n, v,
2433 { DirtyDefInfo ddi = v;
2434
2435 DEBUG(CHK_SECURE,
2436 { Definition def = n;
2437 LOCKDEF(def);
2438 checkDefinition(def);
2439 UNLOCKDEF(def);
2440 });
2441 ddi_reset(ddi); /* see (*) */
2442 });
2443
2444 markPredicatesInEnvironments(LD);
2445 #ifdef O_PLMT
2446 forThreadLocalDataUnsuspended(markPredicatesInEnvironments, 0);
2447 #endif
2448
2449 DEBUG(MSG_CGC, Sdprintf("(marking done)\n"));
2450
2451 for_table(GD->procedures.dirty, n, v,
2452 { Definition def = n;
2453 DirtyDefInfo ddi = v;
2454
2455 if ( false(def, P_FOREIGN) &&
2456 def->impl.clauses.erased_clauses > 0 )
2457 { size_t del = cleanDefinition(def, ddi, start_gen, &rc);
2458
2459 removed += del;
2460 DEBUG(MSG_CGC_PRED,
2461 Sdprintf("cleanDefinition(%s, %s): "
2462 "%zd clauses (left %d)\n",
2463 predicateName(def),
2464 ddi_generation_name(ddi),
2465 del,
2466 (int)def->impl.clauses.erased_clauses));
2467 }
2468
2469 maybeUnregisterDirtyDefinition(def);
2470 });
2471
2472 gcClauseRefs();
2473 GD->clauses.cgc_count++;
2474 GD->clauses.cgc_reclaimed += removed;
2475 GD->clauses.cgc_time += (gct=ThreadCPUTime(LD, CPU_USER) - t0);
2476 GD->clauses.erased_size_last = GD->clauses.erased_size;
2477
2478 DEBUG(MSG_CGC, Sdprintf("CGC: removed %ld clauses "
2479 "(%ld bytes reclaimed, %ld pending) in %2f sec.\n",
2480 (long)removed,
2481 (long)erased_pending - GD->clauses.erased_size,
2482 (long)GD->clauses.erased_size,
2483 gct));
2484
2485 if ( verbose )
2486 rc = printMessage(
2487 ATOM_informational,
2488 PL_FUNCTOR_CHARS, "cgc", 1,
2489 PL_FUNCTOR_CHARS, "done", 4,
2490 PL_INT64, (int64_t)removed,
2491 PL_INT64, (int64_t)(erased_pending - GD->clauses.erased_size),
2492 PL_INT64, (int64_t)GD->clauses.erased_size,
2493 PL_DOUBLE, gct);
2494
2495 out:
2496 GD->clauses.cgc_active = FALSE;
2497 }
2498
2499 return rc;
2500 }
2501
2502 #endif /*O_CLAUSEGC*/
2503
2504 #ifdef O_DEBUG
2505 /*******************************
2506 * CHECKING *
2507 *******************************/
2508
2509 word
pl_check_definition(term_t spec)2510 pl_check_definition(term_t spec)
2511 { GET_LD
2512 Procedure proc;
2513 Definition def;
2514 int nclauses = 0;
2515 int nerased = 0;
2516 int nindexable = 0;
2517 ClauseRef cref;
2518
2519 if ( !get_procedure(spec, &proc, 0, GP_FIND) )
2520 return Sdprintf("$check_definition/1: can't find definition");
2521 def = getProcDefinition(proc);
2522
2523 if ( true(def, P_FOREIGN) )
2524 succeed;
2525
2526 acquire_def(def);
2527 for(cref = def->impl.clauses.first_clause; cref; cref = cref->next)
2528 { Clause clause = cref->value.clause;
2529
2530 if ( cref->d.key == 0 )
2531 nindexable++;
2532
2533 if ( false(clause, CL_ERASED) )
2534 nclauses++;
2535 else
2536 nerased++;
2537 }
2538 release_def(def);
2539
2540 if ( nerased != def->impl.clauses.erased_clauses )
2541 Sdprintf("%s has %d erased clauses, claims %d\n",
2542 predicateName(def), nerased, def->impl.clauses.erased_clauses);
2543
2544 checkClauseIndexSizes(def, nindexable);
2545
2546 if ( def->impl.clauses.number_of_clauses != nclauses )
2547 Sdprintf("%s has inconsistent number_of_clauses (%d, should be %d)",
2548 predicateName(def), def->impl.clauses.number_of_clauses, nclauses);
2549
2550 succeed;
2551 }
2552 #endif /*O_DEBUG*/
2553
2554 /********************************
2555 * UNDEFINED PROCEDURES *
2556 *********************************/
2557
2558 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2559 A dynamic call to `f' in `m' has to be made (via call/1 or from C). This
2560 procedure returns the procedure to be run. If no such procedure exists
2561 an undefined procedure is created and returned. In this case interpret()
2562 will later call trapUndefined() to generate an error message (or link
2563 the procedure from the library via autoload).
2564 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2565
2566 Procedure
resolveProcedure__LD(functor_t f,Module module ARG_LD)2567 resolveProcedure__LD(functor_t f, Module module ARG_LD)
2568 { Procedure proc;
2569
2570 if ( (proc = visibleProcedure(f, module PASS_LD)) )
2571 return proc;
2572
2573 return lookupProcedure(f, module);
2574 }
2575
2576
2577 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2578 autoImport() tries to autoimport f into module `m' and returns the
2579 definition if this is possible.
2580
2581 PROBLEM: I'm not entirely sure it is save to deallocated the old
2582 definition structure in all cases. It is not member of any heap
2583 structure, thus sofar everything is alright. After a dynamic link
2584 interpret() picks up the new definition pointer, thus this should be ok
2585 as well. Any other C-code that does nasty things (non-deterministic
2586 code perhaps, calls indirect via C? (I do recall once conciously have
2587 decided its not save, but can't recall why ...)
2588
2589 Its definitely not safe in MT context as others may be racing for the
2590 definition. How do we get this working without locking the
2591 proc->definition fetch?
2592 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2593
2594 Definition
autoImport(functor_t f,Module m)2595 autoImport(functor_t f, Module m)
2596 { GET_LD
2597 Procedure proc;
2598 Definition def, odef;
2599 ListCell c;
2600 /* Defined: no problem */
2601 if ( (proc = isCurrentProcedure(f, m)) )
2602 { if ( isDefinedProcedure(proc) )
2603 return proc->definition;
2604 if ( true(proc->definition, P_AUTOLOAD) )
2605 return NULL;
2606 }
2607
2608 for(c=m->supers; c; c=c->next)
2609 { Module s = c->value;
2610
2611 if ( (def = autoImport(f, s)) )
2612 goto found;
2613 }
2614 return NULL;
2615
2616 found:
2617 if ( proc == NULL ) /* Create header if not there */
2618 { if ( !(proc = lookupProcedure(f, m)) )
2619 return NULL;
2620 }
2621 /* Now, take the lock also used */
2622 /* by lookupProcedure(). Note */
2623 /* that another thread may have */
2624 /* done the job for us. */
2625 LOCKMODULE(m);
2626 if ( (odef=proc->definition) != def ) /* Nope, we must link the def */
2627 { shareDefinition(def);
2628 proc->definition = def;
2629
2630 if ( unshareDefinition(odef) == 0 )
2631 {
2632 #ifdef O_PLMT
2633 PL_LOCK(L_THREAD);
2634 if ( (GD->statistics.threads_created -
2635 GD->statistics.threads_finished) == 1 )
2636 { DEBUG(MSG_PROC_COUNT, Sdprintf("Unalloc %s\n", predicateName(odef)));
2637 unregisterDirtyDefinition(odef);
2638 freeHeap(odef, sizeof(*odef));
2639 GD->statistics.predicates--;
2640 } else
2641 { DEBUG(MSG_PROC, Sdprintf("autoImport(%s,%s): Linger %s (%p)\n",
2642 functorName(f), PL_atom_chars(m->name),
2643 predicateName(odef), odef));
2644 lingerDefinition(odef);
2645 }
2646 PL_UNLOCK(L_THREAD);
2647 #else
2648 freeHeap(odef, sizeof(struct definition));
2649 #endif
2650 }
2651 }
2652 UNLOCKMODULE(m);
2653
2654 return def;
2655 }
2656
2657
2658 static int
test_autoload_loop(Definition def ARG_LD)2659 test_autoload_loop(Definition def ARG_LD)
2660 { DefinitionChain ch;
2661
2662 for(ch=LD->autoload.nesting; ch; ch=ch->next)
2663 { if ( ch->definition == def )
2664 { LD->autoload.loop = def;
2665
2666 Sdprintf("ERROR: autoload loop:\n");
2667 Sdprintf("ERROR: %s\n", predicateName(def));
2668
2669 for(ch=LD->autoload.nesting; ch; ch=ch->next)
2670 { Sdprintf("ERROR: %s\n", predicateName(ch->definition));
2671 if ( ch->definition == def )
2672 break;
2673 }
2674
2675 return FALSE;
2676 }
2677 }
2678
2679 return TRUE;
2680 }
2681
2682 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2683 Call the autoloader for the given definition.
2684 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2685
2686 static atom_t
autoLoader(Definition def)2687 autoLoader(Definition def)
2688 { GET_LD
2689 fid_t cid;
2690 term_t argv;
2691 qid_t qid;
2692 atom_t answer = ATOM_nil;
2693 struct definition_chain cell;
2694
2695 if ( !GD->procedures.undefinterc4 )
2696 GD->procedures.undefinterc4 = PL_pred(FUNCTOR_undefinterc4,
2697 MODULE_system);
2698
2699 if ( !(cid = PL_open_foreign_frame()) ||
2700 !(argv = PL_new_term_refs(4)) )
2701 return answer;
2702
2703 PL_put_atom( argv+0, def->module->name);
2704 PL_put_atom( argv+1, def->functor->name);
2705 PL_put_integer( argv+2, def->functor->arity);
2706
2707 push_input_context(ATOM_autoload);
2708 cell.definition = def;
2709 cell.next = LD->autoload.nesting;
2710 LD->autoload.nesting = &cell;
2711 if ( (qid = PL_open_query(MODULE_system, PL_Q_NODEBUG|PL_Q_PASS_EXCEPTION,
2712 GD->procedures.undefinterc4, argv)) )
2713 { if ( PL_next_solution(qid) )
2714 PL_get_atom(argv+3, &answer);
2715 PL_close_query(qid);
2716 } else if ( PL_exception(0) )
2717 { PL_clear_exception();
2718 }
2719 LD->autoload.nesting = LD->autoload.nesting->next;
2720 pop_input_context();
2721 PL_discard_foreign_frame(cid);
2722
2723 return answer;
2724 }
2725
2726
2727 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2728 According to Paulo Moura, predicates defined either dynamic, multifile or
2729 discontiguous should not cause an undefined predicate warning.
2730 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2731
2732 Definition
trapUndefined(Definition def ARG_LD)2733 trapUndefined(Definition def ARG_LD)
2734 { int retry_times = 0;
2735 Definition newdef;
2736 Module module = def->module;
2737 FunctorDef functor = def->functor;
2738
2739 retry:
2740 /* Auto import */
2741 if ( (newdef = autoImport(functor->functor, module)) )
2742 return newdef;
2743 /* Pred/Module does not want to trap */
2744 if ( true(def, PROC_DEFINED) ||
2745 getUnknownModule(module) == UNKNOWN_FAIL )
2746 return def;
2747
2748 DEBUG(5, Sdprintf("trapUndefined(%s)\n", predicateName(def)));
2749
2750 /* Trap via exception/3 */
2751 if ( truePrologFlag(PLFLAG_AUTOLOAD) && !GD->bootsession )
2752 { if ( !test_autoload_loop(def PASS_LD) )
2753 { return def;
2754 } else
2755 { atom_t answer = autoLoader(def);
2756
2757 def = lookupDefinition(functor->functor, module);
2758
2759 if ( answer == ATOM_fail )
2760 { return def;
2761 } else if ( answer == ATOM_error )
2762 { goto error;
2763 } else if ( answer == ATOM_retry )
2764 { if ( retry_times++ )
2765 { warning("[Thread %d]: exception handler failed to define %s\n",
2766 PL_thread_self(),
2767 predicateName(def));
2768 return def;
2769 }
2770 goto retry;
2771 }
2772 }
2773 }
2774 /* No one wants to intercept */
2775 error:
2776 if ( GD->bootsession )
2777 { sysError("Undefined predicate: %s", predicateName(def));
2778 } else
2779 { createUndefSupervisor(def);
2780 }
2781
2782 return def;
2783 }
2784
2785
2786 /*******************************
2787 * REQUIRE SUPPORT *
2788 *******************************/
2789
2790 word
pl_require(term_t pred)2791 pl_require(term_t pred)
2792 { Procedure proc;
2793
2794 if ( !get_procedure(pred, &proc, 0, GP_RESOLVE) )
2795 return get_procedure(pred, &proc, 0, GP_DEFINE);
2796
2797 succeed;
2798 }
2799
2800
2801 /********************************
2802 * RETRACT *
2803 *********************************/
2804
2805 typedef struct
2806 { Definition def;
2807 struct clause_choice chp;
2808 int allocated;
2809 } retract_context;
2810
2811 static retract_context *
alloc_retract_context(retract_context * ctx0)2812 alloc_retract_context(retract_context *ctx0)
2813 { retract_context *ctx = allocForeignState(sizeof(*ctx));
2814
2815 *ctx = *ctx0;
2816 ctx->allocated = TRUE;
2817
2818 return ctx;
2819 }
2820
2821 static void
free_retract_context(retract_context * ctx ARG_LD)2822 free_retract_context(retract_context *ctx ARG_LD)
2823 { popPredicateAccess(ctx->def);
2824 leaveDefinition(ctx->def);
2825
2826 if ( ctx->allocated )
2827 freeForeignState(ctx, sizeof(*ctx));
2828 }
2829
2830 static
2831 PRED_IMPL("retract", 1, retract,
2832 PL_FA_TRANSPARENT|PL_FA_NONDETERMINISTIC|PL_FA_ISO)
2833 { PRED_LD
2834 term_t term = A1;
2835 retract_context ctxbuf;
2836 retract_context *ctx;
2837 ClauseRef cref;
2838
2839 if ( CTX_CNTRL == FRG_CUTTED )
2840 { ctx = CTX_PTR;
2841
2842 unprotectCRef(ctx->chp.cref);
2843 free_retract_context(ctx PASS_LD);
2844
2845 return TRUE;
2846 } else
2847 { Module m = NULL;
2848 term_t cl = PL_new_term_ref();
2849 term_t head = PL_new_term_ref();
2850 term_t body = PL_new_term_ref();
2851 Word argv;
2852 atom_t b;
2853 fid_t fid;
2854 definition_ref *dref = NULL;
2855
2856 if ( !PL_strip_module_ex(term, &m, cl) ||
2857 !get_head_and_body_clause(cl, head, body, NULL PASS_LD) )
2858 return FALSE;
2859 if ( PL_get_atom(body, &b) && b == ATOM_true )
2860 PL_put_term(cl, head);
2861
2862 argv = valTermRef(head);
2863 deRef(argv);
2864 if ( isTerm(*argv) ) /* retract(foobar(a1, ...)) */
2865 argv = argTermP(*argv, 0);
2866 else
2867 argv = NULL; /* retract(foobar) */
2868
2869 if ( CTX_CNTRL == FRG_FIRST_CALL )
2870 { functor_t fd;
2871 Procedure proc;
2872 Definition def;
2873
2874 if ( !PL_get_functor(head, &fd) )
2875 return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_callable, head);
2876 if ( !(proc = isCurrentProcedure(fd, m)) )
2877 { checkModifySystemProc(fd);
2878 fail;
2879 }
2880
2881 def = getProcDefinition(proc);
2882
2883 if ( true(def, P_FOREIGN) )
2884 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
2885 if ( false(def, P_DYNAMIC) )
2886 { if ( isDefinedProcedure(proc) )
2887 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
2888 setDynamicDefinition(def, TRUE); /* implicit */
2889 fail; /* no clauses */
2890 }
2891
2892 enterDefinition(def); /* reference the predicate */
2893 dref = pushPredicateAccessObj(def PASS_LD);
2894 setGenerationFrameVal(environment_frame, dref->generation);
2895 cref = firstClause(argv, environment_frame, def, &ctxbuf.chp PASS_LD);
2896 DEBUG(MSG_CGC_RETRACT,
2897 Sdprintf("Started retract from %s at gen = %lld\n",
2898 predicateName(def), generationFrame(environment_frame)));
2899 if ( !cref )
2900 { popPredicateAccess(def);
2901 leaveDefinition(def);
2902 fail;
2903 }
2904
2905 ctx = &ctxbuf;
2906 ctx->def = def;
2907 ctx->allocated = 0;
2908 } else
2909 { ctx = CTX_PTR;
2910 DEBUG(MSG_CGC_RETRACT,
2911 Sdprintf("Retry retract from %s at gen = %lld\n",
2912 predicateName(ctx->def),
2913 generationFrame(environment_frame)));
2914 unprotectCRef(ctx->chp.cref);
2915 cref = nextClause(&ctx->chp, argv, environment_frame, ctx->def);
2916 }
2917
2918 if ( !(fid = PL_open_foreign_frame()) )
2919 { free_retract_context(ctx PASS_LD);
2920 return FALSE;
2921 }
2922
2923 while( cref )
2924 { if ( decompile(cref->value.clause, cl, 0) )
2925 { if ( retractClauseDefinition(ctx->def, cref->value.clause) ||
2926 CTX_CNTRL != FRG_FIRST_CALL )
2927 { if ( !ctx->chp.cref ) /* deterministic last one */
2928 { free_retract_context(ctx PASS_LD);
2929 PL_close_foreign_frame(fid);
2930 return TRUE;
2931 }
2932
2933 if ( ctx == &ctxbuf ) /* non-determinisic; save state */
2934 ctx = alloc_retract_context(ctx);
2935
2936 DEBUG(0,
2937 assert(GLOBALLY_VISIBLE_CLAUSE(
2938 ctx->chp.cref->value.clause,
2939 generationFrame(environment_frame))));
2940 protectCRef(ctx->chp.cref);
2941
2942 PL_close_foreign_frame(fid);
2943 ForeignRedoPtr(ctx);
2944 } else
2945 { setGenerationFrame(environment_frame);
2946 assert(dref);
2947 dref->generation = generationFrame(environment_frame);
2948 DEBUG(MSG_CGC_RETRACT,
2949 Sdprintf("Retract: first clause deleted; set gen to %lld\n",
2950 generationFrame(environment_frame)));
2951 }
2952 }
2953
2954 if ( PL_exception(0) )
2955 break;
2956
2957 PL_rewind_foreign_frame(fid);
2958 cref = nextClause(&ctx->chp, argv, environment_frame, ctx->def);
2959 }
2960
2961 PL_close_foreign_frame(fid);
2962 free_retract_context(ctx PASS_LD);
2963 return FALSE;
2964 }
2965 }
2966
2967
2968 static int
allVars(int argc,Word argv ARG_LD)2969 allVars(int argc, Word argv ARG_LD)
2970 { int i, r, allvars = TRUE;
2971 Word *reset = alloca(argc*sizeof(Word));
2972
2973 for(i=0; i<argc; i++)
2974 { Word p2;
2975
2976 deRef2(argv+i, p2);
2977 if ( isVar(*p2) )
2978 { reset[i] = p2;
2979 *p2 = ATOM_nil;
2980 } else
2981 { allvars = FALSE;
2982 break;
2983 }
2984 }
2985
2986 for(r=0; r<i; r++)
2987 setVar(*reset[r]);
2988
2989 return allvars;
2990 }
2991
2992
2993 static
2994 PRED_IMPL("retractall", 1, retractall, PL_FA_NONDETERMINISTIC|PL_FA_ISO)
2995 { GET_LD
2996 term_t head = A1;
2997 term_t thehead = PL_new_term_ref();
2998 Procedure proc;
2999 Definition def;
3000 ClauseRef cref;
3001 Word argv;
3002 int allvars = TRUE;
3003 fid_t fid;
3004 int rc = TRUE;
3005
3006 if ( !get_procedure(head, &proc, thehead, GP_CREATE) )
3007 fail;
3008
3009 def = getProcDefinition(proc);
3010 if ( true(def, P_FOREIGN) )
3011 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
3012 if ( false(def, P_DYNAMIC) )
3013 { if ( isDefinedProcedure(proc) )
3014 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
3015 if ( !setDynamicDefinition(def, TRUE) )
3016 fail;
3017 succeed; /* nothing to retract */
3018 }
3019
3020 if ( !retractall_event(def, thehead, FUNCTOR_start1 PASS_LD) )
3021 return FALSE;
3022
3023 argv = valTermRef(thehead);
3024 deRef(argv);
3025 if ( isTerm(*argv) )
3026 { int arity = arityTerm(*argv);
3027 argv = argTermP(*argv, 0);
3028
3029 allvars = allVars(arity, argv PASS_LD);
3030 } else
3031 { allvars = TRUE;
3032 argv = NULL;
3033 }
3034
3035 enterDefinition(def);
3036 setGenerationFrameVal(environment_frame, pushPredicateAccess(def));
3037 fid = PL_open_foreign_frame();
3038
3039 DEBUG(CHK_SECURE,
3040 LOCKDEF(def);
3041 checkDefinition(def);
3042 UNLOCKDEF(def));
3043 if ( allvars )
3044 { gen_t gen = generationFrame(environment_frame);
3045
3046 acquire_def(def);
3047 for(cref = def->impl.clauses.first_clause; cref; cref = cref->next)
3048 { if ( visibleClauseCNT(cref->value.clause, gen) )
3049 { if ( !(rc=retractClauseDefinition(def, cref->value.clause)) )
3050 { if ( PL_exception(0) )
3051 break;
3052 }
3053 }
3054 }
3055 release_def(def);
3056 rc = TRUE;
3057 } else
3058 { struct clause_choice chp;
3059
3060 if ( !(cref = firstClause(argv, environment_frame, def, &chp PASS_LD)) )
3061 { popPredicateAccess(def);
3062 leaveDefinition(def);
3063 return TRUE;
3064 }
3065
3066 while( cref )
3067 { if ( decompileHead(cref->value.clause, thehead) )
3068 { if ( !(rc=retractClauseDefinition(def, cref->value.clause)) )
3069 { if ( PL_exception(0) )
3070 break;
3071 }
3072 } else
3073 { if ( PL_exception(0) )
3074 { rc = FALSE;
3075 break;
3076 }
3077 }
3078
3079 PL_rewind_foreign_frame(fid);
3080
3081 if ( !chp.cref )
3082 { popPredicateAccess(def);
3083 leaveDefinition(def);
3084 return TRUE;
3085 }
3086
3087 if ( argv ) /* may be shifted */
3088 { argv = valTermRef(thehead);
3089 argv = argTermP(*argv, 0);
3090 }
3091
3092 cref = nextClause(&chp, argv, environment_frame, def);
3093 rc = TRUE;
3094 }
3095 }
3096 popPredicateAccess(def);
3097 leaveDefinition(def);
3098 DEBUG(CHK_SECURE,
3099 LOCKDEF(def);
3100 checkDefinition(def);
3101 UNLOCKDEF(def));
3102
3103 if ( rc )
3104 rc = retractall_event(def, thehead, FUNCTOR_end1 PASS_LD);
3105
3106 return rc;
3107 }
3108
3109 /********************************
3110 * PROLOG PREDICATES *
3111 *********************************/
3112
3113 static word
do_abolish(Module m,term_t atom,term_t arity)3114 do_abolish(Module m, term_t atom, term_t arity)
3115 { GET_LD
3116 functor_t f;
3117 Procedure proc;
3118 atom_t name;
3119 int a = 0;
3120
3121 if ( !PL_get_atom_ex(atom, &name) ||
3122 !get_arity(arity, 0, MAXARITY, &a) )
3123 fail;
3124
3125 if ( !(f = isCurrentFunctor(name, a)) )
3126 succeed;
3127 if ( !checkModifySystemProc(f) )
3128 fail;
3129 if ( !(proc = isCurrentProcedure(f, m)) )
3130 succeed;
3131
3132 if ( truePrologFlag(PLFLAG_ISO) && false(proc->definition, P_DYNAMIC) )
3133 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, proc);
3134
3135 return abolishProcedure(proc, m);
3136 }
3137
3138
3139 word
pl_abolish(term_t name,term_t arity)3140 pl_abolish(term_t name, term_t arity) /* Name, Arity */
3141 { GET_LD
3142 Module m = NULL;
3143
3144 return ( PL_strip_module(name, &m, name) &&
3145 do_abolish(m, name, arity)
3146 );
3147 }
3148
3149
3150 word
pl_abolish1(term_t spec)3151 pl_abolish1(term_t spec) /* Name/Arity */
3152 { GET_LD
3153 term_t name = PL_new_term_ref();
3154 term_t arity = PL_new_term_ref();
3155 Module m = NULL;
3156
3157 if ( !PL_strip_module(spec, &m, spec) )
3158 return FALSE;
3159
3160 if ( !PL_is_functor(spec, FUNCTOR_divide2) )
3161 return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_predicate_indicator, spec);
3162
3163 _PL_get_arg(1, spec, name);
3164 _PL_get_arg(2, spec, arity);
3165
3166 return do_abolish(m, name, arity);
3167 }
3168
3169
3170 typedef struct patt_mask
3171 { atom_t key;
3172 unsigned int mask;
3173 } patt_mask;
3174
3175 static const patt_mask patt_masks[] =
3176 { { ATOM_dynamic, P_DYNAMIC },
3177 { ATOM_multifile, P_MULTIFILE },
3178 { ATOM_locked, P_LOCKED },
3179 { ATOM_system, P_LOCKED }, /* compatibility */
3180 { ATOM_spy, SPY_ME },
3181 { ATOM_tabled, P_TABLED },
3182 { ATOM_incremental, P_INCREMENTAL },
3183 { ATOM_tshared, P_TSHARED },
3184 { ATOM_trace, TRACE_ME },
3185 { ATOM_hide_childs, HIDE_CHILDS },
3186 { ATOM_transparent, P_TRANSPARENT },
3187 { ATOM_discontiguous, P_DISCONTIGUOUS },
3188 { ATOM_volatile, P_VOLATILE },
3189 { ATOM_thread_local, P_THREAD_LOCAL },
3190 { ATOM_noprofile, P_NOPROFILE },
3191 { ATOM_iso, P_ISO },
3192 { ATOM_public, P_PUBLIC },
3193 { ATOM_non_terminal, P_NON_TERMINAL },
3194 { ATOM_quasi_quotation_syntax, P_QUASI_QUOTATION_SYNTAX },
3195 { ATOM_clausable, P_CLAUSABLE },
3196 { ATOM_autoload, P_AUTOLOAD },
3197 { (atom_t)0, 0 }
3198 };
3199
3200 static unsigned int
attribute_mask(atom_t key)3201 attribute_mask(atom_t key)
3202 { const patt_mask *p;
3203
3204 for(p=patt_masks; p->key; p++)
3205 { if ( p->key == key )
3206 return p->mask;
3207 }
3208
3209 { GET_LD
3210 term_t t;
3211
3212 return ( (t = PL_new_term_ref()) &&
3213 PL_put_atom(t, key) &&
3214 PL_domain_error("predicate_property", t)
3215 );
3216 }
3217 }
3218
3219
3220 static size_t
num_visible_clauses(Definition def,atom_t key)3221 num_visible_clauses(Definition def, atom_t key)
3222 { GET_LD
3223
3224 if ( LD->gen_reload != GEN_INVALID )
3225 { ClauseRef c;
3226 size_t num_clauses = 0;
3227
3228 acquire_def(def);
3229 for(c = def->impl.clauses.first_clause; c; c = c->next)
3230 { Clause cl = c->value.clause;
3231 if ( key == ATOM_number_of_rules && true(cl, UNIT_CLAUSE) )
3232 continue;
3233 if ( visibleClause(cl, generationFrame(environment_frame)) )
3234 num_clauses++;
3235 }
3236 release_def(def);
3237 return num_clauses;
3238 }
3239
3240 if ( key == ATOM_number_of_clauses )
3241 return def->impl.clauses.number_of_clauses;
3242 else
3243 return def->impl.clauses.number_of_rules;
3244 }
3245
3246
3247 size_t
sizeof_predicate(Definition def)3248 sizeof_predicate(Definition def)
3249 { GET_LD
3250 size_t size = sizeof(*def);
3251
3252 size += sizeof_supervisor(def->codes);
3253
3254 if ( false(def, P_FOREIGN) )
3255 { ClauseRef c;
3256
3257 acquire_def(def);
3258 for(c = def->impl.clauses.first_clause; c; c = c->next)
3259 { Clause cl = c->value.clause;
3260
3261 size += sizeofClause(cl->code_size);
3262 size += SIZEOF_CREF_CLAUSE;
3263 }
3264 release_def(def);
3265
3266 size += sizeofClauseIndexes(def);
3267 }
3268
3269 return size;
3270 }
3271
3272
3273 static
3274 PRED_IMPL("$get_predicate_attribute", 3, get_predicate_attribute,
3275 PL_FA_TRANSPARENT)
3276 { PRED_LD
3277 term_t pred = A1;
3278 term_t what = A2;
3279 term_t value = A3;
3280 Procedure proc;
3281 Definition def;
3282 functor_t fd;
3283 atom_t key;
3284 Module module = (Module) NULL;
3285 unsigned int att;
3286 term_t head = PL_new_term_ref();
3287
3288 if ( !PL_strip_module(pred, &module, head) ||
3289 !PL_get_functor(head, &fd) ||
3290 ( !(proc = visibleProcedure(fd, module PASS_LD)) &&
3291 !(proc = isCurrentProcedure(fd, module)) ) )
3292 fail;
3293
3294 def = proc->definition;
3295
3296 if ( !PL_get_atom(what, &key) )
3297 return PL_error(NULL, 0, NULL, ERR_TYPE, ATOM_atom, what);
3298
3299 if ( key == ATOM_imported )
3300 { if ( module == def->module )
3301 fail;
3302 return PL_unify_atom(value, def->module->name);
3303 } else if ( key == ATOM_indexed )
3304 { return unify_index_pattern(proc, value);
3305 } else if ( key == ATOM_meta_predicate )
3306 { if ( false(def, P_META) )
3307 fail;
3308 return unify_meta_pattern(proc, value);
3309 } else if ( key == ATOM_exported )
3310 { return PL_unify_integer(value, isPublicModule(module, proc));
3311 } else if ( key == ATOM_defined )
3312 { int d;
3313
3314 if ( isDefinedProcedure(proc) )
3315 d = 1;
3316 else
3317 d = 0;
3318
3319 return PL_unify_integer(value, d);
3320 } else if ( key == ATOM_line_count || key == ATOM_file )
3321 { int line;
3322 Clause clause;
3323 int rc = FALSE;
3324
3325 if ( false(def, P_FOREIGN|P_THREAD_LOCAL) )
3326 { acquire_def(def);
3327 if ( def->impl.clauses.first_clause &&
3328 (clause = def->impl.clauses.first_clause->value.clause) &&
3329 (line=clause->line_no) )
3330 { if ( key == ATOM_line_count )
3331 { rc = PL_unify_integer(value, line);
3332 } else
3333 { SourceFile sf = indexToSourceFile(clause->source_no);
3334
3335 if ( sf )
3336 rc = PL_unify_atom(value, sf->name);
3337 }
3338 }
3339 release_def(def);
3340 }
3341
3342 return rc;
3343 } else if ( key == ATOM_foreign )
3344 { return PL_unify_integer(value, true(def, P_FOREIGN) ? 1 : 0);
3345 } else if ( key == ATOM_number_of_clauses )
3346 { size_t num_clauses;
3347 if ( def->flags & P_FOREIGN )
3348 fail;
3349
3350 def = getProcDefinition(proc);
3351 num_clauses = num_visible_clauses(def, key);
3352 if ( num_clauses == 0 && false(def, P_DYNAMIC) )
3353 fail;
3354 return PL_unify_int64(value, num_clauses);
3355 } else if ( key == ATOM_last_modified_generation )
3356 { if ( def->flags & P_FOREIGN )
3357 fail;
3358 def = getProcDefinition(proc);
3359 return PL_unify_int64(value, def->last_modified);
3360 } else if ( key == ATOM_number_of_rules )
3361 { if ( def->flags & P_FOREIGN )
3362 fail;
3363
3364 def = getProcDefinition(proc);
3365 if ( def->impl.clauses.number_of_clauses == 0 && false(def, P_DYNAMIC) )
3366 fail;
3367 return PL_unify_integer(value, num_visible_clauses(def, key));
3368 } else if ( key == ATOM_size )
3369 { def = getProcDefinition(proc);
3370 return PL_unify_integer(value, sizeof_predicate(def));
3371 } else if ( tbl_is_predicate_attribute(key) )
3372 { size_t sz_value;
3373
3374 if ( tbl_get_predicate_attribute(def, key, &sz_value) == TRUE )
3375 return PL_unify_int64(value, sz_value);
3376 return FALSE;
3377 } else if ( (att = attribute_mask(key)) )
3378 { return PL_unify_integer(value, (def->flags & att) ? 1 : 0);
3379 } else
3380 { return FALSE;
3381 }
3382 }
3383
3384
3385 static int
setDynamicDefinition_unlocked(Definition def,bool isdyn)3386 setDynamicDefinition_unlocked(Definition def, bool isdyn)
3387 { GET_LD
3388
3389 if ( ( isdyn && true(def, P_DYNAMIC)) ||
3390 (!isdyn && false(def, P_DYNAMIC)) )
3391 return TRUE;
3392
3393 if ( isdyn ) /* static --> dynamic */
3394 { if ( truePrologFlag(PLFLAG_PROTECT_STATIC_CODE) &&
3395 hasClausesDefinition(def) )
3396 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PREDICATE, def);
3397
3398 set(def, P_DYNAMIC);
3399 freeCodesDefinition(def, TRUE); /* reset to S_VIRGIN */
3400 } else /* dynamic --> static */
3401 { clear(def, P_DYNAMIC);
3402 freeCodesDefinition(def, TRUE); /* reset to S_VIRGIN */
3403 }
3404
3405 return TRUE;
3406 }
3407
3408
3409 int
setDynamicDefinition(Definition def,bool isdyn)3410 setDynamicDefinition(Definition def, bool isdyn)
3411 { int rc;
3412
3413 LOCKDEF(def);
3414 rc = setDynamicDefinition_unlocked(def, isdyn);
3415 UNLOCKDEF(def);
3416
3417 return rc;
3418 }
3419
3420 int
setThreadLocalDefinition(Definition def,bool val)3421 setThreadLocalDefinition(Definition def, bool val)
3422 {
3423 #ifdef O_PLMT
3424
3425 LOCKDEF(def);
3426 if ( (val && true(def, P_THREAD_LOCAL)) ||
3427 (!val && false(def, P_THREAD_LOCAL)) )
3428 { UNLOCKDEF(def);
3429 return TRUE;
3430 }
3431
3432 if ( val ) /* static --> local */
3433 { if ( def->impl.clauses.first_clause )
3434 { UNLOCKDEF(def);
3435 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PREDICATE, def);
3436 }
3437
3438 def->impl.local.local = new_ldef_vector();
3439 MEMORY_RELEASE();
3440 set(def, P_DYNAMIC|P_VOLATILE|P_THREAD_LOCAL);
3441 def->codes = SUPERVISOR(thread_local);
3442
3443 UNLOCKDEF(def);
3444 return TRUE;
3445 } else /* local --> static */
3446 { UNLOCKDEF(def);
3447 return PL_error(NULL, 0, "predicate is thread-local",
3448 ERR_MODIFY_STATIC_PREDICATE, def);
3449 }
3450 #else
3451 setDynamicDefinition(def, val);
3452
3453 if ( val )
3454 set(def, P_VOLATILE|P_THREAD_LOCAL);
3455 else
3456 clear(def, P_VOLATILE|P_THREAD_LOCAL);
3457
3458 succeed;
3459 #endif
3460 }
3461
3462
3463 static int
setClausableDefinition(Definition def,int val)3464 setClausableDefinition(Definition def, int val)
3465 { GET_LD
3466
3467 if ( val )
3468 { if ( truePrologFlag(PLFLAG_PROTECT_STATIC_CODE) &&
3469 hasClausesDefinition(def) )
3470 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PREDICATE, def);
3471 set(def, P_CLAUSABLE);
3472 } else
3473 { clear(def, P_CLAUSABLE);
3474 }
3475
3476 return TRUE;
3477 }
3478
3479 int
setAttrDefinition(Definition def,unsigned attr,int val)3480 setAttrDefinition(Definition def, unsigned attr, int val)
3481 { int rc;
3482
3483 if ( attr == P_DYNAMIC )
3484 { rc = setDynamicDefinition(def, val);
3485 } else if ( attr == P_THREAD_LOCAL )
3486 { rc = setThreadLocalDefinition(def, val);
3487 } else if ( attr == P_CLAUSABLE )
3488 { rc = setClausableDefinition(def, val);
3489 } else
3490 { if ( !val )
3491 { clear(def, attr);
3492 } else
3493 { set(def, attr);
3494 }
3495 if ( attr == P_INCREMENTAL )
3496 freeCodesDefinition(def, TRUE);
3497
3498 rc = TRUE;
3499 }
3500
3501 return rc;
3502 }
3503
3504
3505 static int
get_bool_or_int_ex(term_t t,int * val ARG_LD)3506 get_bool_or_int_ex(term_t t, int *val ARG_LD)
3507 { if ( PL_get_bool(t, val) )
3508 return TRUE;
3509 if ( PL_get_integer(t, val) && !(*val & ~1) )
3510 return TRUE; /* accept 0 and 1 */
3511 return PL_get_bool_ex(t, val); /* generate an error */
3512 }
3513
3514
3515 static
3516 PRED_IMPL("$set_predicate_attribute", 3, set_predicate_attribute,
3517 PL_FA_TRANSPARENT)
3518 { PRED_LD
3519 term_t pred = A1;
3520 term_t what = A2;
3521 term_t value = A3;
3522 Procedure proc;
3523 Definition def;
3524 atom_t key;
3525 int val;
3526 uintptr_t att;
3527
3528 if ( !PL_get_atom_ex(what, &key) )
3529 return FALSE;
3530 if ( tbl_is_predicate_attribute(key) )
3531 { size_t v;
3532 atom_t inf;
3533
3534 if ( PL_get_atom(value, &inf) && inf == ATOM_infinite )
3535 v = (size_t)-1;
3536 else if ( !PL_get_size_ex(value, &v) )
3537 return FALSE;
3538
3539 if ( get_procedure(pred, &proc, 0, GP_DEFINE|GP_NAMEARITY) )
3540 return tbl_set_predicate_attribute(proc->definition, key, v) == TRUE;
3541
3542 return FALSE;
3543 }
3544
3545 if ( !get_bool_or_int_ex(value, &val PASS_LD) ||
3546 !(att = attribute_mask(key)) )
3547 return FALSE;
3548
3549 if ( (att&SPY_ME) )
3550 { if ( !get_procedure(pred, &proc, 0, GP_RESOLVE) )
3551 fail;
3552 } else
3553 { if ( !get_procedure(pred, &proc, 0, GP_DEFINE|GP_NAMEARITY) )
3554 fail;
3555 }
3556 def = proc->definition;
3557
3558 if ( ReadingSource && MODULE_parse == def->module )
3559 { SourceFile sf = lookupSourceFile(source_file_name, TRUE);
3560 int rc = setAttrProcedureSource(sf, proc, att, val PASS_LD);
3561 releaseSourceFile(sf);
3562 return rc;
3563 } else
3564 { return setAttrDefinition(def, att, val);
3565 }
3566 }
3567
3568
3569 static
3570 PRED_IMPL("$default_predicate", 2, default_predicate, PL_FA_TRANSPARENT)
3571 { Procedure p1, p2;
3572
3573 if ( get_procedure(A1, &p1, 0, GP_FIND) &&
3574 get_procedure(A2, &p2, 0, GP_FIND) )
3575 { if ( p1->definition == p2->definition || !isDefinedProcedure(p1) )
3576 succeed;
3577 }
3578
3579 fail;
3580 }
3581
3582
3583 static
3584 PRED_IMPL("$get_clause_attribute", 3, get_clause_attribute, 0)
3585 { GET_LD
3586 Clause clause;
3587 atom_t a;
3588
3589 term_t ref = A1;
3590 term_t att = A2;
3591 term_t value = A3;
3592
3593 if ( !PL_get_clref(ref, &clause) ||
3594 !PL_get_atom_ex(att, &a) )
3595 return FALSE;
3596
3597 if ( a == ATOM_line_count )
3598 { if ( clause->line_no )
3599 return PL_unify_integer(value, clause->line_no);
3600 } else if ( a == ATOM_file )
3601 { SourceFile sf = indexToSourceFile(clause->source_no);
3602
3603 if ( sf )
3604 return PL_unify_atom(value, sf->name);
3605 } else if ( a == ATOM_owner )
3606 { SourceFile sf = indexToSourceFile(clause->owner_no);
3607
3608 if ( sf )
3609 return PL_unify_atom(value, sf->name);
3610 } else if ( a == ATOM_size )
3611 { size_t size = sizeofClause(clause->code_size);
3612
3613 return PL_unify_int64(value, size);
3614 } else if ( a == ATOM_fact )
3615 { return PL_unify_atom(value,
3616 true(clause, UNIT_CLAUSE) ? ATOM_true
3617 : ATOM_false);
3618 } else if ( a == ATOM_erased )
3619 { atom_t erased;
3620
3621 if ( visibleClause(clause, generationFrame(environment_frame)) )
3622 erased = ATOM_false;
3623 else
3624 erased = ATOM_true;
3625
3626 return PL_unify_atom(value, erased);
3627 } else if ( a == ATOM_predicate_indicator )
3628 { if ( unify_definition(MODULE_user, value,
3629 clause->predicate, 0,
3630 GP_QUALIFY|GP_NAMEARITY) )
3631 return TRUE;
3632 } else if ( a == ATOM_module )
3633 { return PL_unify_atom(value, clauseBodyContext(clause)->name);
3634 }
3635
3636 fail;
3637 }
3638
3639
3640 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
3641 redefineProcedure() is called when a procedure needs to be defined and
3642 it seems to have a definition.
3643
3644 Sf is the `owning' source-file
3645
3646 (*) occurs if this is actually false. This happens if a file holding a
3647 running predicate is reloaded because the clauses cannot be wiped.
3648 (**) there is a definition, but we are reloading and we have not yet
3649 seen this predicate, so it isn't there.
3650 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
3651
3652 int
redefineProcedure(Procedure proc,SourceFile sf,unsigned int suppress)3653 redefineProcedure(Procedure proc, SourceFile sf, unsigned int suppress)
3654 { GET_LD
3655 Definition def = proc->definition;
3656
3657 if ( true(def, P_FOREIGN) )
3658 { /* first call printMessage() */
3659 /* so we can provide info about the old definition */
3660 if ( !printMessage(ATOM_warning,
3661 PL_FUNCTOR_CHARS, "redefined_procedure", 2,
3662 PL_CHARS, "foreign",
3663 _PL_PREDICATE_INDICATOR, proc) )
3664 return FALSE;
3665 /* ... then abolish */
3666 abolishProcedure(proc, def->module);
3667 } else if ( false(def, P_MULTIFILE) )
3668 { ClauseRef first;
3669
3670 def = getProcDefinition__LD(def PASS_LD);
3671 if ( !(first = hasClausesDefinition(def)) )
3672 return TRUE; /* (*) see above */
3673
3674 if ( first->value.clause->owner_no == sf->index )
3675 { if ( sf->reload && !reloadHasClauses(sf, proc PASS_LD) )
3676 return TRUE; /* (**) see above */
3677
3678 if ( ((debugstatus.styleCheck & ~suppress) & DISCONTIGUOUS_STYLE) &&
3679 false(def, P_DISCONTIGUOUS) &&
3680 sf->current_procedure )
3681 { if ( !printMessage(ATOM_warning,
3682 PL_FUNCTOR_CHARS, "discontiguous", 2,
3683 _PL_PREDICATE_INDICATOR, proc,
3684 _PL_PREDICATE_INDICATOR, sf->current_procedure) )
3685 return FALSE;
3686 }
3687 } else if ( !hasProcedureSourceFile(sf, proc) )
3688 { if ( true(def, P_THREAD_LOCAL) )
3689 return PL_error(NULL, 0, NULL, ERR_MODIFY_THREAD_LOCAL_PROC, proc);
3690
3691 if ( first )
3692 { if ( !printMessage(ATOM_warning,
3693 PL_FUNCTOR_CHARS, "redefined_procedure", 2,
3694 PL_CHARS, "static",
3695 _PL_PREDICATE_INDICATOR, proc) )
3696 return FALSE;
3697 }
3698 /* again, _after_ the printMessage() */
3699 abolishProcedure(proc, def->module);
3700 }
3701 }
3702
3703 return TRUE;
3704 }
3705
3706
3707
3708 /** copy_predicate(From:predicate_indicator, To:predicate_indicator) is det.
3709
3710 Copy all clauses of From into To. To is created as a dynamic predicate.
3711 */
3712
3713 static void
remoduleClause(Clause cl,Module old,Module new)3714 remoduleClause(Clause cl, Module old, Module new)
3715 { Code PC, end;
3716 int in_body = FALSE;
3717
3718 if ( true(cl, UNIT_CLAUSE) )
3719 return;
3720
3721 PC = cl->codes;
3722 end = &PC[cl->code_size];
3723 for( ; PC < end; PC = stepPC(PC) )
3724 { code op = fetchop(PC);
3725
3726 if ( in_body )
3727 { const char *ats=codeTable[op].argtype;
3728 int an;
3729
3730 for(an=0; ats[an]; an++)
3731 { switch(ats[an])
3732 { case CA1_PROC:
3733 { Procedure op = (Procedure)PC[an+1];
3734
3735 if ( op->definition->module != MODULE_system )
3736 { functor_t f = op->definition->functor->functor;
3737
3738 PC[an+1] = (code)lookupProcedure(f, new);
3739 }
3740 break;
3741 }
3742 case CA1_MODULE:
3743 { if ( old == (Module)PC[an+1] )
3744 PC[an+1] = (code)new;
3745 }
3746 }
3747 }
3748 } else if ( op == I_ENTER )
3749 { in_body = TRUE;
3750 }
3751 }
3752 }
3753
3754
3755 static
3756 PRED_IMPL("copy_predicate_clauses", 2, copy_predicate_clauses, PL_FA_TRANSPARENT)
3757 { PRED_LD
3758 Procedure from, to;
3759 Definition def, copy_def;
3760 ClauseRef cref;
3761 gen_t generation;
3762
3763 if ( !get_procedure(A1, &from, 0, GP_NAMEARITY|GP_RESOLVE) )
3764 fail;
3765 if ( !isDefinedProcedure(from) )
3766 trapUndefined(getProcDefinition(from) PASS_LD);
3767 def = getProcDefinition(from);
3768 generation = global_generation(); /* take a consistent snapshot */
3769
3770 if ( true(def, P_FOREIGN) )
3771 return PL_error(NULL, 0, NULL, ERR_PERMISSION_PROC,
3772 ATOM_access, ATOM_private_procedure, from);
3773
3774 if ( !get_procedure(A2, &to, 0, GP_NAMEARITY|GP_CREATE) )
3775 return FALSE;
3776
3777 copy_def = getProcDefinition(to);
3778 if ( true(copy_def, P_FOREIGN) )
3779 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, to);
3780 if ( false(copy_def, P_DYNAMIC) )
3781 { if ( isDefinedProcedure(to) )
3782 return PL_error(NULL, 0, NULL, ERR_MODIFY_STATIC_PROC, to);
3783 if ( !setDynamicDefinition(copy_def, TRUE) )
3784 fail;
3785 }
3786
3787 enterDefinition(def);
3788 acquire_def(def);
3789 for( cref = def->impl.clauses.first_clause; cref; cref = cref->next )
3790 { Clause cl = cref->value.clause;
3791
3792 if ( visibleClause(cl, generation) )
3793 { size_t size = sizeofClause(cl->code_size);
3794 Clause copy = PL_malloc_atomic(size);
3795
3796 memcpy(copy, cl, size);
3797 copy->predicate = copy_def;
3798 if ( def->module != copy_def->module )
3799 remoduleClause(copy, def->module, copy_def->module);
3800 #ifdef O_ATOMGC
3801 #ifdef O_DEBUG_ATOMGC
3802 forAtomsInClause(copy, register_atom_clause);
3803 #else
3804 forAtomsInClause(copy, PL_register_atom);
3805 #endif
3806 #endif
3807 assertProcedure(to, copy, CL_END PASS_LD);
3808 }
3809 }
3810 release_def(def);
3811 leaveDefinition(def);
3812
3813 return TRUE;
3814 }
3815
3816
3817 #if defined(O_MAINTENANCE) || defined(O_DEBUG)
3818
3819 /*******************************
3820 * INTERNAL DEBUGGING *
3821 *******************************/
3822
3823
3824 static void
listGenerations(Definition def)3825 listGenerations(Definition def)
3826 { GET_LD
3827 gen_t gen = generationFrame(environment_frame);
3828 ClauseRef cref;
3829 int i;
3830
3831 Sdprintf("%s has %d clauses at generation %ld\n",
3832 predicateName(def),
3833 def->impl.clauses.number_of_clauses, gen);
3834
3835 acquire_def(def);
3836 for(i=1,cref=def->impl.clauses.first_clause; cref; cref=cref->next, i++)
3837 { Clause clause = cref->value.clause;
3838
3839 Sdprintf("%p: [%2d] %8u-%10u%s%s%s\n",
3840 clause, i,
3841 clause->generation.created,
3842 clause->generation.erased,
3843 true(clause, CL_ERASED) ? " erased" : "",
3844 visibleClause(clause, gen) ? " v " : " X ",
3845 keyName(cref->d.key));
3846 }
3847 release_def(def);
3848
3849 listIndexGenerations(def, gen);
3850 }
3851
3852
3853 void
checkDefinition(Definition def)3854 checkDefinition(Definition def)
3855 { GET_LD
3856 unsigned int nc, indexed = 0;
3857 ClauseRef cref;
3858 unsigned int erased = 0;
3859 Definition old;
3860
3861 /* check basic clause list */
3862 acquire_def2(def, old);
3863 for(nc=0, cref = def->impl.clauses.first_clause; cref; cref=cref->next)
3864 { Clause clause = cref->value.clause;
3865
3866 if ( false(clause, CL_ERASED) )
3867 { if ( cref->d.key )
3868 indexed++;
3869 nc++;
3870 } else
3871 { erased++;
3872 }
3873 }
3874 release_def2(def, old);
3875
3876 assert(nc == def->impl.clauses.number_of_clauses);
3877 assert(erased == def->impl.clauses.erased_clauses);
3878
3879 checkClauseIndexes(def);
3880 }
3881
3882
3883 foreign_t
pl_check_procedure(term_t desc)3884 pl_check_procedure(term_t desc)
3885 { GET_LD
3886 Procedure proc;
3887 Definition def;
3888
3889 if ( !get_procedure(desc, &proc, 0, GP_FIND|GP_NAMEARITY) )
3890 fail;
3891 def = getProcDefinition(proc);
3892
3893 if ( true(def, P_FOREIGN) )
3894 fail;
3895
3896 LOCKDEF(def);
3897 checkDefinition(def);
3898 UNLOCKDEF(def);
3899
3900 succeed;
3901 }
3902
3903
3904 foreign_t
pl_list_generations(term_t desc)3905 pl_list_generations(term_t desc)
3906 { GET_LD
3907 Procedure proc;
3908 Definition def;
3909
3910 if ( !get_procedure(desc, &proc, 0, GP_FIND|GP_NAMEARITY) )
3911 fail;
3912 def = getProcDefinition(proc);
3913
3914 if ( true(def, P_FOREIGN) )
3915 fail; /* permission error */
3916
3917 listGenerations(def);
3918
3919 succeed;
3920 }
3921
3922
3923 #endif /*O_MAINTENANCE*/
3924
3925
3926 /*******************************
3927 * PUBLISH PREDICATES *
3928 *******************************/
3929
3930 BeginPredDefs(proc)
3931 PRED_DEF("retractall", 1, retractall, PL_FA_NONDETERMINISTIC|PL_FA_ISO)
3932 PRED_DEF("$set_predicate_attribute", 3, set_predicate_attribute,
3933 PL_FA_TRANSPARENT)
3934 PRED_DEF("$get_predicate_attribute", 3, get_predicate_attribute,
3935 PL_FA_TRANSPARENT)
3936 PRED_DEF("$default_predicate", 2, default_predicate, PL_FA_TRANSPARENT)
3937 PRED_DEF("meta_predicate", 1, meta_predicate, PL_FA_TRANSPARENT)
3938 PRED_DEF("$get_clause_attribute", 3, get_clause_attribute, 0)
3939 PRED_DEF("retract", 1, retract,
3940 PL_FA_TRANSPARENT|PL_FA_NONDETERMINISTIC|PL_FA_ISO)
3941 PRED_DEF("current_predicate", 1, current_predicate,
3942 PL_FA_TRANSPARENT|PL_FA_NONDETERMINISTIC|PL_FA_ISO)
3943 PRED_DEF("copy_predicate_clauses", 2, copy_predicate_clauses, PL_FA_TRANSPARENT)
3944 PRED_DEF("$cgc_params", 6, cgc_params, 0)
3945 EndPredDefs
3946