YAP 7.1.0
threads.yap
1/*************************************************************************
2* *
3* YAP Prolog *
4* *
5* Yap Prolog was developed at NCCUP - Universidade do Porto *
6* *
7 * Copyright L.Damas, V.S.Costa and Universidade do Porto 1985-1997 *
8* *
9**************************************************************************
10* *
11* File: threads.yap *
12* Last rev: 8/2/88 *
13* mods: *
14* comments: support threads *
15* *
16*************************************************************************/
17
18/**
19 @defgroup Threads Threads
20 @ingroup extensions
21 @{
22
23 YAP implements a SWI-Prolog compatible multithreading
24library. Like in SWI-Prolog, Prolog threads have their own stacks and
25only share the Prolog <em>heap</em>: predicates, records, flags and other
26global non-backtrackable data. The package is based on the POSIX thread
27standard (Butenhof:1997:PPT) used on most popular systems except
28for MS-Windows.
29
30*/
31
32:- system_module( '$_threads', [current_mutex/3,
35 message_queue_create/2,
37 message_queue_property/2,
39 mutex_create/2,
40 mutex_destroy/1,
41 mutex_lock/1,
42 mutex_property/2,
43 mutex_trylock/1,
44 mutex_unlock/1,
45 mutex_unlock_all/0,
47 thread_cancel/1,
51 thread_default/1,
52 thread_defaults/1,
53 thread_detach/1,
58 (thread_local)/1,
61 thread_property/1,
66 thread_set_default/1,
67 thread_set_defaults/1,
68 thread_signal/2,
70 threads/0,
71 (volatile)/1,
72 with_mutex/2], ['$reinit_thread0'/0,
73 '$thread_gfetch'/1,
74 '$thread_local'/2]).
75
76:- '$run_at_thread_start'/0'$system_catch'/4use_system_module( '$_boot', [
77 ,
78 ]).
79
80:- '$do_error'/2use_system_module( '$_errors', []).
81
82
83:- meta_predicate
84 thread_initialization(0),
86 thread_create(0, -, :),
87 thread_create(0, -),
89 thread_signal(+, 0),
90 with_mutex(+, 0),
91 thread_signal(+,0),
92 volatile(:).
93
94volatile(P) :- var(P),
95 throw(error(instantiation_error,volatile(P))).
96volatile(M:P) :-
97 '$do_volatile'(P,M).
98volatile((G1,G2)) :-
99 '$current_module'(M),
100 '$do_volatile'(G1,M),
101 '$do_volatile'(G2,M).
102volatile(P) :-
103 '$current_module'(M),
104 '$do_volatile'(P,M).
105
106'$do_volatile'(P,M) :- dynamic(M:P).
107
108/** @defgroup Creating_and_Destroying_Prolog_Threads Creating and Destroying Prolog Threads
109@ingroup Threads
110
111@{
112 */
113
114:- initialization('$init_thread0').
115
116'$init_thread0' :-
117 recorda('$thread_alias', [0|main], _),
118 recorda.
119'$init_thread0' :-
120 '$no_threads', recorda.
121'$init_thread0' :-
122 recorda('$thread_defaults', [0, 0, 0, false, true], _).
123
124'$reinit_thread0' :-
125 '$no_threads', recorda.
126'$reinit_thread0'.
127
128
129'$top_thread_goal'(G, Detached) :-
130 '$thread_self'(Id),
131 (Detached == true -> '$detach_thread'(Id) ; '$detach_thread'),
132 '$current_module'(Module),
133 '$run_at_thread_start',
134 % always finish with a throw to make sure we clean stacks.
135 '$system_catch'((G -> throw('$thread_finished'(true)) ; throw('$thread_finished'(false))),Module,Exception,'$close_thread'(Exception,Detached)),
136 % force backtracking and handling exceptions
137 '$system_catch'.
138
139'$close_thread'(Status, _Detached) :-
140 '$thread_zombie_self'(Id0), '$thread_zombie_self',
141 '$record_thread_status'(Id0,Status),
142 '$run_at_thread_exit'(Id0),
143 '$erase_thread_info'(Id0).
144
145% OK, we want to ensure atomicity here in case we get an exception while we
146% are closing down the thread.
147'$record_thread_status'(Id0,Stat) :- '$record_thread_status',
148 '$mk_tstatus_key'(Id0, Key),
149 (recorded(Key, _, R), erase(R), erase
150 ;
151 Stat = '$thread_finished'(Status) ->
152 recorda(Key, Status, _)
153 ;
154 recorda(Key, exception(Stat), _)
155 ).
156
157/** @pred thread_create(: _Goal_)
158
159
160Create a new Prolog detached thread using default options. See thread_create/3.
161
162*/
163thread_create(Goal) :-
164 G0 = thread_create(Goal),
165 must_be_callable(Goal),
166 '$thread_options'([detached(true)], [], Stack, Trail, System, Detached, AtExit, G0),
167 '$thread_new_tid'(Id),
168% '$erase_thread_info'(Id), % this should not be here
169 (
170 '$create_thread'(Goal, Stack, Trail, System, Detached, AtExit, Id)
171 ->
172 '$create_thread'
173 ;
174 '$mk_tstatus_key'(Id, Key),
175 recorda(Key, exception(resource_error(memory)),_)
176 ).
177
178/** @pred thread_create(: _Goal_, - _Id_)
179
180
181Create a new Prolog thread using default options. See thread_create/3.
182
183
184*/
185thread_create(Goal, Id) :-
186 G0 = thread_create(Goal, Id),
187 must_be_callable(Goal),
188 ( nonvar(Id) -> '$do_error'(uninstantiation_error(Id),G0) ; '$do_error' ),
189 '$thread_options'([], [], Stack, Trail, System, Detached, AtExit, G0),
190 '$thread_new_tid'(Id),
191% '$erase_thread_info'(Id), % this should not be here
192 (
193 '$create_thread'(Goal, Stack, Trail, System, Detached, AtExit, Id)
194 ->
195 '$create_thread'
196 ;
197 '$mk_tstatus_key'(Id, Key),
198 recorda(Key, exception(resource_error(memory)),_)
199 ).
200
201
202/**
203@pred thread_create(: _Goal_, - _Id_, + _Options_)
204
205Create a new Prolog thread (and underlying C-thread) and start it
206by executing _Goal_. If the thread is created successfully, the
207thread-identifier of the created thread is unified to _Id_.
208 _Options_ is a list of options. Currently defined options are:
209
210+ stack
211Set the limit in K-Bytes to which the Prolog stacks of
212this thread may grow. If omitted, the limit of the calling thread is
213used. See also the commandline `-S` option.
214
215+ trail
216Set the limit in K-Bytes to which the trail stack of this thread may
217grow. If omitted, the limit of the calling thread is used. See also the
218commandline option `-T`.
219
220+ alias
221Associate an alias-name with the thread. This named may be used to
222refer to the thread and remains valid until the thread is joined
223(see thread_join/2).
224
225+ at_exit
226Define an exit hook for the thread. This hook is called when the thread
227terminates, no matter its exit status.
228
229+ detached
230If `false` (default), the thread can be waited for using
231thread_join/2. thread_join/2 must be called on this thread
232to reclaim the all resources associated to the thread. If `true`,
233the system will reclaim all associated resources automatically after the
234thread finishes. Please note that thread identifiers are freed for reuse
235after a detached thread finishes or a normal thread has been joined.
236See also thread_join/2 and thread_detach/1.
237
238
239The _Goal_ argument is <em>copied</em> to the new Prolog engine.
240This implies further instantiation of this term in either thread does
241not have consequences for the other thread: Prolog threads do not share
242data from their stacks.
243*/
244thread_create(Goal, Id, Options) :-
245 G0 = thread_create(Goal, Id, Options),
246 must_be_callable(Goal),
247 ( nonvar(Id) -> '$do_error'(uninstantiation_error(Id),G0) ; '$do_error' ),
248 '$thread_options'(Options, Alias, Stack, Trail, System, Detached, AtExit, G0),
249 '$thread_new_tid'(Id),
250% '$erase_thread_info'(Id), % this should not be here
251 '$record_alias_info'(Id, Alias),
252 (
253 '$create_thread'(Goal, Stack, Trail, System, Detached, AtExit, Id)
254 ->
255 '$create_thread'
256 ;
257 '$mk_tstatus_key'(Id, Key),
258 recorda(Key, exception(resource_error(memory)),_)
259 ).
260
261'$erase_thread_info'(Id) :-
262 recorded('$thread_alias',[Id|_],R),
263 erase(R),
264 erase.
265'$erase_thread_info'(Id) :-
266 recorded('$thread_exit_hook', [Id|_], R),
267 erase(R),
268 erase.
269'$erase_thread_info'(_).
270
271
272'$thread_options'(Opts, Alias, Stack, Trail, System, Detached, AtExit, G) :-
273 strip_module(Opts, Mod, LOpts),
274 (
275 var(Opts)
276 ->
277 '$do_error'(instantiation_error,G)
278 ;
279 var(Mod)
280 ->
281 '$do_error'(instantiation_error,G)
282 ;
283 \+ atom(Mod)
284 ->
285 '$do_error'(uninstantiation_error(Mod),G)
286 ;
287 var(LOpts)
288 ->
289 '$do_error'(instantiation_error,G)
290 ;
291 '$thread_options'(LOpts, Alias, Stack, Trail, System, Detached, AtExit, Mod, G)
292 ).
293
294'$thread_options'([], _, Stack, Trail, System, Detached, AtExit, _M, _) :-
295 recorded('$thread_defaults', [DefaultStack, DefaultTrail, DefaultSystem, DefaultDetached, DefaultAtExit], _),
296 ( var(Stack) -> Stack = DefaultStack; var ),
297 ( var(Trail) -> Trail = DefaultTrail; var ),
298 ( var(System) -> System = DefaultSystem; var ),
299 ( var(Detached) -> Detached = DefaultDetached; var ),
300 ( var(AtExit) -> AtExit = DefaultAtExit; var ).
301'$thread_options'([Opt|Opts], Alias, Stack, Trail, System, Detached, AtExit, M, G0) :-
302 '$thread_option'(Opt, Alias, Stack, Trail, System, Detached, AtExit, M, G0),
303 '$thread_options'(Opts, Alias, Stack, Trail, System, Detached, AtExit, M, G0).
304
305'$thread_option'(Option, _, _, _, _, _, _, _, G0) :- var(Option), var,
306 '$do_error'(instantiation_error,G0).
307'$thread_option'(alias(Alias), Alias, _, _, _, _, _, _, G0) :- '$thread_option',
308 ( \+ atom(Alias) -> '$do_error'(type_error(atom,Alias),G0) ; '$do_error' ).
309'$thread_option'(stack(Stack), _, Stack, _, _, _, _, _, G0) :- '$thread_option',
310 ( \+ integer(Stack) -> '$do_error'(type_error(integer,Stack),G0) ; '$do_error' ).
311'$thread_option'(trail(Trail), _, _, Trail, _, _, _, _, G0) :- '$thread_option',
312 ( \+ integer(Trail) -> '$do_error'(type_error(integer,Trail),G0) ; '$do_error' ).
313'$thread_option'(system(System), _, _, _, System, _, _, _, G0) :- '$thread_option',
314 ( \+ integer(System) -> '$do_error'(type_error(integer,System),G0) ; '$do_error' ).
315'$thread_option'(detached(Detached), _, _, _, _, Detached, _, _, G0) :- '$thread_option',
316 ( Detached \== true, Detached \== false -> '$do_error'(domain_error(thread_option,Detached+[true,false]),G0) ; '$do_error' ).
317'$thread_option'(at_exit(AtExit), _, _, _, _, _, AtExit, _M, G0) :- '$thread_option',
318 ( \+ callable(AtExit) -> '$do_error'(type_error(callable,AtExit),G0) ; '$do_error' ).
319% succeed silently, like SWI.
320'$thread_option'(_Option, _, _, _, _, _, _, _, _G0).
321% '$do_error'(domain_error(thread_option,Option),G0).
322
323'$record_alias_info'(_, Alias) :-
324 var(Alias), var.
325'$record_alias_info'(_, Alias) :-
326 recorded('$thread_alias', [_|Alias], _), recorded,
327 '$do_error'(permission_error(create,thread,alias(Alias)), create_thread).
328'$record_alias_info'(Id, Alias) :-
329 recorda('$thread_alias', [Id|Alias], _).
330
331% vsc: ?????
332thread_defaults(Defaults) :-
333 nonvar(Defaults), nonvar,
334 '$do_error'(uninstantiation_error(Defaults), thread_defaults(Defaults)).
335thread_defaults([stack(Stack), trail(Trail), system(System), detached(Detached), at_exit(AtExit)]) :-
336 recorded('$thread_defaults',[Stack, Trail, System, Detached, AtExit], _).
337
338thread_default(Default) :-
339 var(Default), var,
340 recorded('$thread_defaults', Defaults, _),
341 '$thread_default'(Default, Defaults).
342thread_default(stack(Stack)) :- thread_default,
343 recorded('$thread_defaults',[Stack, _, _, _, _], _).
344thread_default(trail(Trail)) :- thread_default,
345 recorded('$thread_defaults',[_, Trail, _, _, _], _).
346thread_default(system(System)) :- thread_default,
347 recorded('$thread_defaults',[_, _, System, _, _], _).
348thread_default(detached(Detached)) :- thread_default,
349 recorded('$thread_defaults',[_, _, _, Detached, _], _).
350thread_default(at_exit(AtExit)) :- thread_default,
351 recorded('$thread_defaults',[_, _, _, _, AtExit], _).
352thread_default(Default) :-
353 '$do_error'(type_error(thread_option,Default),thread_default(Default)).
354
355'$thread_default'(stack(Stack), [Stack, _, _, _, _]).
356'$thread_default'(trail(Trail), [_, Trail, _, _, _]).
357'$thread_default'(system(System), [_, _, System, _, _]).
358'$thread_default'(detached(Detached), [_, _, _, Detached, _]).
359'$thread_default'(at_exit(AtExit), [_, _, _, _, AtExit]).
360
361thread_set_defaults(V) :- var(V), var,
362 '$do_error'(instantiation_error, thread_set_defaults(V)).
363thread_set_defaults([Default| Defaults]) :- thread_set_defaults,
364 '$thread_set_defaults'([Default| Defaults], thread_set_defaults([Default| Defaults])).
365thread_set_defaults(T) :-
366 '$do_error'(type_error(list, T), thread_set_defaults(T)).
367
368'$thread_set_defaults'([], _).
369'$thread_set_defaults'([Default| Defaults], G) :- '$thread_set_defaults',
370 '$thread_set_default'(Default, G),
371 '$thread_set_defaults'(Defaults, G).
372
373thread_set_default(V) :- var(V), var,
374 '$do_error'(instantiation_error, thread_set_default(V)).
375thread_set_default(Default) :-
376 '$thread_set_default'(Default, thread_set_default(Default)).
377
378'$thread_set_default'(stack(Stack), G) :-
379 \+ integer(Stack), integer,
380 '$do_error'(type_error(integer, Stack), G).
381'$thread_set_default'(stack(Stack), G) :-
382 Stack < 0, '$thread_set_default',
383 '$do_error'(domain_error(not_less_than_zero, Stack), G).
384'$thread_set_default'(stack(Stack), _) :- '$thread_set_default',
385 recorded('$thread_defaults', [_, Trail, System, Detached, AtExit], Ref),
386 erase(Ref),
387 recorda('$thread_defaults', [Stack, Trail, System, Detached, AtExit], _).
388
389'$thread_set_default'(trail(Trail), G) :-
390 \+ integer(Trail), integer,
391 '$do_error'(type_error(integer, Trail), G).
392'$thread_set_default'(trail(Trail), G) :-
393 Trail < 0, '$thread_set_default',
394 '$do_error'(domain_error(not_less_than_zero, Trail), G).
395'$thread_set_default'(trail(Trail), _) :- '$thread_set_default',
396 recorded('$thread_defaults', [Stack, _, System, Detached, AtExit], Ref),
397 erase(Ref),
398 recorda('$thread_defaults', [Stack, Trail, System, Detached, AtExit], _).
399
400'$thread_set_default'(system(System), G) :-
401 \+ integer(System), integer,
402 '$do_error'(type_error(integer, System), G).
403'$thread_set_default'(system(System), G0) :-
404 System < 0, '$thread_set_default',
405 '$do_error'(domain_error(not_less_than_zero, System), G0).
406'$thread_set_default'(system(System), _) :- '$thread_set_default',
407 recorded('$thread_defaults', [Stack, Trail, _, Detached, AtExit], Ref),
408 erase(Ref),
409 recorda('$thread_defaults', [Stack, Trail, System, Detached, AtExit], _).
410
411'$thread_set_default'(detached(Detached), G) :-
412 Detached \== '$thread_set_default', Detached \== '$thread_set_default', '$thread_set_default',
413 '$do_error'(type_error(boolean, Detached), G).
414'$thread_set_default'(detached(Detached), _) :- '$thread_set_default',
415 recorded('$thread_defaults', [Stack, Trail, System, _, AtExit], Ref),
416 erase(Ref),
417 recorda('$thread_defaults', [Stack, Trail, System, Detached, AtExit], _).
418
419'$thread_set_default'(at_exit(AtExit), G) :-
420 \+ callable(AtExit), callable,
421 '$do_error'(type_error(callable, AtExit), G).
422'$thread_set_default'(at_exit(AtExit), _) :- '$thread_set_default',
423 recorded('$thread_defaults', [Stack, Trail, System, Detached, _], Ref),
424 erase(Ref),
425 '$current_module'(M),
426 recorda('$thread_defaults', [Stack, Trail, System, Detached, M:AtExit], _).
427
428'$thread_set_default'(Default, G) :-
429 '$do_error'(domain_error(thread_default, Default), G).
430
431/** @pred thread_self(- _Id_)
432
433
434Get the Prolog thread identifier of the running thread. If the thread
435has an alias, the alias-name is returned.
436
437
438*/
439thread_self(Id) :-
440 nonvar(Id), \+ integer(Id), \+ atom(Id), atom,
441 '$do_error'(domain_error(thread_or_alias, Id), thread_self(Id)).
442thread_self(Id) :-
443 '$thread_self'(Id0),
444 '$thread_id_alias'(Id0, Id).
445
446/* Exit status may be either true, false, exception(Term), or exited(Term) */
447/** @pred thread_join(+ _Id_, - _Status_)
448
449
450Wait for the termination of thread with given _Id_. Then unify the
451result-status of the thread with _Status_. After this call,
452 _Id_ becomes invalid and all resources associated with the thread
453are reclaimed. Note that threads with the attribute `detached`
454`true` cannot be joined. See also current_thread/2.
455
456A thread that has been completed without thread_join/2 being
457called on it is partly reclaimed: the Prolog stacks are released and the
458C-thread is destroyed. A small data-structure representing the
459exit-status of the thread is retained until thread_join/2 is called on
460the thread. Defined values for _Status_ are:
461
462+ true
463The goal has been proven successfully.
464
465+ false
466The goal has failed.
467
468+ exception( _Term_)
469The thread is terminated on an
470exception. See print_message/2 to turn system exceptions into
471readable messages.
472
473+ exited( _Term_)
474The thread is terminated on thread_exit/1 using the argument _Term_.
475
476
477+ thread_detach(+ _Id_)
478
479
480Switch thread into detached-state (see `detached` option at
481thread_create/3 at runtime. _Id_ is the identifier of the thread
482placed in detached state.
483
484One of the possible applications is to simplify debugging. Threads that
485are created as `detached` leave no traces if they crash. For
486not-detached threads the status can be inspected using
487current_thread/2. Threads nobody is waiting for may be created
488normally and detach themselves just before completion. This way they
489leave no traces on normal completion and their reason for failure can be
490inspected.
491
492
493*/
494thread_join(Id, Status) :-
495 nonvar(Status), nonvar,
496 '$do_error'(uninstantiation_error(Status),thread_join(Id, Status)).
497thread_join(Id, Status) :-
498 '$check_thread_or_alias'(Id, thread_join(Id, Status)),
499 '$thread_id_alias'(Id0, Id),
500 '$thread_join'(Id0),
501 '$mk_tstatus_key'(Id0, Key),
502 recorded(Key, Status, R),
503 erase(R),
504 '$thread_destroy'(Id0).
505
506thread_cancel(Id) :-
507 (Id == main; Id == 0), ,
508 '$do_error'(permission_error(cancel, thread, main), thread_cancel(Id)).
509thread_cancel(Id) :-
510 thread_signal(Id, throw(error(thread_cancel(Id),thread_cancel(Id)))).
511
512thread_detach(Id) :-
513 '$check_thread_or_alias'(Id, thread_detach(Id)),
514 '$thread_id_alias'(Id0, Id),
515 '$detach_thread'(Id0),
516 '$mk_tstatus_key'(Id0, Key),
517 ( recorded(Key, _, _) ->
518 '$erase_thread_info'(Id0),
519 '$thread_destroy'(Id0)
520 ;
521 '$thread_unlock'(Id0)
522 ).
523
524/** @pred thread_exit(+ _Term_)
525
526
527Terminates the thread immediately, leaving `exited( _Term_)` as
528result-state for thread_join/2. If the thread has the attribute
529`detached` `true` it terminates, but its exit status cannot be
530retrieved using thread_join/2 making the value of _Term_
531irrelevant. The Prolog stacks and C-thread are reclaimed.
532
533
534*/
535thread_exit(Term) :-
536 var(Term), var,
537 '$do_error'(instantiation_error, thread_exit(Term)).
538thread_exit(Term) :-
539 throw('$thread_finished'(exited(Term))).
540
541'$run_at_thread_exit'(_Id0) :-
542 '$thread_run_at_exit'(G, M),
543 catch(once(M:G), _, fail),
544 catch.
545'$run_at_thread_exit'(Id0) :-
546 recorded('$thread_exit_hook',[Id0|Hook],R), erase(R),
547 catch(once(Hook),_,fail),
548 catch.
549'$run_at_thread_exit'(_).
550
551/** @pred thread_at_exit(: _Term_)
552
553
554Run _Goal_ just before releasing the thread resources. This is to
555be compared to `at_halt/1`, but only for the current
556thread. These hooks are ran regardless of why the execution of the
557thread has been completed. As these hooks are run, the return-code is
558already available through thread_property/2 using the result of
559thread_self/1 as thread-identifier. If you want to guarantee the
560execution of an exit hook no matter how the thread terminates (the thread
561can be aborted before reaching the thread_at_exit/1 call), consider
562using instead the `at_exit/1` option of thread_create/3.
563
564
565*/
566thread_at_exit(Goal) :-
567 must_be_callable(Goal),
568 '$thread_self'(Id0),
569 recordz('$thread_exit_hook',[Id0|Goal],_).
570
571/**
572@}
573*/
574
575/**
576@defgroup Monitoring_Threads Monitoring Threads
577@ingroup Threads
578
579
580Normal multi-threaded applications should not need these the predicates
581from this section because almost any usage of these predicates is
582unsafe. For example checking the existence of a thread before signalling
583it is of no use as it may vanish between the two calls. Catching
584exceptions using catch/3 is the only safe way to deal with
585thread-existence errors.
586
587These predicates are provided for diagnosis and monitoring tasks.
588@{
589*/
590
591/** @pred current_thread(+ _Id_, - _Status_)
592
593
594Enumerates identifiers and status of all currently known threads.
595Calling current_thread/2 does not influence any thread. See also
596thread_join/2. For threads that have an alias-name, this name is
597returned in _Id_ instead of the numerical thread identifier.
598 _Status_ is one of:
599
600+ running
601The thread is running. This is the initial status of a thread. Please
602note that threads waiting for something are considered running too.
603
604+ false
605The _Goal_ of the thread has been completed and failed.
606
607+ true
608The _Goal_ of the thread has been completed and succeeded.
609
610+ exited( _Term_)
611The _Goal_ of the thread has been terminated using thread_exit/1
612with _Term_ as argument. If the underlying native thread has
613exited (using pthread_exit()) _Term_ is unbound.
614
615+ exception( _Term_)
616The _Goal_ of the thread has been terminated due to an uncaught
617exception (see throw/1 and catch/3).
618
619
620
621*/
622current_thread(Id, Status) :-
623 catch(thread_property(Id, status(Status)),
624 error(existence_error(_,_),_), fail).
625
626
627'$thread_id_alias'(Id, Alias) :-
628 recorded('$thread_alias', [Id|Alias], _), recorded.
629'$thread_id_alias'(Id, Id).
630
631
632
633thread_property(Prop) :-
634 '$check_thread_property'(Prop, thread_property(Prop)),
635 '$thread_self'(Id),
636 '$thread_property'(Prop, Id).
637
638/** @pred thread_property(? _Id_, ? _Property_)
639
640
641Enumerates the properties of the specified thread.
642Calling thread_property/2 does not influence any thread. See also
643thread_join/2. For threads that have an alias-name, this name can
644be used in _Id_ instead of the numerical thread identifier.
645 _Property_ is one of:
646
647+ status( _Status_)
648The thread status of a thread (see below).
649
650+ alias( _Alias_)
651The thread alias, if it exists.
652
653+ at_exit( _AtExit_)
654The thread exit hook, if defined (not available if the thread is already terminated).
655
656+ detached( _Boolean_)
657The detached state of the thread.
658
659+ stack( _Size_)
660The thread stack data-area size.
661
662+ trail( _Size_)
663The thread trail data-area size.
664
665+ system( _Size_)
666The thread system data-area size.
667
668
669
670*/
671thread_property(Id, Prop) :-
672 ( nonvar(Id) ->
673 '$check_thread_or_alias'(Id, thread_property(Id, Prop))
674 ; '$enumerate_threads'(Id)
675 ),
676 '$check_thread_property'(Prop, thread_property(Id, Prop)),
677 '$thread_id_alias'(Id0, Id),
678 '$thread_property'(Prop, Id0).
679
680'$enumerate_threads'(Id) :-
681 '$max_threads'(Max),
682 Max1 is Max-1,
683 between(0,Max1,Id),
684 '$thread_stacks'(Id, _, _, _).
685
686'$thread_property'(alias(Alias), Id) :-
687 recorded('$thread_alias', [Id|Alias], _).
688'$thread_property'(status(Status), Id) :-
689 '$mk_tstatus_key'(Id, Key),
690 ( recorded(Key, Exit, _) ->
691 Status = Exit
692 ; Status = recorded
693 ).
694'$thread_property'(detached(Detached), Id) :-
695 ( '$thread_detached'(Id,Detached) -> '$thread_detached' ; Detached = '$thread_detached' ).
696'$thread_property'(at_exit(M:G), _Id) :-
697 '$thread_run_at_exit'(G,M).
698'$thread_property'(stack(Stack), Id) :-
699 '$thread_stacks'(Id, Stack, _, _).
700'$thread_property'(trail(Trail), Id) :-
701 '$thread_stacks'(Id, _, Trail, _).
702'$thread_property'(system(System), Id) :-
703 '$thread_stacks'(Id, _, _, System).
704
705'$thread_stacks' :-
706 format(user_error,'------------------------------------------------------------------------~n',[]),
707 format(user_error, '~t~a~48+~n', 'Thread Detached Status'),
708 format(user_error,'------------------------------------------------------------------------~n',[]),
709 thread_property(Id, detached(Detached)),
710 thread_property(Id, status(Status)),
711 '$thread_id_alias'(Id, Alias),
712 format(user_error,'~t~q~30+~33|~w~42|~q~n', [Alias, Detached, Status]),
713 format.
714format :-
715 format(user_error,'------------------------------------------------------------------------~n',[]).
716
717
718'$check_thread_or_alias'(Term, Goal) :-
719 var(Term), var,
720 '$do_error'(instantiation_error, Goal).
721'$check_thread_or_alias'(Term, Goal) :-
722 \+ integer(Term), \+ atom(Term), atom,
723 '$do_error'(domain_error(thread_or_alias, Term), Goal).
724'$check_thread_or_alias'(Term, Goal) :-
725 atom(Term), \+ recorded('$thread_alias',[_|Term],_), recorded,
726 '$do_error'(existence_error(thread, Term), Goal).
727'$check_thread_or_alias'(Term, Goal) :-
728 integer(Term), \+ '$valid_thread'(Term), '$valid_thread',
729 '$do_error'(existence_error(thread, Term), Goal).
730'$check_thread_or_alias'(_,_).
731
732'$check_thread_property'(Term, _) :-
733 var(Term), var.
734'$check_thread_property'(alias(_), _) :- '$check_thread_property'.
735'$check_thread_property'(detached(_), _) :- '$check_thread_property'.
736'$check_thread_property'(at_exit(_), _) :- '$check_thread_property'.
737'$check_thread_property'(status(_), _) :- '$check_thread_property'.
738'$check_thread_property'(stack(_), _) :- '$check_thread_property'.
739'$check_thread_property'(trail(_), _) :- '$check_thread_property'.
740'$check_thread_property'(system(_), _) :- '$check_thread_property'.
741'$check_thread_property'(Term, Goal) :-
742 '$do_error'(domain_error(thread_property, Term), Goal).
743
744'$check_mutex_or_alias'(Term, Goal) :-
745 var(Term), var,
746 '$do_error'(instantiation_error, Goal).
747'$check_mutex_or_alias'(Term, Goal) :-
748 \+ integer(Term), \+ atom(Term), atom,
749 '$do_error'(domain_error(mutex_or_alias, Term), Goal).
750'$check_mutex_or_alias'(Term, Goal) :-
751 atom(Term), \+ recorded('$mutex_alias',[_|Term],_), recorded,
752 '$do_error'(existence_error(mutex, Term), Goal).
753'$check_mutex_or_alias'(Term, Goal) :-
754% integer(Term), \+ '$valid_mutex'(Term), !,
755 integer(Term), \+ recorded('$mutex_alias',[Term|_],_), recorded,
756 '$do_error'(existence_error(mutex, Term), Goal).
757'$check_mutex_or_alias'(_,_).
758
759'$check_mutex_property'(Term, _) :-
760 var(Term), var.
761'$check_mutex_property'(alias(_), _) :- '$check_mutex_property'.
762'$check_mutex_property'(status(Status), Goal) :- '$check_mutex_property',
763 ( var(Status) ->
764 var
765 ; Status = var ->
766 var
767 ; Status = locked(_, _) ->
768 locked
769 ; '$do_error'(domain_error(mutex_property, status(Status)), Goal)
770 ).
771'$check_mutex_property'(Term, Goal) :-
772 '$do_error'(domain_error(mutex_property, Term), Goal).
773
774'$mk_tstatus_key'(Id0, Key) :-
775 atomic_concat('$thread_exit_status__',Id0,Key).
776
777/** @pred thread_statistics(+ _Id_, + _Key_, - _Value_)
778
779
780Obtains statistical information on thread _Id_ as `statistics/2`
781does in single-threaded applications. This call returns all keys
782of `statistics/2`, although only information statistics about the
783stacks and CPU time yield different values for each thread.
784
785+ mutex_statistics
786
787
788Print usage statistics on internal mutexes and mutexes associated
789with dynamic predicates. For each mutex two numbers are printed:
790the number of times the mutex was acquired and the number of
791collisions: the number times the calling thread has to
792wait for the mutex. The collision-count is not available on
793Windows as this would break portability to Windows-95/98/ME or
794significantly harm performance. Generally collision count is
795close to zero on single-CPU hardware.
796
797+ threads
798
799
800Prints a table of current threads and their status.
801
802
803
804 */
805thread_statistics(Id, Key, Val) :-
806 format("not implemented yet: ~w, ~w, ~w~n",[Id, Key, Val]).
807
808%% @}
809
810
811/** @defgroup Signalling_Threads Signalling Threads
812@ingroup Threadas
813
814
815These predicates provide a mechanism to make another thread execute some
816goal as an <em>interrupt</em>. Signalling threads is safe as these
817interrupts are only checked at safe points in the virtual machine.
818Nevertheless, signalling in multi-threaded environments should be
819handled with care as the receiving thread may hold a <em>mutex</em>
820(see with_mutex/2). Signalling probably only makes sense to start
821debugging threads and to cancel no-longer-needed threads with throw/1,
822where the receiving thread should be designed carefully do handle
823exceptions at any point.
824
825*/
826
827/** @defgroup Thread_Synchronisation Thread Synchronisation
828@ingroup Threads
829@{
830
831All
832 internal Prolog operations are thread-safe. This implies two Prolog
833threads can operate on the same dynamic predicate without corrupting the
834consistency of the predicate. This section deals with user-level
835<em>mutexes</em> (called <em>monitors</em> in ADA or
836<em>critical-sections</em> by Microsoft). A mutex is a
837<em>MUT</em>ual <em>EX</em>clusive device, which implies at most one thread
838can <em>hold</em> a mutex.
839
840Mutexes are used to realise related updates to the Prolog database.
841With `related', we refer to the situation where a `transaction' implies
842two or more changes to the Prolog database. For example, we have a
843predicate `address/2`, representing the address of a person and we want
844to change the address by retracting the old and asserting the new
845address. Between these two operations the database is invalid: this
846person has either no address or two addresses, depending on the
847assert/retract order.
848
849Here is how to realise a correct update:
850
851```
852:- initialization
853 mutex_create(addressbook).
854
855change_address(Id, Address) :-
856 mutex_lock(addressbook),
857 retractall(address(Id, _)),
858 asserta(address(Id, Address)),
859 mutex_unlock(addressbook).
860```
861*/
862
863/** @pred mutex_create(? _MutexId_)
864
865
866 Create a mutex. if _MutexId_ is an atom, a <em>named</em> mutex is
867 created. If it is a variable, an anonymous mutex reference is returned.
868 There is no limit to the number of mutexes that can be created.
869
870
871*/
872mutex_create(Id, Options) :-
873 nonvar(Id), nonvar,
874 '$do_error'(uninstantiation_error(Id), mutex_create(Id, Options)).
875mutex_create(Id, Options) :-
876 Goal = mutex_create(Id, Options),
877 '$mutex_options'(Options, Alias, Goal),
878 ( atom(Alias) ->
879 mutex_create(Alias)
880 ; mutex_create(Id)
881 ).
882
883'$mutex_options'(Var, _, Goal) :-
884 var(Var), var,
885 '$do_error'(instantiation_error, Goal).
886'$mutex_options'([], _, _) :- '$mutex_options'.
887'$mutex_options'([Option| Options], Alias, Goal) :- '$mutex_options',
888 '$mutex_option'(Option, Alias, Goal),
889 '$mutex_options'(Options, Alias, Goal).
890'$mutex_options'(Options, _, Goal) :-
891 '$do_error'(type_error(list, Options), Goal).
892
893'$mutex_option'(Var, _, Goal) :-
894 var(Var), var,
895 '$do_error'(instantiation_error, Goal).
896'$mutex_option'(alias(Alias), Alias, Goal) :- '$mutex_option',
897 ( atom(Alias) ->
898 atom
899 ; '$do_error'(type_error(atom, Alias), Goal)
900 ).
901'$mutex_option'(Option, _, Goal) :-
902 '$do_error'(domain_error(mutex_option, Option), Goal).
903
904/** @pred mutex_unlock_all
905
906
907Unlock all mutexes held by the current thread. This call is especially
908useful to handle thread-termination using abort/0 or exceptions. See
909also thread_signal/2.
910
911
912*/
913'$do_error' :-
914 '$thread_self'(Tid),
915 '$unlock_all_thread_mutexes'(Tid).
916
917'$unlock_all_thread_mutexes'(Tid) :-
918 recorded('$mutex_alias',[Id|_],_),
919 '$mutex_info'(Id, NRefs, Tid),
920 NRefs > 0,
921 '$mutex_unlock_all'(Id),
922 '$mutex_unlock_all'.
923'$unlock_all_thread_mutexes'(_).
924
925'$mutex_unlock_all'(Id) :-
926 '$mutex_info'(Id, NRefs, _),
927 NRefs > 0,
928 '$unlock_mutex'(Id),
929 '$mutex_unlock_all'(Id).
930
931/** @pred current_mutex(? _MutexId_, ? _ThreadId_, ? _Count_)
932
933
934Enumerates all existing mutexes. If the mutex is held by some thread,
935 _ThreadId_ is unified with the identifier of the holding thread and
936 _Count_ with the recursive count of the mutex. Otherwise,
937 _ThreadId_ is `[]` and _Count_ is 0.
938
939
940
941 */
942current_mutex(M, T, NRefs) :-
943 recorded('$mutex_alias',[Id|M],_),
944 '$mutex_info'(Id, NRefs, T).
945
946mutex_property(Mutex, Prop) :-
947 ( nonvar(Mutex) ->
948 '$check_mutex_or_alias'(Mutex, mutex_property(Mutex, Prop))
949 ; recorded('$mutex_alias', [_|Mutex], _)
950 ),
951 '$check_mutex_property'(Prop, mutex_property(Mutex, Prop)),
952 '$mutex_id_alias'(Id, Mutex),
953 '$mutex_property'(Id, Prop).
954
955'$mutex_property'(Id, alias(Alias)) :-
956 recorded('$mutex_alias', [Id|Alias], _),
957 Id \= Alias.
958'$mutex_property'(Id, status(Status)) :-
959 '$mutex_info'(Id, Count, HoldingThread),
960 ( Count =:= 0 ->
961 Status = unlocked
962 ; % Count > 0,
963 '$thread_id_alias'(HoldingThread, Alias),
964 once((Thread = Alias; Thread = HoldingThread)),
965 Status = locked(Thread, Count)
966 ).
967
968%% @}
969
970/** @defgroup Thread_Communication Thread communication
971@ingroup Threads
972
973
974Prolog threads can exchange data using dynamic predicates, database
975records, and other globally shared data. These provide no suitable means
976to wait for data or a condition as they can only be checked in an
977expensive polling loop. <em>Message queues</em> provide a means for
978threads to wait for data or conditions without using the CPU.
979
980Each thread has a message-queue attached to it that is identified
981by the thread. Additional queues are created using
982`message_queue_create/2`.
983@{
984*/
985
986message_queue_create(Id, Options) :-
987 nonvar(Id), nonvar,
988 '$do_error'(uninstantiation_error(Id), message_queue_create(Id, Options)).
989message_queue_create(Id, Options) :-
990 var(Options), var,
991 '$do_error'(instantiation_error, message_queue_create(Id, Options)).
992message_queue_create(Id, []) :- message_queue_create,
993 '$message_queue_create'(Id).
994message_queue_create(Id, [alias(Alias)]) :-
995 var(Alias), var,
996 '$do_error'(instantiation_error, message_queue_create(Id, [alias(Alias)])).
997message_queue_create(Id, [alias(Alias)]) :-
998 \+ atom(Alias), atom,
999 '$do_error'(type_error(atom,Alias), message_queue_create(Id, [alias(Alias)])).
1000message_queue_create(Id, [alias(Alias)]) :- var(Id), var,
1001 ( recorded('$thread_alias', [_|Alias], _) ->
1002 '$do_error'(permission_error(create,queue,alias(Alias)),message_queue_create(Alias, [alias(Alias)]))
1003 ; '$message_queue_create'(Id),
1004 recordz('$thread_alias', [Id|Alias], _)
1005 ).
1006message_queue_create(Alias, [alias(Alias)]) :- message_queue_create,
1007 ( recorded('$thread_alias', [_|Alias], _) ->
1008 '$do_error'(permission_error(create,queue,alias(Alias)),message_queue_create(Alias, [alias(Alias)]))
1009 ; '$message_queue_create'(Alias)
1010 ).
1011message_queue_create(Id, [Option| _]) :-
1012 '$do_error'(domain_error(queue_option, Option), message_queue_create(Id, [Option| _])).
1013message_queue_create(Id, Options) :-
1014 '$do_error'(type_error(list, Options), message_queue_create(Id, Options)).
1015
1016/** @pred message_queue_create(? _Queue_)
1017
1018
1019If _Queue_ is an atom, create a named queue. To avoid ambiguity
1020on `thread_send_message/2`, the name of a queue may not be in use
1021as a thread-name. If _Queue_ is unbound an anonymous queue is
1022created and _Queue_ is unified to its identifier.
1023
1024
1025*/
1027 ( var(Id) -> % ISO DTR
1028 '$message_queue_create'(Id)
1029 ; atom(Id) -> % old behavior
1030 '$message_queue_create'(Id)
1031 ; '$do_error'(uninstantiation_error(Id), message_queue_create(Id))
1032 ).
1033
1034/** @pred message_queue_destroy(+ _Queue_)
1035
1036
1037Destroy a message queue created with message_queue_create/1. It is
1038<em>not</em> allows to destroy the queue of a thread. Neither is it
1039allowed to destroy a queue other threads are waiting for or, for
1040anonymous message queues, may try to wait for later.
1041
1042
1043*/
1044message_queue_destroy(Name) :-
1045 var(Name), var,
1046 '$do_error'(instantiation_error,message_queue_destroy(Name)).
1047message_queue_destroy(Alias) :-
1048 recorded('$thread_alias', [Id|Alias], Ref),
1049 atom(Id), atom,
1050 '$message_queue_destroy'(Id),
1051 erase(Ref).
1052message_queue_destroy(Name) :-
1053 atom(Name),
1054 '$message_queue_destroy'(Name),
1055 recorded('$thread_alias', [Name|_Alias], Ref),
1056 erase(Ref),
1057 erase.
1059
1060/* @pred message_queue_property(+ _Queue_)
1061
1062
1063Report on the alias and number of messages stored in a queue created
1064with message_queue_create/1.
1065
1066+ `alias(Alias)` report the alias for stream _S_. It can also be used
1067to enumerate all message queues that have aliases, including anonymous
1068queues.
1069
1070+ `size(Size)` unifies _Size_ with the number of messages in the queue.
1071*/
1072
1073message_queue_property( Id, alias(Alias) ) :-
1074 recorded('$thread_alias',[Id|Alias],_).
1075message_queue_property( Alias, size(Size) ) :-
1076 ground(Alias),
1077 recorded('$thread_alias',[Id|Alias],_),
1078 '$message_queue_size'(Id, Size).
1079message_queue_property( Id, size(Size) ) :-
1080 '$message_queue_size'(Id, Size).
1081
1082
1083
1084/** @pred thread_send_message(+ _Term_)
1085
1086Places _Term_ in the message-queue of the thread running the goal.
1087Any term can be placed in a message queue, but note that the term is
1088copied to the receiving thread and variable-bindings are thus lost.
1089This call returns immediately.
1090*/
1091thread_send_message(Term) :-
1092 '$thread_self'(Id),
1093 thread_send_message(Id, Term).
1094
1095/** @pred thread_send_message(+ _QueueOrThreadId_, + _Term_)
1096
1097Place _Term_ in the given queue or default queue of the indicated
1098thread (which can even be the message queue of itself (see
1099thread_self/1). Any term can be placed in a message queue, but note that
1100the term is copied to the receiving thread and variable-bindings are
1101thus lost. This call returns immediately.
1102
1103If more than one thread is waiting for messages on the given queue and
1104at least one of these is waiting with a partially instantiated
1105 _Term_, the waiting threads are <em>all</em> sent a wakeup signal,
1106starting a rush for the available messages in the queue. This behaviour
1107can seriously harm performance with many threads waiting on the same
1108queue as all-but-the-winner perform a useless scan of the queue. If
1109there is only one waiting thread or all waiting threads wait with an
1110unbound variable an arbitrary thread is restarted to scan the queue.
1111
1112*/
1113thread_send_message(Queue, Term) :- var(Queue), var,
1114 '$do_error'(instantiation_error,thread_send_message(Queue,Term)).
1115thread_send_message(Queue, Term) :-
1116 recorded('$thread_alias',[Id|Queue],_R), recorded,
1117 '$message_queue_send'(Id, Term).
1118thread_send_message(Queue, Term) :-
1119 '$message_queue_send'(Queue, Term).
1120
1121/** @pred thread_get_message(? _Term_)
1122
1123
1124Examines the thread message-queue and if necessary blocks execution
1125until a term that unifies to _Term_ arrives in the queue. After
1126a term from the queue has been unified unified to _Term_, the
1127term is deleted from the queue and this predicate returns.
1128
1129Please note that not-unifying messages remain in the queue. After
1130the following has been executed, thread 1 has the term `gnu`
1131in its queue and continues execution using _A_ is `gnat`.
1132
1133```
1134 <thread 1>
1135 thread_get_message(a(A)),
1136
1137 <thread 2>
1138 thread_send_message(b(gnu)),
1139 thread_send_message(a(gnat)),
1140```
1141
1142See also thread_peek_message/1.
1143
1144
1145*/
1146thread_get_message(Term) :-
1147 '$thread_self'(Id),
1148 thread_get_message(Id, Term).
1149
1150/** @pred thread_get_message(+ _Queue_, ? _Term_)
1151
1152As thread_get_message/1, operating on a given queue. It is allowed to
1153peek into another thread's message queue, an operation that can be used
1154to check whether a thread has swallowed a message sent to it.
1155
1156
1157*/
1158thread_get_message(Queue, Term) :- var(Queue), var,
1159 '$do_error'(instantiation_error,thread_get_message(Queue,Term)).
1160thread_get_message(Queue, Term) :-
1161 recorded('$thread_alias',[Id|Queue],_R), recorded,
1162 '$message_queue_receive'(Id, Term).
1163thread_get_message(Queue, Term) :-
1164 '$message_queue_receive'(Queue, Term).
1165
1166
1167/** @pred thread_peek_message(? _Term_)
1168
1169
1170Examines the thread message-queue and compares the queued terms
1171with _Term_ until one unifies or the end of the queue has been
1172reached. In the first case the call succeeds (possibly instantiating
1173 _Term_. If no term from the queue unifies this call fails.
1174
1175
1176*/
1177thread_peek_message(Term) :-
1178 '$thread_self'(Id),
1179 thread_peek_message(Id, Term).
1180
1181/** @pred thread_peek_message(+ _Queue_, ? _Term_)
1182
1183As thread_peek_message/1, operating on a given queue. It is allowed to
1184peek into another thread's message queue, an operation that can be used
1185to check whether a thread has swallowed a message sent to it.
1186
1187
1188
1189Explicit message queues are designed with the <em>worker-pool</em> model
1190in mind, where multiple threads wait on a single queue and pick up the
1191first goal to execute. Below is a simple implementation where the
1192workers execute arbitrary Prolog goals. Note that this example provides
1193no means to tell when all work is done. This must be realised using
1194additional synchronisation.
1195
1196```
1197% create_workers(+Id, +N)
1198%
1199% Create a pool with given Id and number of workers.
1200
1201create_workers(Id, N) :-
1202 message_queue_create(Id),
1203 forall(between(1, N, _),
1204 thread_create(do_work(Id), _, [])).
1205
1206do_work(Id) :-
1207 repeat,
1208 thread_get_message(Id, Goal),
1209 ( catch(Goal, E, print_message(error, E))
1210 -> true
1211 ; print_message(error, goal_failed(Goal, worker(Id)))
1212 ),
1213 fail.
1214
1215% work(+Id, +Goal)
1216%
1217% Post work to be done by the pool
1218
1219work(Id, Goal) :-
1220 thread_send_message(Id, Goal).
1221```
1222
1223
1224 */
1225thread_peek_message(Queue, Term) :- var(Queue), var,
1226 '$do_error'(instantiation_error,thread_peek_message(Queue,Term)).
1227thread_peek_message(Queue, Term) :-
1228 recorded('$thread_alias',[Id|Queue],_R), recorded,
1229 '$message_queue_peek'(Id, Term).
1230tthread_peek_message(Queue, Term) :-
1231 '$message_queue_peek'(Queue, Term).
1232
1233%% @}
1234
1235/** @defgroup Signalling_Threads Signalling Threads
1236@ingroup Threadas
1237
1238
1239These predicates provide a mechanism to make another thread execute some
1240goal as an <em>interrupt</em>. Signalling threads is safe as these
1241interrupts are only checked at safe points in the virtual machine.
1242Nevertheless, signalling in multi-threaded environments should be
1243handled with care as the receiving thread may hold a <em>mutex</em>
1244(see with_mutex/2). Signalling probably only makes sense to start
1245debugging threads and to cancel no-longer-needed threads with throw/1,
1246where the receiving thread should be designed carefully do handle
1247exceptions at any point.
1248
1249@{
1250*/
1251
1252/** @pred thread_sleep(+ _Time_)
1253
1254th
1255Make current thread sleep for _Time_ seconds. _Time_ may be an
1256integer or a floating point number. When time is zero or a negative value
1257the call succeeds and returns immediately. This call should not be used if
1258alarms are also being used.
1259
1260
1261
1262 */
1263thread_sleep(Time) :-
1264 var(Time), var,
1265 '$do_error'(instantiation_error,thread_sleep(Time)).
1266thread_sleep(Time) :-
1267 integer(Time), integer,
1268 ( Time > 0 ->
1269 '$thread_sleep'(Time,0,_,_)
1270 ; '$thread_sleep'
1271 ).
1272thread_sleep(Time) :-
1273 float(Time), float,
1274 ( Time > 0.0 ->
1275 STime is integer(float_integer_part(Time)),
1276 NTime is integer(float_fractional_part(Time))*1000000000,
1277 '$thread_sleep'(STime,NTime,_,_)
1278 ; '$thread_sleep'
1279 ).
1280thread_sleep(Time) :-
1281 '$do_error'(type_error(number,Time),thread_sleep(Time)).
1282
1283
1284thread_signal(Id, Goal) :-
1285 '$check_thread_or_alias'(Id, thread_signal(Id, Goal)),
1286 must_be_callable(Goal),
1287 '$thread_id_alias'(Id0, Id),
1288 ( recorded('$thread_signal', [Id0| _], R), erase(R), erase
1289 ; erase
1290 ),
1291 recorda('$thread_signal', [Id0| Goal], _),
1292 '$signal_thread'(Id0).
1293
1294'$thread_gfetch'(G) :-
1295 '$thread_self'(Id),
1296 recorded('$thread_signal',[Id|G],R),
1297 erase(R).
1298
1299%% @}
1300
1301/** @defgroup Threads_and_Dynamic_Predicates Threads and Dynamic Predicates
1302@ingroup Threads
1303
1304@{
1305
1306Besides queues threads can share and exchange data using dynamic
1307predicates. The multi-threaded version knows about two types of
1308dynamic predicates. By default, a predicate declared <em>dynamic</em>
1309(see dynamic/1) is shared by all threads. Each thread may
1310assert, retract and run the dynamic predicate. Synchronisation inside
1311Prolog guarantees the consistency of the predicate. Updates are
1312<em>logical</em>: visible clauses are not affected by assert/retract
1313after a query started on the predicate. In many cases primitive from
1314thread synchronisation should be used to ensure application invariants on
1315the predicate are maintained.
1316
1317Besides shared predicates, dynamic predicates can be declared with the
1318thread_local/1 directive. Such predicates share their
1319attributes, but the clause-list is different in each thread.
1320
1321*/
1322
1323/** @pred thread_local( _+Functor/Arity_)
1324
1325
1326related to the dynamic/1 directive. It tells the system that the
1327predicate may be modified using assert/1, retract/1,
1328etc, during execution of the program. Unlike normal shared dynamic
1329data however each thread has its own clause-list for the predicate.
1330As a thread starts, this clause list is empty. If there are still
1331clauses as the thread terminates these are automatically reclaimed by
1332the system. The `thread_local` property implies
1333the property `dynamic`.
1334
1335Thread-local dynamic predicates are intended for maintaining
1336thread-specific state or intermediate results of a computation.
1337
1338It is not recommended to put clauses for a thread-local predicate into
1339a file as in the example below as the clause is only visible from the
1340thread that loaded the source-file. All other threads start with an
1341empty clause-list.
1342
1343```
1344:- thread_local
1345 foo/1.
1346
1347foo(gnat).
1348```
1349
1350
1351
1352
1353 */
1354thread_local(X) :-
1355 '$current_module'(M),
1356 '$thread_local'(X,M).
1357
1358'$thread_local'(X,M) :- var(X), var,
1359 '$do_error'(instantiation_error,thread_local(M:X)).
1360'$thread_local'(Mod:Spec,_) :- '$thread_local',
1361 '$thread_local'(Spec,Mod).
1362'$thread_local'([], _) :- '$thread_local'.
1363'$thread_local'([H|L], M) :- '$thread_local', '$thread_local'(H, M), '$thread_local'(L, M).
1364'$thread_local'((A,B),M) :- '$thread_local', '$thread_local'(A,M), '$thread_local'(B,M).
1365'$thread_local'(X,M) :- '$thread_local',
1366 '$thread_local2'(X,M).
1367
1368'$thread_local2'(A/N, Mod) :- integer(N), atom(A), atom,
1369 functor(T,A,N),
1370 (Mod \= idb -> '$predicate_flags'(T,Mod,F,F) ; '$predicate_flags'),
1371 ( '$install_thread_local'(T,Mod) -> '$install_thread_local' ;
1372 F /\ 0'$install_thread_local' =\= 0 -> '$do_error'(permission_error(modify,dynamic_procedure,A/N),thread_local(Mod:A/N)) ;
1373 '$do_error'(permission_error(modify,static_procedure,A/N),thread_local(Mod:A/N))
1374 ).
1375'$thread_local2'(X,Mod) :-
1376 '$do_error'(type_error(callable,X),thread_local(Mod:X)).
1377
1378
1379
1380%% @}
1381
1382
1383/**
1384@}
1385*/
1386
callable( ?_Goal_ )
catch( : Goal,+ Exception,+ Action)
must_be_callable( ?_Goal_ )
throw(+ Ball)
thread_at_exit(: Term)
thread_create(: Goal)
Definition: threads.yap:116
thread_create(: Goal, - Id)
thread_create(: Goal, - Id, + Options)
thread_exit(+ Term)
thread_join(+ Id, - Status)
thread_self(- Id)
format(+ T, :L)
erase(+ R)
recordz(+ K, T,- R)
current_thread(+ Id, - Status)
thread_property(? Id, ? Property)
thread_statistics(+ Id, + Key, - Value)
thread_sleep(+ Time)
message_queue_create(? Queue)
message_queue_destroy(+ Queue)
thread_get_message(? Term)
thread_get_message(+ Queue, ? Term)
thread_peek_message(? Term)
thread_peek_message(+ Queue, ? Term)
thread_send_message(+ Term)
thread_send_message(+ QueueOrThreadId, + Term)
current_mutex(? MutexId, ? ThreadId, ? Count)
mutex_create(? MutexId)
initialization(+ G)
once( 0:G)
dynamic( + P )
ground( T)
atom( T)
float( T)
functor( T, F, N)
integer( T)
nonvar( T)
var( T)
between(+ Low:int, + High:int, ? Value:int)