trivial main thread
2.0.0Compatibility library to run things in the main thread.
About trivial-main-thread
Sometimes it is absolutely necessary to run certain tasks in the main thread of the implementation. This is particularly the case with GUI applications on OS X, where only thread 0 is allowed to issue drawing calls. This library aims to help with that.
Why a Wrapper
Why not just call bt:interrupt-thread
and be done with it? Well, an implementation is not required to provide the user with the main thread, and may instead choose to use it for its own purposes. For example, CCL uses the thread for housekeeping and signal handling. As such, some implementations require workarounds to make this go by smoothly. That's why this library exists.
Basic Usage
Load trivial-main-thread through ASDF or Quicklisp.
(ql:quickload :trivial-main-thread)
Now you can simply issue calls to be sent to the main thread by using call-in-main-thread
or with-body-in-main-thread
.
(call-in-main-thread (lambda () (+ 1 1)))
(with-body-in-main-thread (:blocking T)
(+ 1 1))
Upon first usage of either of these two functions, trivial-main-thread will start a new thread to resume the main thread's functionality in if necessary, and interrupts the main thread with a task runner loop. From then on, call requests can be sent to the thread.
For example, if you wanted to start a Qt application, you could do the following:
(call-in-main-thread #'qt:make-qapplication)
(with-body-in-main-thread ()
(qt:with-main-window (window (make-instance 'my-window))))
And the application will safely run in the main thread.
In the case where the main thread is the evaluating thread, no additional threads are started, and the runner used will simply directly run tasks.
Note that you must not interact with the functions from this library prior to dumping an image, as it will cache the thread object internally when you do so. However, this object will be invalid when the implementation resumes from image. Thus, ensure that you only call these functions in your image resume function.
Supported Implementations
Allegro
CCL
CMUCL
Clasp
ECL
LispWorks
MKCL
SBCL
The following are explicitly unsupported:
ABCL
By default the JVM steals the main thread and does not give it to you. If you want to use ABCL on OS X, you'll have to figure out how to make ABCL launch on thread0.Corman
This implementation does not even give access to a thread listing.
Changes in Version 2
This has been rewritten to remove the dependency on simple-tasks, using a trivial internal queue implementation instead. The API has also been streamlined, removing access to some internal functions that should not have been exposed. Error handling behaviour has also been improved via the *on-error*
variable.
System Information
Definition Index
-
TRIVIAL-MAIN-THREAD
- ORG.SHIRAKUMO.TRIVIAL-MAIN-THREAD
- TMT
No documentation provided.-
EXTERNAL SPECIAL-VARIABLE *ON-ERROR*
Sets what to do if an error is signalled in the main thread runner. The value may be one of the following: :RESIGNAL --- Resignal the condition in the calling thread. If the condition is not caused by a task, the debugger is invoked instead :QUIT --- The implementation exits via UIOP:QUIT NIL :IGNORE --- The error is not handled and the behaviour is implementation-dependent FUNCTION SYMBOL --- The named function is invoked with the error. Note that this makes the following also permissible: STOP-MAIN-RUNNER INVOKE-DEBUGGER CONTINUE ABORT Defaults to :RESIGNAL See START-MAIN-RUNNER See STOP-MAIN-RUNNER See CL:INVOKE-DEBUGGER See CL:CONTINUE See CL:ABORT See UIOP:QUIT
-
EXTERNAL FUNCTION CALL-IN-MAIN-THREAD
- FUNCTION
- &KEY
- BLOCKING
Call FUNCTION in the main thread. If this is called in the main thread, the function is simply invoked. Otherwise, ENSURE-MAIN-RUNNER is called and the function is scheduled for execution. If BLOCKING is non-NIL, the current thread is blocked until the function has finished running and the function's return values are returned. If the function signals an error and *ON-ERROR* is :RESIGNAL, the error is re-signaled in the calling thread. See *ON-ERROR* See MAIN-THREAD-P See ENSURE-MAIN-RUNNER
-
EXTERNAL FUNCTION ENSURE-MAIN-RUNNER
Ensure that the main thread runner is indeed running. See START-MAIN-RUNNER See STOP-MAIN-RUNNER
-
EXTERNAL FUNCTION MAIN-THREAD
Returns the main thread. See MAIN-THREAD-P
-
EXTERNAL FUNCTION MAIN-THREAD-P
- &OPTIONAL
- THREAD
Returns true if the given thread is the main thread. See MAIN-THREAD
-
EXTERNAL FUNCTION START-MAIN-RUNNER
Starts the runner in the main thread. If the runner is already running, an error is signalled. Otherwise, the main thread is hijacked via SWAP-MAIN-THREAD. The runner keeps a queue of tasks (function thunks) to invoke. When it gets woken up to process tasks (via CALL-IN-MAIN-THREAD), it goes through the queue, invokes the functions in sequence, and puts the function's return values back into the queue so the caller can process them. If an error is signalled during task processing, the behaviour described in *ON-ERROR* is executed. See *ON-ERROR* See SWAP-MAIN-THREAD See ENSURE-MAIN-RUNNER
-
EXTERNAL FUNCTION STOP-MAIN-RUNNER
- &OPTIONAL
- RETURN
Stops the runner in the main thread, allowing it to continue. If the runner is not running in the main thread, an error is signalled. If this is called from within the runner, the function causes an unwind. See START-MAIN-RUNNER
-
EXTERNAL FUNCTION SWAP-MAIN-THREAD
- NEW-FUNCTION
Swaps the main thread for our own FUNCTION. If the implementation uses the main thread for vital tasks, this function tries to ensure that these vital tasks are continued in a new thread instead. If this is called from the main thread, the function is simply invoked. See MAIN-THREAD-P
-
EXTERNAL MACRO WITH-BODY-IN-MAIN-THREAD
- &KEY
- BLOCKING
- &BODY
- BODY
Evaluate the BODY in the main thread. See CALL-IN-MAIN-THREAD