aboutsummaryrefslogtreecommitdiff
path: root/examples/pthreads/pthreads.tm
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-03-18 17:31:17 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-03-18 17:31:17 -0400
commit45cbcbce3b061f40c99ad03a6f38f92ba3fc63d7 (patch)
tree01aeb6f8cd75a86aa66f4ab09c81c8a500faa736 /examples/pthreads/pthreads.tm
parent6086b2dbda874d5538e878101c7ee98a10fdeddb (diff)
Pluralize pthreads
Diffstat (limited to 'examples/pthreads/pthreads.tm')
-rw-r--r--examples/pthreads/pthreads.tm107
1 files changed, 107 insertions, 0 deletions
diff --git a/examples/pthreads/pthreads.tm b/examples/pthreads/pthreads.tm
new file mode 100644
index 00000000..13d94e40
--- /dev/null
+++ b/examples/pthreads/pthreads.tm
@@ -0,0 +1,107 @@
+# A Posix Threads (pthreads) wrapper
+use <pthread.h>
+
+extern pthread_mutex_lock:func(mutex:&pthread_mutex_t -> Int32)
+extern pthread_mutex_unlock:func(mutex:&pthread_mutex_t -> Int32)
+
+struct pthread_mutex_t(; extern, opaque):
+ func new(->@pthread_mutex_t):
+ return inline C : @pthread_mutex_t {
+ pthread_mutex_t *mutex = new(pthread_mutex_t);
+ pthread_mutex_init(mutex, NULL);
+ GC_register_finalizer(mutex, (void*)pthread_mutex_destroy, NULL, NULL, NULL);
+ mutex
+ }
+
+ func lock(m:&pthread_mutex_t):
+ fail("Failed to lock mutex") unless pthread_mutex_lock(m) == 0
+
+ func unlock(m:&pthread_mutex_t):
+ fail("Failed to unlock mutex") unless pthread_mutex_unlock(m) == 0
+
+extern pthread_cond_wait:func(cond:&pthread_cond_t, mutex:&pthread_mutex_t -> Int32)
+extern pthread_cond_signal:func(cond:&pthread_cond_t -> Int32)
+extern pthread_cond_broadcast:func(cond:&pthread_cond_t -> Int32)
+
+struct pthread_cond_t(; extern, opaque):
+ func new(->@pthread_cond_t):
+ return inline C : @pthread_cond_t {
+ pthread_cond_t *cond = new(pthread_cond_t);
+ pthread_cond_init(cond, NULL);
+ GC_register_finalizer(cond, (void*)pthread_cond_destroy, NULL, NULL, NULL);
+ cond
+ }
+
+ func wait(cond:&pthread_cond_t, mutex:&pthread_mutex_t):
+ fail("Failed to wait on condition") unless pthread_cond_wait(cond, mutex) == 0
+
+ func signal(cond:&pthread_cond_t):
+ fail("Failed to signal pthread_cond_t") unless pthread_cond_signal(cond) == 0
+
+ func broadcast(cond:&pthread_cond_t):
+ fail("Failed to broadcast pthread_cond_t") unless pthread_cond_broadcast(cond) == 0
+
+struct pthread_t(; extern, opaque):
+ func new(fn:func() -> @pthread_t):
+ return inline C : @pthread_t {
+ pthread_t *thread = new(pthread_t);
+ pthread_create(thread, NULL, _$fn.fn, _$fn.userdata);
+ thread
+ }
+
+ func join(p:@pthread_t): inline C { pthread_join(*_$p, NULL); }
+ func cancel(p:@pthread_t): inline C { pthread_cancel(*_$p); }
+ func detatch(p:@pthread_t): inline C { pthread_detach(*_$p); }
+
+struct IntQueue(_queue:@[Int], _mutex:@pthread_mutex_t, _cond:@pthread_cond_t):
+ func new(initial=[:Int] -> IntQueue):
+ return IntQueue(@initial, pthread_mutex_t.new(), pthread_cond_t.new())
+
+ func give(q:IntQueue, n:Int):
+ begin: q._mutex:lock()
+ end: q._mutex:unlock()
+ do: q._queue:insert(n)
+ q._cond:signal()
+
+ func take(q:IntQueue -> Int):
+ begin: q._mutex:lock()
+ end: q._mutex:unlock()
+ do:
+ repeat:
+ if n := q._queue:pop(1):
+ return n
+ q._cond:wait(q._mutex)
+ fail("Unreachable")
+
+func main():
+ jobs := IntQueue.new()
+ results := IntQueue.new()
+
+ say_mutex := pthread_mutex_t.new()
+ announce := func(speaker:Text, text:Text):
+ begin: say_mutex:lock()
+ end: say_mutex:unlock()
+ do: say("$\033[2m[$speaker]$\033[m $text")
+
+ worker := pthread_t.new(func():
+ say("I'm in the thread!")
+ repeat:
+ announce("worker", "waiting for job")
+ job := jobs:take()
+ result := job * 10
+ announce("worker", "Jobbing $job into $result")
+ results:give(result)
+ announce("worker", "Signaled $result")
+ )
+
+ for i in 10:
+ announce("boss", "Pushing job $i")
+ jobs:give(i)
+ announce("boss", "Gave job $i")
+
+ for i in 10:
+ announce("boss", "Getting result...")
+ result := results:take()
+ announce("boss", "Got result $result")
+
+ >> worker:cancel()