#if runtime.Multi_Threading_Enabled {
#load "./intrinsics/atomics"
+
#load "./sync/mutex"
+ #load "./sync/condition_variable"
+ #load "./sync/semaphore"
+ #load "./sync/barrier"
+
#load "./threads/thread"
}
\ No newline at end of file
--- /dev/null
+package core.sync
+
+Barrier :: struct {
+ mutex : Mutex;
+ cond : Condition_Variable;
+
+ index : i32;
+ generation : i32;
+ thread_count : i32;
+}
+
+barrier_init :: (b: ^Barrier, thread_count: i32) {
+ mutex_init(^b.mutex);
+ condition_init(^b.cond);
+
+ b.index = 0;
+ b.generation = 0;
+ b.thread_count = thread_count;
+}
+
+barrier_destroy :: (b: ^Barrier) {
+ mutex_destroy(^b.mutex);
+ condition_destroy(^b.cond);
+}
+
+barrier_wait :: (b: ^Barrier) {
+ scoped_mutex(^b.mutex);
+
+ local_gen := b.generation;
+ b.index += 1;
+
+ if b.index < b.thread_count {
+ mutex_unlock(^b.mutex);
+
+ while local_gen == b.generation && b.index < b.thread_count {
+ condition_wait(^b.cond);
+ }
+ return;
+ }
+
+ b.index = 0;
+ b.generation += 1;
+ condition_broadcast(^b.cond);
+ return;
+}
\ No newline at end of file
--- /dev/null
+package core.sync
+
+// TODO: Free the semaphores after they are used.
+
+Condition_Variable :: struct {
+ Node :: struct {
+ semaphore : Semaphore;
+ next : ^Node;
+ }
+
+ mutex: Mutex;
+ queue: ^Node;
+}
+
+condition_init :: (c: ^Condition_Variable) {
+ mutex_init(^c.mutex);
+ c.queue = null;
+}
+
+condition_destroy :: (c: ^Condition_Variable) {
+ if c.queue != null do condition_broadcast(c);
+
+ mutex_destroy(^c.mutex);
+}
+
+condition_wait :: (c: ^Condition_Variable) {
+ self: Condition_Variable.Node;
+
+ critical_section(^c.mutex, #code {
+ self.next = c.queue;
+ c.queue = ^self;
+ semaphore_init(^self.semaphore, 0);
+ });
+
+ semaphore_wait(^self.semaphore);
+}
+
+condition_signal :: (c: ^Condition_Variable) {
+ scoped_mutex(^c.mutex);
+
+ if c.queue != null {
+ semaphore_post(^c.queue.semaphore);
+ semaphore_destroy(^c.queue.semaphore);
+ c.queue = c.queue.next;
+ }
+}
+
+condition_broadcast :: (c: ^Condition_Variable) {
+ scoped_mutex(^c.mutex);
+
+ while c.queue != null {
+ semaphore_post(^c.queue.semaphore);
+ semaphore_destroy(^c.queue.semaphore);
+ c.queue = c.queue.next;
+ }
+}
\ No newline at end of file
package core.sync
use package core.intrinsics.atomics
+use package core.thread { Thread_ID }
// `lock` has two states: 0, and 1.
// 0 means unlocked
Mutex :: struct {
lock : i32;
- // owner : Thread_Id;
+ owner : Thread_ID;
}
mutex_init :: (m: ^Mutex) {
while __atomic_cmpxchg(^m.lock, 0, 1) == 1 {
__atomic_wait(^m.lock, 1);
}
+
+ m.owner = context.thread_id;
}
mutex_unlock :: (m: ^Mutex) {
+ m.owner = -1;
__atomic_store(^m.lock, 0);
__atomic_notify(^m.lock, maximum = 1);
}
ml(m);
defer mu(m);
+}
+
+critical_section :: macro (m: ^Mutex, body: Code) -> i32 {
+ scoped_mutex(m);
+
+ #insert body;
+
+ return 0;
}
\ No newline at end of file
mutex_init(^s.mutex);
}
+semaphore_destroy :: (s: ^Semaphore) {
+ mutex_destroy(^s.mutex);
+}
+
semaphore_post :: (s: ^Semaphore) {
scoped_mutex(^s.mutex);
s.counter += 1;
use package core.intrinsics.atomics
#private {
+ runtime :: package runtime
+
thread_mutex : sync.Mutex;
next_thread_id := 1;
thread_map : Map(Thread_ID, ^Thread);
}
}
-#private_file runtime :: package runtime