Bordeaux Threads APIv2
From APIv1 to v2
Sometime last year I took a look at the bug reports and feature
requests that had accumulated on Bordeaux-Threads and I decided it was
time to bring the code back into shape. The code had insufficient test
coverage, which had allowed bugs to make their way into releases, with
some egregious examples like with-lock-held
having incongruent
signatures on some implementations.
That wasn’t the only case though: join-thread
was inconsistently
implemented, in some cases not returning the thread function’s return
values at all, or returning only the first value. Thread termination
due to a non-local exit was sometimes ignored, and other times
signaled by implementation-specific conditions.
Fixing all the bugs would sometimes require changing the API in ways
that are backwards-incompatible so I decided to develop the new API
afresh, in a separate package. This allows migrating to the new API in
a more controlled manner: for convenience, both APIs are loaded at the
same time so .asd
files can stay the same, one only needs to switch
package imports or symbol qualifiers from package bordeaux-threads
(or bt
) to bordeaux-threads-2
(nicknamed bt2
).
The new API is mostly compatible with the old one, so most users won’t notice the difference but it’s still important to test the migration. I hope I can stabilise the it by the end of the year, and I welcome suggestions and feature requests (you can see the current list on Github).
The most recent release, 0.9.1 should be available in the next Quicklisp dist, and it contains the new API. It also comes with a brand new reference manual.
Changes and new features
Support for non-SMP threading models is no longer actively supported. This affects mostly the Allegro and Lispworks implementations.
In general, I tried to tighten the semantics of the API, and ensure that all functions return useful values.
Threads
The type bt2:thread
is now a wrapper around the native thread
object. Its public accessors are bt2:thread-name
and
bt2:thread-native-thread
.
bt2:make-thread
gains a new keyword argument named
trap-conditions
. If true, conditions terminating a thread will be
captured instead of reaching the global debugger.
bt2:join-thread
now returns all values returned by the
thread function. If a thread function was terminated by a condition,
bt2:join-thread
will signal a condition of type
bt2:abnormal-exit
. The termination condition can be read using
bt2:abnormal-exit-condition
.
The new functions bt2:signal-in-thread
, bt2:warn-in-thread
and
bt2:error-in-thread
will respectively call cl:signal
, cl:warn
and cl:error
within the dynamic context of the target thread.
Locks
The types bt2:lock
and bt2:recursive-lock
are now wrappers around
the native lock, which can be read using bt2:lock-native-lock
.
The native lock types are now exported as bt2:native-lock
and
bt2:native-recursive-lock
, with the respecitve predicates
bt2:native-lock-p
and bt2:native-recursive-lock-p
.
Condition variables
On implementations that don’t provide a native implementation, there’s a new implementation based on semaphores.
Semaphores
On implementations that don’t provide a native semaphore, there’s a simple implementation using condition variables.
Atomics
APIv1 does not have any support for atomic operations, and since they
are somewhat inconsistently
supported
across implementations, I decided to add a higher-level construct that
covers many of the uses of atomic operations:
bt2:atomic-integer
.
The implementation wraps an unsigned machine word (32 or 64 bits depending on the CPU architecture). You can find out more in its documentation.