Qore Programming Language Reference Manual 0.8.11.1
A thread is an independent sequence of execution of Qore code within a Qore program or script. Each thread has a thread ID or TID.
The first thread of execution in a Qore program has TID 1. TID 0 is always reserved for the special signal handler thread.
The Qore language is designed to be thread-safe and Qore programs should not crash the Qore executable due to threading errors. Threading errors should only cause exceptions to be thrown or application errors to occur.
Threading functionality in Qore is provided by the operating system's POSIX threads library.
New threads are created with the background operator. This operator executes the expression given as an argument in a new thread and returns the TID (integer thread ID) of the new thread to the calling thread. This is most useful for calling user functions or object methods designed to run in a separate thread.
All global variables are shared in Qore programs, while local variables (declared with
my) are generally local to each thread (and thus accessed without any mutual-exclusion locking), regardless of location. This means that if a variable is declared with
my at the top level, it will actually have global scope, but also each thread will have its own copy of the variable. In effect, declaring a top-level local variable with
my actually creates a global thread-local variable.
The following code gives an example of declaring a global thread-local variable by using
my at the top-level:
This will print out:
Note that the second time the local variable is accessed in the background thread, it has no value.
Due to the way Qore's local variables work, it is illegal to declare a top-level local variable after first block is parsed in the program; that is; if any call to parse() or Qore::Program::parse() is made in an existing program (where a top-level block already exists), and an attempt to declare a new top-level local variable is made, then a
ILLEGAL-TOP-LEVEL-LOCAL-VARIABLE parse exception will be raised.
Access to global variables in qore is wrapped in mutual-exclusion locks to guarantee safe access to global variable data in a multithreaded context. Local variables are thread-local and therefore not locked, except when referenced in a closure or when a reference is taken of them, in which case the local variable's scope is extended to that of the closure's or the reference's, and all accesses to the bound local variable are made within mutual-exclusion locks as these variables may be used in multithreaded contexts.
synchronizedkeyword can be used before function or class method definitions in order to guarantee that the function or method call will only be executed in one thread at a time. As in Java, this keyword can also be used safely with recursive functions and methods (internally a recursive mutual exclusion lock that participates in Qore's deadlock detection framework is used to guarantee thread-exclusivity and allow recursion).
|Mutex||A mutual-exclusion thread lock|
|Gate||A recursive thread lock|
|RWLock||A read-write thread lock|
|Condition||Allows Qore programs to block until a certain condition becomes true|
|Counter||A blocking counter class|
|Queue||A thread-safe, blocking queue class (useful for message passing)|
|Sequence||A simple, thread-atomic sequence object (increment-only)|
|ThreadPool||A flexible, dynamically scalable thread pool|
|AutoLock||A helper class to automatically release Mutex locks when the AutoLock object is deleted|
|AutoGate||A helper class to automatically exit Gate locks when the AutoGate object is deleted|
|AutoReadLock||A helper class to automatically release read locks when the AutoReadLock object is deleted|
|AutoWriteLock||A helper class to automatically release read locks when the AutoWriteLock object is deleted|
|save_thread_data()||Saves a thread-local value against a key.|
|get_all_thread_data()||Retrieves the entire thread-local hash.|
|get_thread_data()||Retrieves a thread-local value based on a key.|
|delete_all_thread_data()||Deletes the entire thread-local data hash.|
|delete_thread_data()||Delete the value of a key in the thread-local data hash.|
|gettid()||Gets the thread's TID (thread identifier)|
|thread_list()||Returns a list of TIDs of running threads|
|num_threads()||Returns the number of running threads|
|throwThreadResourceExceptions()||runs thread-resource cleanup routines and throws the associated exceptions|
|mark_thread_resources()||sets a checkpoint for throwing thread resource exceptions|
|throw_thread_resource_exceptions_to_mark()||runs thread-resource cleanup routines and throws the associated exceptions to the last mark and clears the mark|
Qore supports deadlock detection in complex locking scenarios and will throw a
THREAD-DEADLOCK exception rather than allow an operation to be performed that would cause a deadlock. Deadlock detection is implemented for internal locking (global variable and object access), synchronized methods and functions, etc, as well as for all Qore threading classes.
Qore can only detect deadlocks when a lock resource acquired by one thread is required by another who holds a lock that the first thread also needs. Other errors such as forgetting to unlock a global lock and trying to acquire that lock in another thread cannot be differentiated from valid use of threading primitives and will result in a process that never terminates (a deadlocked process). However, common threading errors such as trying to lock the same Mutex twice in the same thread without unlocking it between the two Mutex::lock() calls are caught in Qore and exceptions are thrown. Additionally, locks are tracked as thread resources, so if a thread terminates while holding a lock, an exception will be thrown and the lock will be automatically released.