Scieneer Common Lisp 1.3.9 online documentation

Threads

Introduction

Threads are built upon the underlying POSIX threads library of the operating system. This allows the threads to be scheduled by the host operating system and to exploit Symmetric Multi-Processing. Further, since the lisp threads are built on the same libraries as foreign code, both can cooperate together.

Threads

thread:thread[Class]

Class precedence list:

thread:thread, structure-object, ext:instance, t.

Standard structure for the description of a thread. Slots include: thread:thread-name, and thread:thread-plist. Threads should be created using thread:thread-create.

thread:*thread*[Dynamic Variable]

The current thread.

thread:thread-name structure[Function]

Return the thread name.

thread:thread-plist structure[Function]

Return the thread property list.

thread:thread-create function &key name background-streams-p[Function]

Create a new thread, returning the thread object. When the new thread starts it calls the given function, and upon return will exit. A thread :name may be provided which defaults to "Anonymous".

When :background-streams-p is true, and when attached to a capable interface, the thread will bind the standard streams to background streams. Background streams are only opened when there is input or output, and will typically be opened in a fresh interface window avoiding clashes will other threads. The default value for the :background-streams-p argument is the value of thread::*use-background-streams*.

Each thread requires various local memory regions. These are currently allocated upon thread startup, obtaining the size from global variables: vm::*alien-stack-size*, lisp::*binding-stack-size*, vm::*control-stack-size*, vm::*foreign-stack-size*, lisp::*symbol-dynamic-values-size*. Future developments will likely allow the stack sizes to be passed to thread-create so that threads with varying region sizes can be safely created.

thread::*use-background-streams*[Global Variable]

The default :background-streams-p argument to thread:thread-create.

thread:thread-exit [Function]

Exit the current thread by throwing to the end of the thread allowing the thread to unwind gracefully.

thread:thread-interrupt thread interrupt[Function]

Interrupt thread and cause it to evaluate the interrupt function. The interrupt will be deferred if interrupts are disabled, see sys:without-interrupts. The use of thread:thread-interrupt is not recommended in deployed code, but it may be handy in a development environment.

A thread can not be interrupted while it is executing foreign code and the interrupt will remain pending until the thread returns to lisp code and interrupts are enabled. Since stream operations typically call out to foreign code, a thread blocked on a stream operation is typically not interruptible. Rather than using a thread interrupt to attempt to abort lengthy stream operations, it is recommended that a stream timeout should be used which will generate a synchronous event in the running thread, for example see sys:make-fd-stream which accepts a :timeout argument.

Threads do not safely handle a throw from within interrupts. Code in general is not hardened against such events, and unwind-protect cleanup forms are interruptible so can be aborted. When necessary, it is recommended that threads poll for exit events, such as an exit flag, and arrange to exit gracefully themselves.

sys:without-interrupts &body body[Macro]

Execute body with interrupts disabled. Any interrupts sent from other lisp threads via thread:thread-interrupt will be deferred while executing the body and are handled at the exit of the body if interrupts become enabled. The sys:without-interrupts contexts may be nested, in which case pending interrupts will not be handled unit exit from the outermost context when interrupts become enabled.

The thread will not relinquish its lock on the lisp heap while executing the body, even while executing foreign code. This blocks other threads wanting exclusive access to the lisp heap. Further the thread may not seek exclusive access to the lisp heap with interrupts disabled, see thread:with-exclusive-execution. Garbage collection is inhibited while any threads are running with interrupts disabled because a thread requires exclusive access to the lisp heap for garbage collection.

With interrupts disabled, the thread is only permitted to acquire locks which are only even held with interrupts disabled. If a thread attempted to acquire a lock held by another thread with interrupts enabled it could deadlock because the other thread may have been suspended by threads seeking exclusive access to the lisp heap and the lock would never be released and free. The system enforces checks on whether interrups are enabled or disabled when acquiring locks to prevent such deadlocks.

Since only a subset of the standard lisp functions may be safe for use with interrupts disabled, sys:without-interrupts it is not recommended for use by user code.

Note that the behavior of without-interrupts in the threaded environment of Scieneer Common Lisp is significantly different from many other lisp implementations. Many threads may be running in parallel in SCL irrespective of whether they have interrupts enabled, whereas in many other implementations disabling interrupts will disable scheduling and give a thread exclusive execution rights. See thread:with-exclusive-execution for acquiring exclusive execution rights under SCL.

thread:with-exclusive-execution &body body[Macro]

Execute the body while being the only thread executing lisp code, or more precisely being the only thread holding the lisp heap lock. Exclusive contexts may be nested.

Interrupts must be enabled on entry to the exclusive context, unless entering a nested exclusive context, and interrupts are disabled within the exclusive context. If threads attempted to gain exclusive access to the heap with interrupts disabled then competing threads would deadlock. These restrictions are enforced and an error is generated if violated to avoid a deadlock.

The environment in which the body executes is susceptible to deadlocks. A deadlock would occur if an attempt were made to acquire a lock held by another thread blocked from executing lisp code and thus unable to release the lock. The system enforces restrictions on the context in which locks may be held in order to catch such deadlocks, and only locks that are held with interrupts disabled may be held within an exclusive execution context. Since only a subset of the standard lisp functions may be safe for use in this context it is not recommended for use by user code.

thread:destroy-thread thread[Function]

Destroy thread by sending it an interrupt which throws to the end of the thread giving it a chance to unwind gracefully.

Warning: The thread is not guaranteed to exit cleanly. For restrictions, see thread:thread-interrupt. When necessary, it is recommended that threads poll for exit events, such as an exit flag, and arrange to exit gracefully themselves.

thread:show-threads [Function]

Show a list of the active Lisp threads.

thread:thread-run-time [Function]

Return the thread run time in seconds.

thread:thread-real-time [Function]

Return the accrued real time in seconds elapsed since the thread started. The returned time is a double-float in seconds.

thread:map-over-threads function[Function]

Map function over all the threads. The function is called with the thread list lock held for reading which blocks threads from exiting or new threads from begin created and makes it safer accessing the thread dynamic values. The function is also called with interrupts disabled to prevent deadlocking the thread list lock. Returns nil.


Thread resources

Each thread requires various stacks and a vector for symbol dynamic values. The stack sizes can be controlled by the following global variables, and each can be setup by a command line argument.

vm::*alien-stack-size*[Global Variable]

Size in words of the alien stack allocated for a thread. The alien stack is used by lisp code to hold stack allocated alien objects. Except on the x86 port, the alien stack is shared with the foreign stack. The size may be initialised upon startup by the -alien-stack-size command line argument.

lisp::*binding-stack-size*[Global Variable]

Size of the variable binding stack allocated for a thread. The size is in units of bindings, each binding requiring two words. The size may be initialised upon startup by the -binding-stack-size command line argument.

vm::*control-stack-size*[Global Variable]

Size in words of the lisp contol stack allocated for a thread. The size may be initialised upon startup by the -control-stack-size command line argument.

vm::*foreign-stack-size*[Global Variable]

Size in words of the foreign stack allocated for a thread. The foreign stack is used by foreign code, and for the stack allocation of untagged lisp objects such as numbers. On the x86 port the foreign stack is shared with the control stack. The size may be initialised upon startup by the -foreign-stack-size command line argument.

lisp::*symbol-dynamic-values-size*[Global Variable]

Size in cells of the data area allocated for local symbol dynamic values for a thread. One cell is required for each special symbol that is ever bound. The size may be initialised upon startup by the -symbol-dynamic-values-size command line argument.