You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add optional compile-time thread-safety for the forwarding API (#179)
* Add optional compile-time thread-safety for the forwarding API
The `lbt_*` mutation API (`lbt_forward()`, `lbt_set_forward()`,
`lbt_set_forward_by_index()`) is documented as thread-unsafe: it mutates global
forwarding tables with no locking. This adds an opt-in process-global lock,
enabled with `make LBT_THREADSAFE=1`, that serializes those mutators so racing
initializers cannot corrupt the tables.
- New `lbt_lock()`/`lbt_unlock()` helpers: a `pthread_mutex_t` (static
initializer) elsewhere, a `CRITICAL_SECTION` (initialized in `DllMain`) on
Windows. When `LBT_THREADSAFE` is undefined they compile to no-ops, so the
default build is byte-for-byte equivalent in behavior to before.
- The public mutators now wrap unlocked `*_impl` workers; internal callers
(`lbt_forward_impl`, the constructor) call the workers directly to avoid
re-entrant locking.
- `Make.inc` gains the `LBT_THREADSAFE` toggle (default 0), adding `-pthread`
on non-Windows when enabled.
Scope note: only the mutators are locked; read-only accessors
(`lbt_get_config()`, `lbt_get_forward()`) remain unlocked, so callers must
still avoid racing reads against concurrent reconfiguration.
Verified: builds with and without the flag; public symbols still exported and
the `*_impl` workers stay internal; a dlopen smoke test exercises the lock path
without deadlock.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* Exercise the LBT_THREADSAFE build in the test suite
The locking added in this PR was previously never compiled or run by the tests:
`build_libblastrampoline()` always built the default, no-op build.
Make the harness build with the optional internal locking compiled in by
default, so every backend's tests run against a `-DLBT_THREADSAFE` library and
each `lbt_forward()`/`lbt_set_forward()` goes through `lbt_lock()`/`lbt_unlock()`
across the whole existing CI matrix (all OSes/interfaces). The value is read
from the `LBT_THREADSAFE` env var, so `LBT_THREADSAFE=0` still tests the plain
build that ships by default.
Verified locally: the `direct` backend passes (74/74) with the library built
`-DLBT_THREADSAFE -pthread`, both with the env var set and via the new default.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* README: document the LBT_THREADSAFE build option
Mention `make LBT_THREADSAFE=1` in the thread-safety note: it compiles in a
process-global lock around the mutating API (pthread mutex / CRITICAL_SECTION),
is off by default, and guards mutators only (readers and call forwarding stay
lock-free).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* README: make the threading note a concise section
Promote the thread-safety note to a `### Threading` section and trim it to the
essentials: thread-unsafe by default, `make LBT_THREADSAFE=1` adds a lock around
the mutating API, readers/forwarding stay lock-free.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: README.md
+5-2Lines changed: 5 additions & 2 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -53,8 +53,11 @@ This support is only available on the `x86_64` and `i686` architectures, however
53
53
Vendor-specific APIs such as `openblas_get_num_threads()` are not included in header files or exported from the library.
54
54
See the [public header file](src/libblastrampoline.h) for the most up-to-date documentation on the `libblastrampoline` API.
55
55
56
-
**Note**: all `lbt_*` functions should be considered thread-unsafe.
57
-
Do not attempt to load two BLAS libraries on two different threads at the same time.
56
+
### Threading
57
+
58
+
By default, all `lbt_*` functions are thread-unsafe; do not reconfigure forwards from multiple threads at once.
59
+
Building with `make LBT_THREADSAFE=1` adds a process-global lock around the mutating API (`lbt_forward()`, `lbt_set_forward()`, `lbt_set_forward_by_index()`).
60
+
Readers and the BLAS/LAPACK call forwarding itself stay lock-free, so the model remains "configure under the lock, then use".
0 commit comments