mulle-thread is a set of C wrapper headers for a simplified subset of thread functions and for a limited range of atomic operations that strongly mimics the thread standard, even if thread is not available. On Windows it has to do a bit more work.
mulle-threads main advantages are simplicity, portability, sanity.
- basic atomic operations CAS, ++, -- on void pointers.
- basic thread operations
- mutex functionality
- thread local storage, with proper destruction
Since thread has <stdatomic.h> and <threads.h>, eventually this project
could become superflous. In the meantime though it's a convenient abstraction
on threads and atomic operations.
mulle_thread_once doesn't guarantee, that exceptions or thread cancellation
within the init function will clear the once flag for a second run. In fact
it will not.
| Release Version | Release Notes | AI Documentation |
|---|---|---|
| RELEASENOTES | DeepWiki for mulle-thread |
An easy way to get a locked code region is to use:
void foo( mulle_thread_mutex_t *mutex)
{
mulle_thread_mutex_do( *mutex)
{
// code block is now executed with mutex locked
}
// mutex is unlocked again
}The macro takes the address of its argument, so pass the mutex variable itself
(or *mutex if you only hold a pointer to it). break and continue will
exit the block and unlock the mutex. But when you use return the function
exits and the mutex remains locked.
By default mulle-thread uses the C11 <threads.h> backend when it is
available, so mulle_thread_create calls thrd_create. ThreadSanitizer
intercepts pthread_create, but not thrd_create: on glibc thrd_create
creates the thread through a libc-internal call that bypasses interposition.
The new thread therefore starts with no tsan thread state and crashes as soon
as it executes any instrumented code:
ThreadSanitizer:DEADLYSIGNAL
ERROR: ThreadSanitizer: SEGV on unknown address 0x000000000018 ...
The backtrace points at __tsan_func_entry at the top of the thread function,
which makes it look like a bug in your code. It is not. Force the pthreads
backend for sanitizer builds:
cc -fsanitize=thread -DMULLE_THREAD_USE_PTHREADS ...Reproduced with both gcc libtsan and clang, and fixed by the flag in both. Note that a trivial thread function may survive, because with optimization there is nothing left in it to instrument, so a smoke test can be misleading.
MULLE_THREAD_USE_PTHREADS is checked in mulle-thread.h before the backend
header is included, so use a compiler flag rather than a #define in a source
file. Use the same flag for any sanitizer or race detector that needs to
observe thread creation.
mulle-thread is a component of the mulle-core library. So in your code include the mulle-core umbrella header:
#include <mulle-core/mulle-core.h>git submodule add https://github.com/mulle-core/mulle-core.git mulle-coreAdd this to your CMakeLists.txt:
add_subdirectory( mulle-core)
target_link_libraries( ${PROJECT_NAME} PRIVATE mulle-core)mulle-sde add github:mulle-core/mulle-coreclib install --out src mulle-concurrent/mulle-threadAppend src to your include path (e.g. add -isystem src to your CFLAGS)
and compile all the sources that were downloaded.
Use mulle-sde to build and install mulle-thread and all dependencies:
mulle-sde install --prefix /usr/local \
https://github.com/mulle-concurrent/mulle-thread/archive/latest.tar.gzInstall the requirements:
| Requirements | Description |
|---|---|
| mulle-c11 | 🔀 Cross-platform C compiler glue (and some cpp conveniences) |
Download the latest tar or zip archive and unpack it.
Install mulle-thread into /usr/local with cmake:
PREFIX_DIR="/usr/local"
cmake -B build \
-DMULLE_SDK_PATH="${PREFIX_DIR}" \
-DCMAKE_INSTALL_PREFIX="${PREFIX_DIR}" \
-DCMAKE_PREFIX_PATH="${PREFIX_DIR}" \
-DCMAKE_BUILD_TYPE=Release &&
cmake --build build --config Release &&
cmake --install build --config ReleaseNat! for Mulle kybernetiK