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 returning only the first value or none at all. 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 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

Non-SMP threading models are 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.