Vocabulary/tcapdot
T. y Set Debug Thread Verb
Rank Infinity -- operates on x and y as a whole -- WHY IS THIS IMPORTANT?
T. y sets the debug thread number to y. The master thread is always thread 0. On entry into debug suspension, the debug thread is set to the number of the thread that caused the suspension.
During debug suspension, sentences are executed in the debug thread. Commands that end suspension, giving a line number to resume at or a value to be used as the value of the debugged sentence, execute in the debug thread but the value they produce is applied in the thread that caused the suspension.
T. y takes effect when its result becomes the result of a sentence entered from the keyboard during debug suspension.
Related Primitives
Execute as Task (t.)
x T. y Threads and Tasks Verb
Rank Infinity -- operates on x and y as a whole -- WHY IS THIS IMPORTANT?
x T. y performs a thread- and task-related function depending on the value of x:
x T. y functions Description x Type/
Precision
of ResultType/
Precision
of yAction Create thread 0 Integer atom [pool#] Create a thread in threadpool pool# (default 0), if possible. Result is the sequential thread number, or _1 if the thread could not be created. The master thread is thread 0 and is not a member of any threadpool.
Even if your code does not create tasks, the system can take advantage of threadpool 0 to make primitives run faster; you should use 8 T. '' to query the number of cores N in your system, and create N-1 threads in threadpool 0.
{{0 T.0}}^:] <: {. 8 T. ''Number of worker threads 1 Integer atom '' The total number of worker threads that have been created. Thread statistics for a threadpool 2 Integer list
#idle,#unfinished,#threads[pool#] Statistics are reported for threadpool pool# (default 0). #idle is the number of threads not working on a user task or an internal task. #unfinished is the number of user tasks that have not completed. #threads is the number of worker threads in the threadpool. Number of executing thread 3 Integer atom '' The number of the currently-executing thread. The master thread is thread 0. Rattle a box to get status 4 Integer array, one for each atom in y A box or array of boxes The threads continue to run between calculation of each atom of this result Status Result atom Pyx waiting to run _ Pyx running in thread n n Pyx completed with error number n -n Pyx completed with no error _1000 Not a pyx _1001 Note: non-negative status codes indicate tasks which are not finished yet.
Create a pyx 5 The created pyx The maximum number of seconds to wait for the result, or 0 for no limit A pyx is created. It is very easy to lock up the system with user-created pyxes. For example, the sentence
5 T. 0
entered from the keyboard will lock up. Pyxes used to hold the results of tasks are not susceptible to lockup.User-created pyxes can be used a semaphores between tasks.
Install a value into a pyx 6 i. 0 0 pyx,<value value becomes the value of pyx, waking up any tasks that were waiting for it. It is illegal to store a value or errorcode into a pyx more than once. Install errorcode into a pyx 7 i. 0 0 pyx,<errorcode pyx becomes a pyx in error, with error code errorcode, waking up any tasks that were waiting for it. It is illegal to store a value or errorcode into a pyx more than once. Read system characteristics relating to threads 8 Integer list
corecount,maxthreads'' corecount is the number of cores in your machine. maxthreads is the maximum number of threads supported, including the master thread. Mutexes 10 mutex shared Create a mutex. shared is 0 for an exclusive mutex, 1 for a shared mutex (also called a recursive mutex). The mutex is opaque; its type, shape, and value are meaningful only as an argument to a mutex function, and they may not be displayed or modified; but the mutex may safely be passed as an argument or put into a box, and it is not considered boxed for the purposes of ;, L., and similar. 11 Integer
errorcodemutex[;timeout] Take an lock on mutex, waiting up to timeout seconds (or forever if timeout is _ or omitted). errorcode is 0 if the lock was taken, 1 if the request timed out. A timeout value of 0 will always return immediately. Requesting a lock on a mutex that the thread already has a lock on is allowed only for a shared mutex and will return immediately still holding the lock.
13 i. 0 0 mutex Release the lock on the mutex. For shared mutexes, the lock is released only after every execution of (11 T. mutex) has been matched by a (13 T. mutex). It is an error for a thread to release a mutex it doesn't have a lock on. Set lingertime for a threadpool 14 Previous value of lingertime pool#,lingertime instruct threads in threadpool pool# to stay active, waiting for jobs, for lingertime seconds after they complete the last task, before they go into a wait state. This is useful if you expect your code to create tasks so fast that a new task would be requested before a thread could come out of a wait state. Wake up all threads in a threadpool 15 i. 0 0 pool# Start all threads in theadpool pool#. If you expect that a task will be dispatched to the threadpool within the threadpool's lingertime (set by 14 T., the threads will be immediately ready for work without the operating system's thread-start delay. Atomically
Modifiable
Values
(AMV)16 AMV initial value Create an Atomically Modifiable Value (AMV) and set its value to initial value. An AMV is a boxed integer atom, but it should be accessed only through AMV primitives (17 T. and 18 T.). An AMV may be assigned to a name 17 value of AMV before addition AMV,<increment Add increment to the value of AMV, and return the value of AMV before the change. The modification is performed atomically, that is, the read, increment, and store cannot be interrupted by other threads. 18 '' if modification performed; value of AMV if modification not performed AMV,desired;expected Perform compare and swap on AMV: if the value of AMV is expected, it is replaced with desired, and the result is 0$0; otherwise the result is the value of AMV. The modification is performed atomically, that is, the read, compare, and conditional store cannot be interrupted by other threads. Destroy thread 55 1 if a thread was destroyed, 0 if not threadpool # or '' The thread to be destroyed is the highest-numbered thread in the given threadpool, or the highest-numbered thread if threadpool is omitted. If the selected thread is the only worker thread in its threadpool and there are tasks waiting in the threadpool, the deletion blocks until that condition clears. The selected thread is then marked for deletion after it finishes any task it is executing.
A shared mutex (sometimes called a recursive mutex) is one which can be locked multiple times on the same thread. It must be unlocked as many times as it has been locked before it will be available to other threads. If an exclusive mutex is locked more than once on the same thread, an error will be thrown.
Exclusive and shared mutexes have similar performance; you should choose whichever one does a better job of enforcing application invariants.
Threadpools
Parallelizable primitives use threadpool 0. For best results, you should create one fewer worker thread in threadpool 0 than you have cores in your CPU. Note that a J thread can use all the resources of a core, making it a bad idea to create J threads for more than one hyperthread per core.
You should use threadpool 0 for your heavy computation also.
If you have threads that are often idle, consider allocating them in a different threadpool. That will avoid the thrashing that would result if you had too many threads in threadpool 0.
Details and Examples
User-created pyxes can be used as semaphores using the following protocol. Note that attempting to display a pyx will wait for its value to be assigned.
Originating task Responding task localpyx=.5 T. 30
function t. '' localpyx;local_stuff'remotepyx remote_stuff'=.y Set up shared data for responding task to use Get ready to receive 'remotepyx remote_stuff'=.>localpyx 6 T. remotepyx,<(localpyx =. 5 T. 30);<local_stuff *** The responding task has control of the shared data *** 6 T. remotepyx,<(localpyx =. 5 T. 30);<local_stuff 'remotepyx remote_stuff'=.>localpyx *** The originating task has control of the shared data *** Control can be passed back and forth indefinitely Mutexes can be used as locks to guarantee exclusive use of a resource. First create a mutex by executing mutex =: 10 T. 0 .
Task 0 Task 1 11 T. mutex NB. acquire mutex use shared resource continue to use shared resource 11 T. mutex NB. try to acquire lock, block because mutex already owned continue to use shared resource still blocked 13 T. mutex NB. release mutex end block and acquire mutex use shared resource 13 T. mutex NB. release mutex Deadlocks
If you create tasks that have enough resources to create their result, they will run to completion without the possibility of deadlock.
If your tasks wait for the results of tasks that started later, it is up to you to ensure that they do not deadlock. A trivial example of deadlock is
pyxes =: {{ y {:: pyxes [ 6!:3 (1)}} t. 'worker'"0 (1 0)
Each thread waits for the other: deadlock.
Oddities
Threads cannot be created on Raspberry Pi distribution.
Related Primitives
Execute as Task (t.)