1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009 1010 1011 1012 1013 1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026
|
\chapter{Multithreaded applications} \label{sec:threads}
SWI-Prolog multithreading is based on standard C language multithreading
support. It is not like {\em ParLog} or other parallel implementations of
the Prolog language. Prolog threads have their own stacks and only share
the Prolog \emph{heap}: predicates, records, flags and other global
non-backtrackable data. SWI-Prolog thread support is designed with the
following goals in mind.
\begin{itemlist}
\item[Multithreaded server applications]
Today's computing services often focus on (internet) server applications.
Such applications often have need for communication between services
and/or fast non-blocking service to multiple concurrent clients. The
shared heap provides fast communication, and thread creation is
relatively cheap.%
\footnote{On an Intel i7-2600K, running Ubuntu Linux 12.04,
SWI-Prolog 6.2 creates and joins 32,000 threads per
second elapsed time.}
\item[Interactive applications]
Interactive applications often need to perform extensive computation.
If such computations are executed in a new thread, the main thread can
process events and allow the user to cancel the ongoing computation.
User interfaces can also use multiple threads, each thread dealing with
input from a distinct group of windows. See also \secref{mt-xpce}.
\item[Natural integration with foreign code]
Each Prolog thread runs in a native thread of the operating system,
automatically making them cooperate with \jargon{MT-safe} foreign code.
In addition, any foreign thread can create its own Prolog engine for
dealing with calling Prolog from C code.
\end{itemlist}
SWI-Prolog multithreading is based on the POSIX thread standard
\cite{Butenhof:1997:PPT} used on most popular systems except for
MS-Windows. On Windows it uses the
\url[pthread-win32]{http://sources.redhat.com/pthreads-win32/} emulation
of POSIX threads mixed with the Windows native API for smoother and
faster operation.
\section{Creating and destroying Prolog threads}
\label{sec:threadcreate}
\begin{description}
\predicate{thread_create}{3}{:Goal, -Id, +Options}
Create a new Prolog thread (and underlying C thread) and start it
by executing \arg{Goal}. If the thread is created successfully, the
thread identifier of the created thread is unified to \arg{Id}.
\arg{Options} is a list of options. The currently defined options are
below. Stack size options can also take the value \const{inf} or
\const{infinite}, which is mapped to the maximum stack size supported
by the platform.
\begin{description}
\termitem{alias}{AliasName}
Associate an `alias name' with the thread. This name may be used to
refer to the thread and remains valid until the thread is joined
(see thread_join/2).
\termitem{at_exit}{:AtExit}
Register \arg{AtExit} as using thread_at_exit/1 before entering the
thread goal. Unlike calling thread_at_exit/1 as part of the normal
\arg{Goal}, this \emph{ensures} the \arg{Goal} is called. Using
thread_at_exit/1, the thread may be signalled or run out of resources
before thread_at_exit/1 is reached.
\termitem{detached}{Bool}
If \const{false} (default), the thread can be waited for using
thread_join/2. thread_join/2 must be called on this thread to reclaim
all resources associated with the thread. If \const{true}, the system
will reclaim all associated resources automatically after the thread
finishes. Please note that thread identifiers are freed for reuse after
a detached thread finishes or a normal thread has been joined.
See also thread_join/2 and thread_detach/1.
If a detached thread dies due to failure or exception of the initial
goal, the thread prints a message using print_message/2. If such
termination is considered normal, the code must be wrapped using
ignore/1 and/or catch/3 to ensure successful completion.
\termitem{inherit_from}{+ThreadId}
Inherit defaults from the given \arg{ThreadId} instead of the calling
thread. This option was added to ensure that the
\const{__thread_pool_manager} (see thread_create_in_pool/4), which is
created lazily, has a predictable state. The following properties are
inherited:
\begin{itemize}
\item The prompt (see prompt/2)
\item The \jargon{typein} module (see module/1)
\item The standard streams (\const{user_input}, etc.)
\item The default encoding (see \prologflag{encoding})
\item The default locale (see setlocale/1)
\item All prolog flags
\item The limits of Prolog stacks (see set_prolog_stack/2)
\end{itemize}
\termitem{global}{K-Bytes}
Set the limit to which the global stack of this thread may grow. If
omitted, the limit of the calling thread is used. See also the
\cmdlineoption{-G} command line option.
\termitem{local}{K-Bytes}
Set the limit to which the local stack of this thread may grow. If
omitted, the limit of the calling thread is used. See also the
\cmdlineoption{-L} command line option.
\termitem{c_stack}{K-Bytes}
Set the limit to which the system stack of this thread may grow. The
default, minimum and maximum values are system-dependent.\footnote{Older
versions used \const{stack}. This is still accepted as a synonym.}.
\termitem{trail}{K-Bytes}
Set the limit to which the trail stack of this thread may grow. If
omitted, the limit of the calling thread is used. See also the
\cmdlineoption{-T} command line option.
\end{description}
The \arg{Goal} argument is \emph{copied} to the new Prolog engine.
This implies that further instantiation of this term in either thread does
not have consequences for the other thread: Prolog threads do not share
data from their stacks.
\predicate{thread_self}{1}{-Id}
Get the Prolog thread identifier of the running thread. If the thread
has an alias, the alias name is returned.
\predicate{thread_join}{2}{+Id, -Status}
Wait for the termination of the thread with the given \arg{Id}. Then unify
the result status of the thread with \arg{Status}.
After this call, \arg{Id} becomes invalid and all resources associated
with the thread are reclaimed. Note that threads with the attribute
\term{detached}{true} cannot be joined. See also thread_property/2.
A thread that has been completed without thread_join/2 being called on
it is partly reclaimed: the Prolog stacks are released and the C thread
is destroyed. A small data structure representing the exit status of the
thread is retained until thread_join/2 is called on the thread. Defined
values for \arg{Status} are:
\begin{description}
\termitem{true}{}
The goal has been proven successfully.
\termitem{false}{}
The goal has failed.
\termitem{exception}{Term}
The thread is terminated on an exception. See print_message/2 to turn
system exceptions into readable messages.
\termitem{exited}{Term}
The thread is terminated on thread_exit/1 using the argument \arg{Term}.
\end{description}
\predicate{thread_detach}{1}{+Id}
Switch thread into detached state (see \term{detached}{Bool} option at
thread_create/3) at runtime. \arg{Id} is the identifier of the thread
placed in detached state. This may be the result of thread_self/1.
One of the possible applications is to simplify debugging. Threads that
are created as \jargon{detached} leave no traces if they crash. For
non-detached threads the status can be inspected using
thread_property/2. Threads nobody is waiting for may be created normally
and detach themselves just before completion. This way they leave no
traces on normal completion and their reason for failure can be
inspected.
\predicate[deprecated]{thread_exit}{1}{+Term}
Terminates the thread immediately, leaving \term{exited}{Term} as
result state for thread_join/2. If the thread has the attribute
\term{detached}{true} it terminates, but its exit status cannot be
retrieved using thread_join/2, making the value of \arg{Term} irrelevant.
The Prolog stacks and C thread are reclaimed.
The current implementation does not guarantee proper releasing of all
mutexes and proper cleanup in setup_call_cleanup/3, etc. Please use the
exception mechanism (throw/1) to abort execution using non-standard
control.
\predicate{thread_initialization}{1}{:Goal}
Run \arg{Goal} when thread is started. This predicate is similar to
initialization/1, but is intended for initialization operations of
the runtime stacks, such as setting global variables as described in
\secref{gvar}. \arg{Goal} is run on four occasions: at the call to
this predicate, after loading a saved state, on starting a new
thread and on creating a Prolog engine through the C interface. On
loading a saved state, \arg{Goal} is executed \emph{after} running the
initialization/1 hooks.
\predicate{thread_at_exit}{1}{:Goal}
Run \arg{Goal} just before releasing the thread resources. This is to be
compared to at_halt/1, but only for the current thread. These hooks are
run regardless of why the execution of the thread has been completed.
When these hooks are run, the return code is already available through
thread_property/2 using the result of thread_self/1 as
thread identifier. Note that there are two scenarios for using exit
hooks. Using thread_at_exit/1 is typically used if the thread creates a
side-effect that must be reverted if the thread dies. Another scenario
is where the creator of the thread wants to be informed when the thread
ends. That cannot be guaranteed by means of thread_at_exit/1 because it
is possible that the thread cannot be created or dies almost instantly
due to a signal or resource error. The \term{at_exit}{Goal} option of
thread_create/3 is designed to deal with this scenario.
\predicate{thread_setconcurrency}{2}{-Old, +New}
\index{Solaris}%
Determine the concurrency of the process, which is defined as the
maximum number of concurrently active threads. `Active' here means they
are using CPU time. This option is provided if the thread implementation
provides pthread_setconcurrency(). Solaris is a typical example of this
family. On other systems this predicate unifies \arg{Old} to 0 (zero)
and succeeds silently.
\end{description}
\section{Monitoring threads} \label{sec:thmonitor}
Normal multithreaded applications should not need the predicates
from this section because almost any usage of these predicates is
unsafe. For example checking the existence of a thread before signalling
it is of no use as it may vanish between the two calls. Catching
exceptions using catch/3 is the only safe way to deal with
thread-existence errors.
These predicates are provided for diagnosis and monitoring tasks. See
also \secref{thutil}, describing more high-level primitives.
\begin{description}
\predicate{thread_property}{2}{?Id, ?Property}
True if thread \arg{Id} has \arg{Property}. Either or both arguments
may be unbound, enumerating all relations on backtracking.
Calling thread_property/2 does not influence any thread. See also
thread_join/2. For threads that have an alias name, this name is
returned in \arg{Id} instead of the opaque thread identifier.
Defined properties are:
\begin{description}
\termitem{alias}{Alias}
\arg{Alias} is the alias name of thread \arg{Id}.
\termitem{detached}{Boolean}
Current detached status of the thread.
\termitem{status}{Status}
Current status of the thread. \arg{Status} is one of:
\begin{description}
\termitem{running}{}
The thread is running. This is the initial status of a thread. Please
note that threads waiting for something are considered running too.
\termitem{false}{}
The \arg{Goal} of the thread has been completed and failed.
\termitem{true}{}
The \arg{Goal} of the thread has been completed and succeeded.
\termitem{exited}{Term}
The \arg{Goal} of the thread has been terminated using thread_exit/1
with \arg{Term} as argument. If the underlying native thread has
exited (using pthread_exit()) \arg{Term} is unbound.
\termitem{exception}{Term}
The \arg{Goal} of the thread has been terminated due to an uncaught
exception (see throw/1 and catch/3).
\end{description}
\end{description}
See also thread_statistics/3 to obtain resource usage information and
message_queue_property/2 to get the number of queued messages for a
thread.
\predicate{thread_statistics}{3}{+Id, +Key, -Value}
Obtains statistical information on thread \arg{Id} as statistics/2
does in single-threaded applications. This call supports all keys
of statistics/2, although only stack sizes and CPU time yield
different values for each thread.%
\footnote{There is no portable interface to obtain
thread-specific CPU time and some operating systems
provide no access to this information at all. On
such systems the total process CPU is returned. Thread
CPU time is supported on MS-Windows, Linux and
MacOSX.}
\predicate{mutex_statistics}{0}{}
Print usage statistics on internal mutexes and mutexes associated with
dynamic predicates. For each mutex two numbers are printed: the number
of times the mutex was acquired and the number of \jargon{collisions}:
the number of times the calling thread has to wait for the mutex.
Generally collision count is close to zero on single-CPU hardware.
\end{description}
\section{Thread communication} \label{sec:threadcom}
\subsection{Message queues} \label{sec:msgqueue}
Prolog threads can exchange data using dynamic predicates, database
records, and other globally shared data. These provide no suitable means
to wait for data or a condition as they can only be checked in an
expensive polling loop. \jargon{Message queues} provide a means for
threads to wait for data or conditions without using the CPU.
Each thread has a message queue attached to it that is identified
by the thread. Additional queues are created using
message_queue_create/1.
\begin{description}
\predicate{thread_send_message}{2}{+QueueOrThreadId, +Term}
Place \arg{Term} in the given queue or default queue of the indicated
thread (which can even be the message queue of itself, see
thread_self/1). Any term can be placed in a message queue, but note that
the term is copied to the receiving thread and variable bindings are
thus lost. This call returns immediately.
If more than one thread is waiting for messages on the given queue and
at least one of these is waiting with a partially instantiated
\arg{Term}, the waiting threads are \emph{all} sent a wake-up signal,
starting a rush for the available messages in the queue. This behaviour
can seriously harm performance with many threads waiting on the same
queue as all-but-the-winner perform a useless scan of the queue. If
there is only one waiting thread or all waiting threads wait with an
unbound variable, an arbitrary thread is restarted to scan the queue.%
\footnote{See the documentation for the POSIX thread functions
pthread_cond_signal() v.s.\ pthread_cond_broadcast()
for background information.}
\predicate{thread_get_message}{1}{?Term}
Examines the thread message queue and if necessary blocks execution
until a term that unifies to \arg{Term} arrives in the queue. After
a term from the queue has been unified to \arg{Term}, the
term is deleted from the queue.
Please note that non-unifying messages remain in the queue. After
the following has been executed, thread 1 has the term \term{b}{gnu}
in its queue and continues execution using \arg{A}~=~\const{gnat}.
\begin{code}
<thread 1>
thread_get_message(a(A)),
<thread 2>
thread_send_message(Thread_1, b(gnu)),
thread_send_message(Thread_1, a(gnat)),
\end{code}
See also thread_peek_message/1.
\predicate{thread_peek_message}{1}{?Term}
Examines the thread message queue and compares the queued terms
with \arg{Term} until one unifies or the end of the queue has been
reached. In the first case the call succeeds, possibly instantiating
\arg{Term}. If no term from the queue unifies, this call fails. I.e.,
thread_peek_message/1 never waits and does not remove any term from the
queue. See also thread_get_message/3.
\predicate{message_queue_create}{1}{?Queue}
If \arg{Queue} is an atom, create a named queue. To avoid ambiguity
of thread_send_message/2, the name of a queue may not be in use as a
thread name. If \arg{Queue} is unbound an anonymous queue is created
and \arg{Queue} is unified to its identifier.
\predicate{message_queue_create}{2}{-Queue, +Options}
Create a message queue from \arg{Options}. Defined options are:
\begin{description}
\termitem{alias}{+Alias}
Same as \term{message_queue_create}{Alias}, but according to the
ISO draft on Prolog threads.
\termitem{max_size}{+Size}
Maximum number of terms in the queue. If this number is reached,
thread_send_message/2 will suspend until the queue is drained.
The option can be used if the source, sending messages to the
queue, is faster than the drain, consuming the messages.
\end{description}
\predicate[det]{message_queue_destroy}{1}{+Queue}
Destroy a message queue created with message_queue_create/1. A
permission error is raised if \arg{Queue} refers to (the default queue
of) a thread. Other threads that are waiting for \arg{Queue} using
thread_get_message/2 receive an existence error.
\predicate[det]{thread_get_message}{2}{+Queue, ?Term}
As thread_get_message/1, operating on a given queue. It is allowed (but
not advised) to get messages from the queue of other threads. This
predicate raises an existence error exception if \arg{Queue} doesn't
exist or is destroyed using message_queue_destroy/1 while this predicate
is waiting.
\predicate[semidet]{thread_get_message}{3}{+Queue, ?Term, +Options}
As thread_get_message/2, but providing additional \arg{Options}:
\begin{description}
\termitem{deadline}{+AbsTime}
The call fails (silently) if no message has arrived before
\arg{AbsTime}. See get_time/1 for the representation of absolute time.
If \arg{AbsTime} is earlier then the current time, thread_get_message/3
fails immediately. Both resolution and maximum wait time is
platform-dependent.\footnote{The implementation uses
MsgWaitForMultipleObjects() on MS-Windows and pthread_cond_timedwait()
on other systems.}
\termitem{timeout}{+Time}
\arg{Time} is a float or integer and specifies the maximum time to wait
in seconds. This is a relative-time version of the \const{deadline}
option. If both options are provided, the earliest time is effective.
It \arg{Time} is 0 or 0.0, thread_get_message/3 examines the queue but
does not suspend if no matching term is available. Note that unlike
thread_peek_message/2, a matching term is removed from the queue.
It \arg{Time} $< 0$, thread_get_message/3 fails immediately.
\end{description}
\predicate[semidet]{thread_peek_message}{2}{+Queue, ?Term}
As thread_peek_message/1, operating on a given queue. It is allowed
to peek into another thread's message queue, an operation that can be
used to check whether a thread has swallowed a message sent to it.
\predicate{message_queue_property}{2}{?Queue, ?Property}
True if \arg{Property} is a property of \arg{Queue}. Defined properties
are:
\begin{description}
\termitem{alias}{Alias}
Queue has the given alias name.
\termitem{max_size}{Size}
Maximum number of terms that can be in the queue. See
message_queue_create/2. This property is not present if there is no
limit (default).
\termitem{size}{Size}
Queue currently contains \arg{Size} terms. Note that due to concurrent
access the returned value may be outdated before it is returned. It can
be used for debugging purposes as well as work distribution purposes.
\end{description}
The \term{size}{Size} property is always present and may be used to
enumerate the created message queues. Note that this predicate does
\emph{not enumerate} threads, but can be used to query the properties
of the default queue of a thread.
\end{description}
Explicit message queues are designed with the \jargon{worker-pool} model
in mind, where multiple threads wait on a single queue and pick up the
first goal to execute. Below is a simple implementation where the
workers execute arbitrary Prolog goals. Note that this example provides
no means to tell when all work is done. This must be realised using
additional synchronisation.
\begin{code}
%% create_workers(?Id, +N)
%
% Create a pool with Id and number of workers.
% After the pool is created, post_job/1 can be used to
% send jobs to the pool.
create_workers(Id, N) :-
message_queue_create(Id),
forall(between(1, N, _),
thread_create(do_work(Id), _, [])).
do_work(Id) :-
repeat,
thread_get_message(Id, Goal),
( catch(Goal, E, print_message(error, E))
-> true
; print_message(error, goal_failed(Goal, worker(Id)))
),
fail.
%% post_job(+Id, +Goal)
%
% Post a job to be executed by one of the pool's workers.
post_job(Id, Goal) :-
thread_send_message(Id, Goal).
\end{code}
\subsection{Signalling threads}
\label{sec:thread-signal}
These predicates provide a mechanism to make another thread execute some
goal as an \jargon{interrupt}. Signalling threads is safe as these
interrupts are only checked at safe points in the virtual machine.
Nevertheless, signalling in multithreaded environments should be
handled with care as the receiving thread may hold a \jargon{mutex}
(see with_mutex/2). Signalling probably only makes sense to start
debugging threads and to cancel no-longer-needed threads with throw/1,
where the receiving thread should be designed carefully to handle
exceptions at any point.
\begin{description}
\predicate{thread_signal}{2}{+ThreadId, :Goal}
Make thread \arg{ThreadId} execute \arg{Goal} at the first
opportunity. In the current implementation, this implies at the first
pass through the \jargon{Call port}. The predicate thread_signal/2
itself places \arg{Goal} into the signalled thread's signal queue
and returns immediately.
Signals (interrupts) do not cooperate well with the world of
multithreading, mainly because the status of mutexes cannot be
guaranteed easily. At the call port, the Prolog virtual machine
holds no locks and therefore the asynchronous execution is safe.
\arg{Goal} can be any valid Prolog goal, including throw/1 to make
the receiving thread generate an exception, and trace/0 to start
tracing the receiving thread.
In the Windows version, the receiving thread immediately executes
the signal if it reaches a Windows GetMessage() call, which generally
happens if the thread is waiting for (user) input.
\end{description}
\subsection{Threads and dynamic predicates} \label{sec:threadlocal}
Besides queues (\secref{msgqueue}) threads can share and exchange
data using dynamic predicates. The multithreaded version knows about
two types of dynamic predicates. By default, a predicate declared
\jargon{dynamic} (see dynamic/1) is shared by all threads. Each thread
may assert, retract and run the dynamic predicate. Synchronisation
inside Prolog guarantees the consistency of the predicate. Updates are
\jargon{logical}: visible clauses are not affected by assert/retract
after a query started on the predicate. In many cases primitives from
\secref{threadsync} should be used to ensure that application
invariants on the predicate are maintained.
Besides shared predicates, dynamic predicates can be declared with the
thread_local/1 directive. Such predicates share their attributes, but
the clause list is different in each thread.
\begin{description}
\prefixop{thread_local}{+Functor/+Arity, \ldots}
This directive is related to the dynamic/1 directive. It tells the
system that the predicate may be modified using assert/1, retract/1,
etc., during execution of the program. Unlike normal shared dynamic
data, however, each thread has its own clause list for the predicate.
As a thread starts, this clause list is empty. If there are still
clauses when the thread terminates, these are automatically reclaimed
by the system (see also volatile/1). The thread_local property
implies the properties \jargon{dynamic} and \jargon{volatile}.
Thread-local dynamic predicates are intended for maintaining
thread-specific state or intermediate results of a computation.
It is not recommended to put clauses for a thread-local predicate into
a file, as in the example below, because the clause is only visible from the
thread that loaded the source file. All other threads start with an
empty clause list.
\begin{code}
:- thread_local
foo/1.
foo(gnat).
\end{code}
\textbf{DISCLAIMER} Whether or not this declaration is appropriate in
the sense of the proper mechanism to reach the goal is still debated.
If you have strong feelings in favour or against, please share them
in the SWI-Prolog mailing list.
\end{description}
\section{Thread synchronisation} \label{sec:threadsync}
All internal Prolog operations are thread-safe. This implies that two Prolog
threads can operate on the same dynamic predicate without corrupting the
consistency of the predicate. This section deals with user-level
\jargon{mutexes} (called \jargon{monitors} in ADA or
\jargon{critical sections} by Microsoft). A mutex is a
{\bf MUT}ual {\bf EX}clusive device, which implies that at most one thread
can \jargon{hold} a mutex.
Mutexes are used to realise related updates to the Prolog database.
With `related', we refer to the situation where a `transaction' implies
two or more changes to the Prolog database. For example, we have a
predicate \nopredref{address}{2}, representing the address of a person
and we want to change the address by retracting the old and asserting
the new address. Between these two operations the database is invalid:
this person has either no address or two addresses, depending on the
assert/retract order.
Here is how to realise a correct update:
\begin{code}
:- initialization
mutex_create(addressbook).
change_address(Id, Address) :-
mutex_lock(addressbook),
retractall(address(Id, _)),
asserta(address(Id, Address)),
mutex_unlock(addressbook).
\end{code}
\begin{description}
\predicate{mutex_create}{1}{?MutexId}
Create a mutex. If \arg{MutexId} is an atom, a \jargon{named} mutex is
created. If it is a variable, an anonymous mutex reference is returned.
There is no limit to the number of mutexes that can be created.
\predicate{mutex_create}{2}{-MutexId, +Options}
Create a mutex using options. Defined options are:
\begin{description}
\termitem{alias}{Alias}
Set the alias name. Using \term{mutex_create}{X, [alias(name)]}
is preferred over the equivalent \term{mutex_create}{name}.
\end{description}
\predicate{mutex_destroy}{1}{+MutexId}
Destroy a mutex. After this call, \arg{MutexId} becomes invalid and
further references yield an \except{existence_error} exception.
\predicate{with_mutex}{2}{+MutexId, :Goal}
Execute \arg{Goal} while holding \arg{MutexId}. If \arg{Goal} leaves
choice points, these are destroyed (as in once/1). The mutex is unlocked
regardless of whether \arg{Goal} succeeds, fails or raises an exception.
An exception thrown by \arg{Goal} is re-thrown after the mutex has been
successfully unlocked. See also mutex_create/1 and setup_call_cleanup/3.
Although described in the thread section, this predicate is also
available in the single-threaded version, where it behaves simply as
once/1.
\predicate{mutex_lock}{1}{+MutexId}
Lock the mutex. Prolog mutexes are \jargon{recursive} mutexes: they
can be locked multiple times by the same thread. Only after unlocking
it as many times as it is locked does the mutex become available for
locking by other threads. If another thread has locked the mutex the
calling thread is suspended until the mutex is unlocked.
If \arg{MutexId} is an atom, and there is no current mutex with that
name, the mutex is created automatically using mutex_create/1. This
implies named mutexes need not be declared explicitly.
Please note that locking and unlocking mutexes should be paired
carefully. Especially make sure to unlock mutexes even if the protected
code fails or raises an exception. For most common cases, use
with_mutex/2, which provides a safer way for handling Prolog-level
mutexes. The predicate setup_call_cleanup/3 is another way to guarantee
that the mutex is unlocked while retaining non-determinism.
\predicate{mutex_trylock}{1}{+MutexId}
As mutex_lock/1, but if the mutex is held by another thread, this
predicates fails immediately.
\predicate{mutex_unlock}{1}{+MutexId}
Unlock the mutex. This can only be called if the mutex is held by the
calling thread. If this is not the case, a \except{permission_error}
exception is raised.
\predicate{mutex_unlock_all}{0}{}
Unlock all mutexes held by the current thread. This call is especially
useful to handle thread termination using abort/0 or exceptions. See
also thread_signal/2.
\predicate{mutex_property}{2}{?MutexId, ?Property}
True if \arg{Property} is a property of \arg{MutexId}. Defined properties are:
\begin{description}
\termitem{alias}{Alias}
Mutex has the defined alias name. See mutex_create/2 using the `alias'
option.
\termitem{status}{Status}
Current status of the mutex. One of \const{unlocked} if the mutex is
currently not locked, or \term{locked}{Owner, Count} if mutex is locked
\arg{Count} times by thread \arg{Owner}. Note that unless \arg{Owner}
is the calling thread, the locked status can change at any time. There
is no useful application of this property, except for diagnostic
purposes.%
\bug{As \arg{Owner} and \arg{Count} are fetched separately from
the mutex, the values may be inconsistent.}
\end{description}
\end{description}
\section{Thread support library(threadutil)} \label{sec:thutil}
This library defines a couple of useful predicates for demonstrating and
debugging multithreaded applications. This library is certainly not
complete.
\begin{description}
\predicate{threads}{0}{}
Lists all current threads and their status.
\predicate{join_threads}{0}{}
Join all terminated threads. For normal applications,
dealing with terminated threads must be part of the application logic,
either detaching the thread before termination or making sure it will be
joined. The predicate join_threads/0 is intended for interactive
sessions to reclaim resources from threads that died unexpectedly
during development.
\predicate{interactor}{0}{}
Create a new console and run the Prolog top level in this new console.
See also attach_console/0. In the Windows version a new interactor
can also be created from the {\sf Run/New thread} menu.
\end{description}
\subsection{Debugging threads}
\label{sec:threaddebug}
Support for debugging threads is still very limited. Debug and trace
mode are flags that are local to each thread. Individual threads can be
debugged either using the graphical debugger described in
\secref{guitracer} (see tspy/1 and friends) or by attaching a console to
the thread and running the traditional command line debugger (see
attach_console/0). When using the graphical debugger, the debugger
must be \emph{loaded} from the main thread (for example using guitracer)
before gtrace/0 can be called from a thread.
\begin{description}
\predicate{attach_console}{0}{}
If the current thread has no console attached yet, attach one and
redirect the user streams (input, output, and error) to the new console
window. On Unix systems the console is an \program{xterm} application.
On Windows systems this requires the GUI version \program{swipl-win.exe}
rather than the console-based \program{swipl.exe}.
This predicate has a couple of useful applications. One is to separate
(debugging) I/O of different threads. Another is to start debugging a
thread that is running in the background. If thread 10 is running, the
following sequence starts the tracer on this thread:
\begin{code}
?- thread_signal(10, (attach_console, trace)).
\end{code}
\predicate{tdebug}{1}{+ThreadId}
Prepare \arg{ThreadId} for debugging using the graphical tracer. This
implies installing the tracer hooks in the thread and switching the
thread to debug mode using debug/0. The call is injected into the
thread using thread_signal/2. We refer to the documentation of this
predicate for asynchronous interaction with threads. New threads
created inherit their debug mode from the thread that created them.
\predicate{tdebug}{0}{}
Call tdebug/1 in all running threads.
\predicate{tnodebug}{1}{+ThreadId}
Disable debugging thread \arg{ThreadId}.
\predicate{tnodebug}{0}{}
Disable debugging in all threads.
\predicate{tspy}{2}{:Spec, +ThreadId}
Set a spy point as spy/1 and enable the thread for debugging using
tdebug/1. Note that a spy point is a global flag on a predicate that
is visible from all threads. Spy points are honoured in all threads
that are in debug mode and ignored in threads that are in nodebug
mode.
\predicate{tspy}{1}{:Spec}
Set a spy point as spy/1 and enable debugging in all threads using
tdebug/0. Note that removing spy points can be done using nospy/1.
Disabling spy points in a specific thread is achieved by tnodebug/1.
\end{description}
\subsection{Profiling threads}
\label{sec:tprofile}
In the current implementation, at most one thread can be profiled at
any moment. Any thread can call profile/1 to profile the execution
of some part of its code. The predicate tprofile/1 allows for profiling
the execution of another thread until the user stops collecting profile
data.
\begin{description}
\predicate{tprofile}{1}{+ThreadId}
Start collecting profile data in \arg{ThreadId} and ask the user to hit
<return> to stop the profiler. See \secref{profile} for details on the
execution profiler.
\end{description}
\section{Multithreaded mixed C and Prolog applications}
\label{sec:foreignthread}
All foreign code linked to the multithreading version of SWI-Prolog
should be thread-safe (\jargon{reentrant}) or guarded in Prolog using
with_mutex/2 from simultaneous access from multiple Prolog threads.
If you want to write mixed multithreaded C and Prolog applications
you should first familiarise yourself with writing multithreaded
applications in C (C++).
If you are using SWI-Prolog as an embedded engine in a multithreaded
application you can access the Prolog engine from multiple threads by
creating an \jargon{engine} in each thread from which you call Prolog.
Without creating an engine, a thread can only use functions that do
\emph{not} use the \type{term_t} type (for example PL_new_atom()).
The system supports two models. \Secref{threadoneone} describes the
original one-to-one mapping. In this schema a native thread attaches
a Prolog thread if it needs to call Prolog and detaches it when
finished, as opposed to the model from \secref{threadmanymany}, where
threads temporarily use a Prolog engine.
\subsection{A Prolog thread for each native thread (one-to-one)}
\label{sec:threadoneone}
In the one-to-one model, the thread that called PL_initialise() has a
Prolog engine attached. If another C thread in the system wishes to call
Prolog it must first attach an engine using PL_thread_attach_engine()
and call PL_thread_destroy_engine() after all Prolog work is finished.
This model is especially suitable with long running threads that need
to do Prolog work regularly. See \secref{threadmanymany} for the
alternative many-to-many model.
\begin{description}
\cfunction{int}{PL_thread_self}{}
Returns the integer Prolog identifier of the engine or -1 if the calling
thread has no Prolog engine. This function is also provided in the
single-threaded version of SWI-Prolog, where it returns -2.
\cfunction{int}{PL_unify_thread_id}{term_t t, int i}
Unify \arg{t} with the Prolog thread identifier for thread \arg{i}.
Thread identifiers are normally returned from PL_thread_self(). Returns
-1 if the thread does not exist or the unification fails.
\cfunction{int}{PL_thread_attach_engine}{const PL_thread_attr_t *attr}
Creates a new Prolog engine in the calling thread. If the calling thread
already has an engine the reference count of the engine is incremented.
The \arg{attr} argument can be \const{NULL} to create a thread with
default attributes. Otherwise it is a pointer to a structure with
the definition below. For any field with value `0', the default is
used. The \const{cancel} field may be filled with a pointer to a
function that is called when PL_cleanup() terminates the running
Prolog engines. If this function is not present or returns \const{FALSE}
pthread_cancel() is used. The \const{flags} field defines the following
flags:
\begin{description}
\termitem{PL_THREAD_NO_DEBUG}{}
If this flag is present, the thread starts in normal no-debug status.
By default, the debug status is inherited from the main thread.
\end{description}
\begin{code}
typedef struct
{ unsigned long local_size; /* Stack sizes (Kbytes) */
unsigned long global_size;
unsigned long trail_size;
unsigned long argument_size;
char * alias; /* alias name */
int (*cancel)(int thread);
intptr_t flags;
} PL_thread_attr_t;
\end{code}
The structure may be destroyed after PL_thread_attach_engine() has
returned. On success it returns the Prolog identifier for the thread
(as returned by PL_thread_self()). If an error occurs, -1 is returned.
If this Prolog is not compiled for multithreading, -2 is returned.
\cfunction{int}{PL_thread_destroy_engine}{}
Destroy the Prolog engine in the calling thread. Only takes effect if
PL_thread_destroy_engine() is called as many times as
PL_thread_attach_engine() in this thread. Returns \const{TRUE} on
success and \const{FALSE} if the calling thread has no engine or this
Prolog does not support threads.
Please note that construction and destruction of engines are
relatively expensive operations. Only destroy an engine if performance
is not critical and memory is a critical resource.
\cfunction{int}{PL_thread_at_exit}{void (*function)(void *),
void *closure,
int global}
Register a handle to be called as the Prolog engine is destroyed.
The handler function is called with one \ctype{void *} argument holding
\arg{closure}. If \arg{global} is \const{TRUE}, the handler is installed
\emph{for all threads}. Globally installed handlers are executed after
the thread-local handlers. If the handler is installed local for the
current thread only (\arg{global} == \const{FALSE}) it is stored in the
same FIFO queue as used by thread_at_exit/1.
\end{description}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% MANY-TO-MANY %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\subsection{Pooling Prolog engines (many-to-many)}
\label{sec:threadmanymany}
In this model Prolog engines live as entities that are independent from
threads. If a thread needs to call Prolog it takes one of the engines
from the pool and returns the engine when done. This model is suitable
in the following identified cases:
\begin{itemlist}
\item [Compatibility with the single-threaded version]
In the single-threaded version, foreign threads must serialise access to
the one and only thread engine. Functions from this section allow
sharing one engine among multiple threads.
\item [Many native threads with infrequent Prolog work]
Prolog threads are expensive in terms of memory and time to create and
destroy them. For systems that use a large number of threads that only
infrequently need to call Prolog, it is better to take an engine from a pool
and return it there.
\item [Prolog status must be handed to another thread]
This situation has been identified by Uwe Lesta when creating a .NET
interface for SWI-Prolog. .NET distributes work for an active internet
connection over a pool of threads. If a Prolog engine contains the state
for a connection, it must be possible to detach the engine from a
thread and re-attach it to another thread handling the same connection.
\end{itemlist}
\begin{description}
\cfunction{PL_engine_t}{PL_create_engine}{PL_thread_attr_t *attributes}
Create a new Prolog engine. \arg{attributes} is described with
PL_thread_attach_engine(). Any thread can make this call after
PL_initialise() returns success. The returned engine is not attached to
any thread and lives until PL_destroy_engine() is used on the returned
handle.
In the single-threaded version this call always returns \const{NULL},
indicating failure.
\cfunction{int}{PL_destroy_engine}{PL_engine_t e}
Destroy the given engine. Destroying an engine is only allowed if the
engine is not attached to any thread or attached to the calling thread.
On success this function returns \const{TRUE}, on failure the return
value is \const{FALSE}.
\cfunction{int}{PL_set_engine}{PL_engine_t engine, PL_engine_t *old}
Make the calling thread ready to use \arg{engine}. If \arg{old} is
non-\const{NULL} the current engine associated with the calling thread
is stored at the given location. If \arg{engine} equals
\const{PL_ENGINE_MAIN} the initial engine is attached to the calling
thread. If \arg{engine} is \const{PL_ENGINE_CURRENT} the engine is not
changed. This can be used to query the current engine. This call returns
\const{PL_ENGINE_SET} if the engine was switched successfully,
\const{PL_ENGINE_INVAL} if \arg{engine} is not a valid engine handle and
\const{PL_ENGINE_INUSE} if the engine is currently in use by another
thread.
Engines can be changed at any time. For example, it is allowed to
select an engine to initiate a Prolog goal, detach it and at a later
moment execute the goal from another thread. Note, however, that the
\ctype{term_t}, \ctype{qid_t} and \ctype{fid_t} types are interpreted
relative to the engine for which they are created. Behaviour when
passing one of these types from one engine to another is undefined.
In the single-threaded version this call only succeeds if \arg{engine}
refers to the main engine.
\end{description}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
% XPCE %
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\section{Multithreading and the XPCE graphics system} \label{sec:mt-xpce}
\label{sec:xpcethread}
GUI applications written in XPCE can benefit from Prolog threads if they
need to do expensive computations that would otherwise block the UI. The
XPCE message passing system is guarded with a single \jargon{mutex},
which synchronises both access from Prolog and activation through the
GUI. In MS-Windows, GUI events are processed by the thread that created
the window in which the event occurred, whereas in Unix/X11 they are
processed by the thread that dispatches messages. In practice, the most
feasible approach to graphical Prolog implementations is to control XPCE
from a single thread and deploy other threads for (long) computations.
Traditionally, XPCE runs in the foreground (\const{main}) thread. We are
working towards a situation where XPCE can run comfortably in a separate
thread. A separate XPCE thread can be created using pce_dispatch/1. It
is also possible to create this thread as the \pllib(pce) is loaded by
setting the \prologflag{xpce_threaded} to \const{true}.
Threads other than the thread in which XPCE runs are provided with two
predicates to communicate with XPCE.
\begin{description}
\predicate[det]{in_pce_thread}{1}{:Goal}
Assuming XPCE is running in the foreground thread, this call gives
background threads the opportunity to make calls to the XPCE thread.
A call to in_pce_thread/1 succeeds immediately, copying \arg{Goal}
to the XPCE thread. \arg{Goal} is added to the XPCE event queue
and executed synchronous to normal user events like typing and clicking.
\predicate[semidet]{in_pce_thread_sync}{1}{:Goal}
Same as in_pce_thread/1, but wait for \arg{Goal} to be completed.
Success depends on the success of executing \arg{Goal}. Variable
bindings inside \arg{Goal} are visible to the caller, but it should be
noted that the values are being \emph{copied}. If \arg{Goal} throws an
exception, this exception is re-thrown by in_pce_thread/1. If the
calling thread is the `pce thread', in_pce_thread_sync/1 executes a
direct meta-call. See also pce_thread/1.
Note that in_pce_thread_sync/1 is expensive because it requires copying
and thread communication. For example, \exam{in_pce_thread_sync{true}}
runs at approximately 50,000 calls per second (AMD Phenom 9600B, Ubuntu
11.04).
\predicate{pce_dispatch}{1}{+Options}
Create a Prolog thread with the alias name \const{pce} for XPCE
event handling. In the X11 version this call creates a thread that
executes the X11 event-dispatch loop. In MS-Windows it creates a thread
that executes a windows event-dispatch loop. The XPCE event-handling
thread has the alias \const{pce}. \arg{Options} specifies the
thread attributes as thread_create/3.
\end{description}
|