added test for availability of wait and notify
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 22 Nov 2021 04:31:09 +0000 (22:31 -0600)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Mon, 22 Nov 2021 04:31:09 +0000 (22:31 -0600)
core/intrinsics/atomics.onyx
core/sync/mutex.onyx
core/sync/semaphore.onyx
core/threads/thread.onyx
src/builtins.c

index 542513bdffeca5277ed190d8a363b6c3eacac15f..70d5b01d8ce4193bd746453629a7a695f42f47bc 100644 (file)
@@ -9,8 +9,10 @@ package core.intrinsics.atomics
 }
 
 // __atomic_wait is only valid for i32 and i64
-__atomic_wait   :: (addr: ^$T, value: T, timeout: i64 = -1) -> i32 #intrinsic ---
-__atomic_notify :: (addr: rawptr, maximum: i32 = 1) -> i32 #intrinsic ---
+#if runtime.Wait_Notify_Available {
+    __atomic_wait   :: (addr: ^$T, value: T, timeout: i64 = -1) -> i32 #intrinsic ---
+    __atomic_notify :: (addr: rawptr, maximum: i32 = 1) -> i32 #intrinsic ---
+}
 
 __atomic_fence  :: () -> void #intrinsic ---
 
index 19cc3894f058c1e9c19987789bbc771e86f81410..5fb2316079c7df2eb884c481c81d2e27de5bb656 100644 (file)
@@ -3,6 +3,8 @@ package core.sync
 use package core.intrinsics.atomics
 use package core.thread { Thread_ID }
 
+#local runtime :: package runtime
+
 // `lock` has two states: 0, and 1.
 //    0 means unlocked
 //    1 means locked
@@ -32,12 +34,14 @@ mutex_lock :: (m: ^Mutex) {
     while __atomic_cmpxchg(^m.lock, 0, 1) == 1 {
         if m.owner == context.thread_id do return;
 
-        @ThreadingCleanup // You cannot wait on the main thread in
-        // a web browser, for kind of obvious reasons. However, this
-        // makes waiting for a mutex expensive because the only option
-        // is to do a spin-lock. Ugh.
-        if context.thread_id != 0 {
-            __atomic_wait(^m.lock, 1);
+        #if runtime.Wait_Notify_Available {
+            @ThreadingCleanup // You cannot wait on the main thread in
+            // a web browser, for kind of obvious reasons. However, this
+            // makes waiting for a mutex expensive because the only option
+            // is to do a spin-lock. Ugh.
+            if context.thread_id != 0 {
+                __atomic_wait(^m.lock, 1);
+            }
         }
     }
 
@@ -49,7 +53,10 @@ mutex_unlock :: (m: ^Mutex) {
     
     m.owner = -1;
     __atomic_store(^m.lock, 0);
-    __atomic_notify(^m.lock, maximum = 1);
+
+    #if runtime.Wait_Notify_Available {
+        __atomic_notify(^m.lock, maximum = 1);
+    }
 }
 
 scoped_mutex :: macro (m: ^Mutex) {
index 26f061e1168c16823f9b23d0085aef72253167c9..88de3b24ec476ab10c2d20a4c27d9d825eb86d9d 100644 (file)
@@ -1,6 +1,7 @@
 package core.sync
 
 use package core.intrinsics.atomics
+#local runtime :: package runtime
 
 Semaphore :: struct {
     mutex   : Mutex;
@@ -22,7 +23,12 @@ semaphore_post :: (s: ^Semaphore, count := 1) {
     
     scoped_mutex(^s.mutex);
     s.counter += count;
-    __atomic_notify(^s.counter, maximum = count);
+
+    @Bug // This is susceptible to starvation. Semaphores should have a queue
+    // or something like that.
+    #if runtime.Wait_Notify_Available {
+        __atomic_notify(^s.counter, maximum = count);
+    }
 }
 
 semaphore_wait :: (s: ^Semaphore) {
@@ -37,7 +43,9 @@ semaphore_wait :: (s: ^Semaphore) {
         } else {
             mutex_unlock(^s.mutex);
 
-            __atomic_wait(^s.counter, 0);
+            #if runtime.Wait_Notify_Available {
+                __atomic_wait(^s.counter, 0);
+            }
         }
     }
 }
\ No newline at end of file
index 2f1d8b658e494cd7ad649f1437dad802e8886833..632bd1dfe4b082edca9126b119ddc4fd10bbfb84 100644 (file)
@@ -31,7 +31,11 @@ spawn :: (t: ^Thread, data: rawptr, func: (rawptr) -> void) {
 }
 
 join :: (t: ^Thread) {
-    while t.alive do __atomic_wait(^t.id, t.id);
+    while t.alive {
+        #if runtime.Wait_Notify_Available {
+            __atomic_wait(^t.id, t.id);
+        }
+    }
 }
 
 kill :: (t: ^Thread) -> i32 {
@@ -53,7 +57,9 @@ __exited :: (id: i32) {
     thread := thread_map->get(id);
     if thread != null {
         thread.alive = false;
-        __atomic_notify(^thread.id);
+        #if runtime.Wait_Notify_Available {
+            __atomic_notify(^thread.id);
+        }
 
         thread_map->delete(id);
     }
index 8ddfa417ce342734f584b57b5fb0332171f92b44..f86a63fa180f03da1ee0cbf2e9b05a4cc5cdb5bd 100644 (file)
@@ -474,4 +474,8 @@ void introduce_build_options(bh_allocator a) {
     AstNumLit* multi_threaded = make_int_literal(a, context.options->use_multi_threading);
     multi_threaded->type_node = (AstType *) &basic_type_bool;
     symbol_builtin_introduce(p->scope, "Multi_Threading_Enabled", (AstNode *) multi_threaded);
+
+    AstNumLit* wait_notify_available = make_int_literal(a, context.options->use_multi_threading && context.options->runtime == Runtime_Js);
+    wait_notify_available->type_node = (AstType *) &basic_type_bool;
+    symbol_builtin_introduce(p->scope, "Wait_Notify_Available", (AstNode *) wait_notify_available);
 }