Download
142 lines 4.6 KiBTomoModified 2026-06-06 raw
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
# A Posix Threads (pthreads) wrapper
use <gc.h>
use <pthread.h>

struct Mutex(_mutex:@Memory)
    func new(->Mutex)
        return Mutex(
            C_code : @Memory`
                pthread_mutex_t *mutex = GC_MALLOC(sizeof(pthread_mutex_t));
                pthread_mutex_init(mutex, NULL);
                GC_register_finalizer(mutex, (void*)pthread_mutex_destroy, NULL, NULL, NULL);
                mutex
            `
        )

    func lock(m:Mutex)
        status := C_code:Int32`pthread_mutex_lock(@(m._mutex))`
        fail("Failed to lock Mutex") unless status == 0

    func unlock(m:Mutex)
        status := C_code:Int32`pthread_mutex_unlock(@(m._mutex))`
        fail("Failed to unlock Mutex") unless status == 0

struct Condition(_cond:@Memory)
    func new(->Condition)
        return Condition(
            C_code : @Memory `
                pthread_cond_t *cond = GC_MALLOC(sizeof(pthread_cond_t));
                pthread_cond_init(cond, NULL);
                GC_register_finalizer(cond, (void*)pthread_cond_destroy, NULL, NULL, NULL);
                cond
            `
        )

    func wait(cond:Condition, mutex:Mutex)
        status := C_code:Int32`pthread_cond_wait(@(cond._cond), @(mutex._mutex))`
        fail("Failed to wait on Condition") unless status == 0

    func signal(cond:Condition)
        status := C_code:Int32`pthread_cond_signal(@(cond._cond))`
        fail("Failed to signal Condition") unless status == 0

    func broadcast(cond:Condition)
        status := C_code:Int32`pthread_cond_broadcast(@(cond._cond))`
        fail("Failed to broadcast Condition") unless status == 0

struct RWLock(_rwlock:@Memory)
    func new(->RWLock)
        return RWLock(
            C_code : @Memory `
                pthread_rwlock_t *lock = GC_MALLOC(sizeof(pthread_rwlock_t));
                pthread_rwlock_init(lock, NULL);
                GC_register_finalizer(lock, (void*)pthread_rwlock_destroy, NULL, NULL, NULL);
                lock
            `
        )

    func read_lock(lock:RWLock)
        status := C_code:Int32 `pthread_rwlock_rdlock(@(lock._rwlock))`
        fail("Failed to read-lock RWLock") unless status == 0

    func write_lock(lock:RWLock)
        status := C_code:Int32 `pthread_rwlock_wrlock(@(lock._rwlock))`
        fail("Failed to write-lock RWLock") unless status == 0

    func unlock(lock:RWLock)
        status := C_code:Int32 `pthread_rwlock_unlock(@(lock._rwlock))`
        fail("Failed to unlock RWLock") unless status == 0

struct PThread(_pthread:@Memory)
    func new(fn:func() -> PThread)
        return PThread(
            C_code:@Memory `
                pthread_t *thread = GC_MALLOC(sizeof(pthread_t));
                pthread_create(thread, NULL, @fn.fn, @fn.userdata);
                thread
            `
        )

    func join(p:PThread)
        status := C_code:Int32 `pthread_join(*(pthread_t*)@(p._pthread), NULL)`
        fail("Failed to cancel PThread") if status != 0

    func cancel(p:PThread)
        status := C_code:Int32 `pthread_cancel(*(pthread_t*)@(p._pthread))`
        fail("Failed to cancel PThread") if status != 0

    func detatch(p:PThread)
        status := C_code:Int32 `pthread_detach(*(pthread_t*)@(p._pthread))`
        fail("Failed to detatch PThread") if status != 0

struct IntQueue(_queue:@[Int], _mutex:Mutex, _cond:Condition)
    func new(initial:[Int]=[] -> IntQueue)
        return IntQueue(@initial, Mutex.new(), Condition.new())

    func give(q:IntQueue, n:Int)
        do q._mutex.lock()
            q._queue.insert(n)
            q._mutex.unlock()
        q._cond.signal()

    func take(q:IntQueue -> Int)
        do q._mutex.lock()
            n := q._queue.pop(1)
            while not n
                q._cond.wait(q._mutex)
                n = q._queue.pop(1)
            q._mutex.unlock()
            return n!

func main()
    jobs := IntQueue.new()
    results := IntQueue.new()

    say_mutex := Mutex.new()
    announce := func(speaker:Text, text:Text)
        do say_mutex.lock()
            say("\[2][$speaker]\[] $text")
            say_mutex.unlock()

    worker := PThread.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()