thread -- Threading
The thread module provides cross-platform threading primitives: threads, mutexes, condition variables, and read-write locks. It uses POSIX pthreads on Linux/macOS and Win32 threads on Windows (compile with -D__WINDOWS__ to select the Win32 backend).
#include "thread.h"Handle Representation
All handles (thread IDs, mutexes, condition variables, read-write locks) are stored as unsigned long long:
- POSIX:
pthread_tvalue (8 bytes on 64-bit), or a pointer to a heap-allocated pthread object - Win32:
HANDLE(kernel object pointer, 8 bytes on x64), or a pointer to a heap-allocated Windows primitive
Thread
int thread_create(unsigned long long* tid, void* fn, void* arg);
int thread_join(unsigned long long tid);
int thread_detach(unsigned long long tid);
void thread_yield();
void thread_sleep_ms(unsigned long ms);
unsigned long long thread_self();thread_create
Create a new thread. The function signature must be void* fn(void*) on POSIX or DWORD WINAPI fn(LPVOID) on Win32. The thread ID is written to *tid. Returns 0 on success.
thread_join
Block until thread tid finishes. Returns 0 on success.
thread_detach
Detach thread tid so its resources are automatically reclaimed on exit. Returns 0 on success.
thread_yield
Yield the current thread's time slice to other runnable threads.
thread_sleep_ms
Sleep the calling thread for ms milliseconds.
thread_self
Return the calling thread's ID.
Mutex
int mutex_init(unsigned long long* m);
int mutex_destroy(unsigned long long* m);
int mutex_lock(unsigned long long* m);
int mutex_trylock(unsigned long long* m);
int mutex_unlock(unsigned long long* m);mutex_init / mutex_destroy
Initialize or destroy a mutex. The mutex handle is stored in *m.
mutex_lock / mutex_unlock
Acquire or release the mutex. mutex_lock blocks if the mutex is held by another thread.
mutex_trylock
Attempt to acquire the mutex without blocking. Returns 0 if the lock was acquired, non-zero if busy.
Condition Variable
int cond_init(unsigned long long* cv);
int cond_destroy(unsigned long long* cv);
int cond_wait(unsigned long long* cv, unsigned long long* m);
int cond_timedwait_ms(unsigned long long* cv, unsigned long long* m, unsigned long ms);
int cond_signal(unsigned long long* cv);
int cond_broadcast(unsigned long long* cv);cond_wait
Atomically release mutex *m and block until the condition variable is signaled. Re-acquires the mutex before returning.
cond_timedwait_ms
Like cond_wait, but with a timeout in milliseconds. Returns non-zero on timeout.
cond_signal / cond_broadcast
Wake one (signal) or all (broadcast) threads waiting on the condition variable.
Read-Write Lock
int rwlock_init(unsigned long long* rw);
int rwlock_destroy(unsigned long long* rw);
int rwlock_rdlock(unsigned long long* rw);
int rwlock_wrlock(unsigned long long* rw);
int rwlock_tryrdlock(unsigned long long* rw);
int rwlock_trywrlock(unsigned long long* rw);
int rwlock_rdunlock(unsigned long long* rw);
int rwlock_wrunlock(unsigned long long* rw);Multiple simultaneous readers are allowed; writers are exclusive. Use rdlock/rdunlock for shared read access, and wrlock/wrunlock for exclusive write access.
Example
#include "thread.h"
#include "io.h"
#include "atomic.h"
int counter = 0;
unsigned long long mtx;
void* worker(void* arg) {
int i = 0;
while (i < 1000) {
mutex_lock(&mtx);
counter = counter + 1;
mutex_unlock(&mtx);
i = i + 1;
}
return 0;
}
int main() {
mutex_init(&mtx);
unsigned long long t1;
unsigned long long t2;
thread_create(&t1, worker, 0);
thread_create(&t2, worker, 0);
thread_join(t1);
thread_join(t2);
print("counter = ");
println_int(counter); // 2000
mutex_destroy(&mtx);
return 0;
}Producer-Consumer Example
#include "thread.h"
#include "io.h"
int buffer = 0;
int ready = 0;
unsigned long long mtx;
unsigned long long cv;
void* producer(void* arg) {
mutex_lock(&mtx);
buffer = 42;
ready = 1;
cond_signal(&cv);
mutex_unlock(&mtx);
return 0;
}
void* consumer(void* arg) {
mutex_lock(&mtx);
while (ready == 0) {
cond_wait(&cv, &mtx);
}
print("received: ");
println_int(buffer); // 42
mutex_unlock(&mtx);
return 0;
}
int main() {
mutex_init(&mtx);
cond_init(&cv);
unsigned long long t1;
unsigned long long t2;
thread_create(&t1, consumer, 0);
thread_create(&t2, producer, 0);
thread_join(t1);
thread_join(t2);
cond_destroy(&cv);
mutex_destroy(&mtx);
return 0;
}