From 8d932577d12c2355fd263ee2dc8cd63593fc9ad8 Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Sun, 14 Oct 2018 15:46:54 +0200 Subject: [PATCH 1/4] Fix: POSIX threads return an errnum, but don't set errno - Pass the returned errnum to `Errno.new`. - Fix `Thread::Mutex.try_lock` to return true when the lock was acquired and false otherwise. - Add a spec for `Thread.current`. --- spec/std/thread_spec.cr | 8 ++++++++ src/gc/boehm.cr | 2 +- src/thread.cr | 6 +++--- src/thread/condition_variable.cr | 25 ++++++++++--------------- src/thread/mutex.cr | 29 +++++++++++++++-------------- 5 files changed, 37 insertions(+), 33 deletions(-) diff --git a/spec/std/thread_spec.cr b/spec/std/thread_spec.cr index f16938655c01..47e1db6d6bf3 100644 --- a/spec/std/thread_spec.cr +++ b/spec/std/thread_spec.cr @@ -14,4 +14,12 @@ describe Thread do thread.join end end + + it "returns current thread object" do + current = nil + thread = Thread.new { current = Thread.current } + thread.join + current.should be(thread) + current.should_not be(Thread.current) + end end diff --git a/src/gc/boehm.cr b/src/gc/boehm.cr index bf37a5f607c7..92c4faa5dd70 100644 --- a/src/gc/boehm.cr +++ b/src/gc/boehm.cr @@ -162,7 +162,7 @@ module GC # :nodoc: def self.pthread_join(thread : LibC::PthreadT) : Void* ret = LibGC.pthread_join(thread, out value) - raise Errno.new("pthread_join") unless ret == 0 + raise Errno.new("pthread_join", ret) unless ret == 0 value end diff --git a/src/thread.cr b/src/thread.cr index 28f9fbf62a0a..ed33b6d11f09 100644 --- a/src/thread.cr +++ b/src/thread.cr @@ -32,7 +32,7 @@ class Thread if ret == 0 @@threads.push(self) else - raise Errno.new("pthread_create") + raise Errno.new("pthread_create", ret) end end @@ -67,7 +67,7 @@ class Thread @@current_key = begin ret = LibC.pthread_key_create(out current_key, nil) - raise Errno.new("pthread_key_create") unless ret == 0 + raise Errno.new("pthread_key_create", ret) unless ret == 0 current_key end @@ -83,7 +83,7 @@ class Thread # Associates the Thread object to the running system thread. protected def self.current=(thread : Thread) : Thread ret = LibC.pthread_setspecific(@@current_key, thread.as(Void*)) - raise Errno.new("pthread_setspecific") unless ret == 0 + raise Errno.new("pthread_setspecific", ret) unless ret == 0 thread end {% else %} diff --git a/src/thread/condition_variable.cr b/src/thread/condition_variable.cr index 2eb8c26d72d4..8f6f9c4a8000 100644 --- a/src/thread/condition_variable.cr +++ b/src/thread/condition_variable.cr @@ -5,33 +5,28 @@ class Thread # :nodoc: class ConditionVariable def initialize - if LibC.pthread_cond_init(out @cond, nil) != 0 - raise Errno.new("pthread_cond_init") - end + ret = LibC.pthread_cond_init(out @cond, nil) + raise Errno.new("pthread_cond_init", ret) unless ret == 0 end def signal - if LibC.pthread_cond_signal(self) != 0 - raise Errno.new("pthread_cond_signal") - end + ret = LibC.pthread_cond_signal(self) + raise Errno.new("pthread_cond_signal", ret) unless ret == 0 end def broadcast - if LibC.pthread_cond_broadcast(self) != 0 - raise Errno.new("pthread_cond_broadcast") - end + ret = LibC.pthread_cond_broadcast(self) + raise Errno.new("pthread_cond_broadcast", ret) unless ret == 0 end def wait(mutex : Thread::Mutex) - if LibC.pthread_cond_wait(self, mutex) != 0 - raise Errno.new("pthread_cond_wait") - end + ret = LibC.pthread_cond_wait(self, mutex) + raise Errno.new("pthread_cond_wait", ret) unless ret == 0 end def finalize - if LibC.pthread_cond_destroy(self) != 0 - raise Errno.new("pthread_cond_broadcast") - end + ret = LibC.pthread_cond_destroy(self) + raise Errno.new("pthread_cond_broadcast", ret) unless ret == 0 end def to_unsafe diff --git a/src/thread/mutex.cr b/src/thread/mutex.cr index f1241a0c6549..3c53f80fd55f 100644 --- a/src/thread/mutex.cr +++ b/src/thread/mutex.cr @@ -5,27 +5,29 @@ class Thread # :nodoc: class Mutex def initialize - if LibC.pthread_mutex_init(out @mutex, nil) != 0 - raise Errno.new("pthread_mutex_init") - end + ret = LibC.pthread_mutex_init(out @mutex, nil) + raise Errno.new("pthread_mutex_init", ret) unless ret == 0 end def lock - if LibC.pthread_mutex_lock(self) != 0 - raise Errno.new("pthread_mutex_lock") - end + ret = LibC.pthread_mutex_lock(self) + raise Errno.new("pthread_mutex_lock", ret) unless ret == 0 end def try_lock - if LibC.pthread_mutex_trylock(self) != 0 - raise Errno.new("pthread_mutex_trylock") + case ret = LibC.pthread_mutex_trylock(self) + when 0 + true + when Errno::EBUSY + false + else + raise Errno.new("pthread_mutex_trylock", ret) end end def unlock - if LibC.pthread_mutex_unlock(self) != 0 - raise Errno.new("pthread_mutex_unlock") - end + ret = LibC.pthread_mutex_unlock(self) + raise Errno.new("pthread_mutex_unlock", ret) unless ret == 0 end def synchronize @@ -36,9 +38,8 @@ class Thread end def finalize - if LibC.pthread_mutex_destroy(self) != 0 - raise Errno.new("pthread_mutex_destroy") - end + ret = LibC.pthread_mutex_destroy(self) + raise Errno.new("pthread_mutex_destroy", ret) unless ret == 0 end def to_unsafe From 4e55ba4c834051d53134704b27260f82256ea13c Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Sun, 14 Oct 2018 16:08:31 +0200 Subject: [PATCH 2/4] Add error checking to POSIX thread mutexes Prevents deadlocks and undefined mutex behavior. > A thread attempting to relock this mutex without first > unlocking it shall return with an error. A thread attempting > to unlock a mutex which another thread has locked shall > return with an error. A thread attempting to unlock an > unlocked mutex shall return with an error. > > http://pubs.opengroup.org/onlinepubs/7908799/xsh/pthread_mutexattr_settype.html --- spec/std/thread/mutex_spec.cr | 36 +++++++++++++++++++ src/lib_c/aarch64-linux-gnu/c/pthread.cr | 5 +++ src/lib_c/aarch64-linux-musl/c/pthread.cr | 5 +++ src/lib_c/amd64-unknown-openbsd/c/pthread.cr | 5 +++ src/lib_c/arm-linux-gnueabihf/c/pthread.cr | 5 +++ src/lib_c/i686-linux-gnu/c/pthread.cr | 5 +++ src/lib_c/i686-linux-musl/c/pthread.cr | 5 +++ src/lib_c/x86_64-linux-gnu/c/pthread.cr | 5 +++ src/lib_c/x86_64-linux-musl/c/pthread.cr | 5 +++ src/lib_c/x86_64-macosx-darwin/c/pthread.cr | 5 +++ src/lib_c/x86_64-portbld-freebsd/c/pthread.cr | 5 +++ src/thread/mutex.cr | 8 ++++- 12 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 spec/std/thread/mutex_spec.cr diff --git a/spec/std/thread/mutex_spec.cr b/spec/std/thread/mutex_spec.cr new file mode 100644 index 000000000000..0aaba3ae2fc5 --- /dev/null +++ b/spec/std/thread/mutex_spec.cr @@ -0,0 +1,36 @@ +require "spec" + +describe Thread::Mutex do + it "synchronizes" do + a = 0 + mutex = Thread::Mutex.new + + threads = 10.times.map do + Thread.new do + mutex.synchronize { a += 1 } + end + end.to_a + + threads.each(&.join) + a.should eq(10) + end + + it "won't lock recursively" do + mutex = Thread::Mutex.new + mutex.try_lock.should be_true + mutex.try_lock.should be_false + expect_raises(Errno) { mutex.lock } + mutex.unlock + end + + it "won't unlock from another thread" do + mutex = Thread::Mutex.new + mutex.lock + + expect_raises(Errno) do + Thread.new { mutex.unlock }.join + end + + mutex.unlock + end +end diff --git a/src/lib_c/aarch64-linux-gnu/c/pthread.cr b/src/lib_c/aarch64-linux-gnu/c/pthread.cr index 75146db23d80..5aafb4b6946f 100644 --- a/src/lib_c/aarch64-linux-gnu/c/pthread.cr +++ b/src/lib_c/aarch64-linux-gnu/c/pthread.cr @@ -1,6 +1,8 @@ require "./sys/types" lib LibC + PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_cond_broadcast(cond : PthreadCondT*) : Int fun pthread_cond_destroy(cond : PthreadCondT*) : Int fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int @@ -10,6 +12,9 @@ lib LibC fun pthread_detach(th : PthreadT) : Int fun pthread_equal(thread1 : PthreadT, thread2 : PthreadT) : Int fun pthread_join(th : PthreadT, thread_return : Void**) : Int + fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int fun pthread_mutex_destroy(mutex : PthreadMutexT*) : Int fun pthread_mutex_init(mutex : PthreadMutexT*, mutexattr : PthreadMutexattrT*) : Int fun pthread_mutex_lock(mutex : PthreadMutexT*) : Int diff --git a/src/lib_c/aarch64-linux-musl/c/pthread.cr b/src/lib_c/aarch64-linux-musl/c/pthread.cr index d2e9d6e30ccc..bc3c3669110b 100644 --- a/src/lib_c/aarch64-linux-musl/c/pthread.cr +++ b/src/lib_c/aarch64-linux-musl/c/pthread.cr @@ -1,6 +1,8 @@ require "./sys/types" lib LibC + PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int @@ -9,6 +11,9 @@ lib LibC fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int fun pthread_join(x0 : PthreadT, x1 : Void**) : Int + fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int diff --git a/src/lib_c/amd64-unknown-openbsd/c/pthread.cr b/src/lib_c/amd64-unknown-openbsd/c/pthread.cr index a0acaf9134b1..6b7e8947c714 100644 --- a/src/lib_c/amd64-unknown-openbsd/c/pthread.cr +++ b/src/lib_c/amd64-unknown-openbsd/c/pthread.cr @@ -2,6 +2,8 @@ require "./sys/types" @[Link("pthread")] lib LibC + PTHREAD_MUTEX_ERRORCHECK = 1 + fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int @@ -14,6 +16,9 @@ lib LibC fun pthread_join(x0 : PthreadT, x1 : Void**) : Int alias PthreadKeyDestructor = (Void*) -> fun pthread_key_create(PthreadKeyT*, PthreadKeyDestructor) : Int + fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int diff --git a/src/lib_c/arm-linux-gnueabihf/c/pthread.cr b/src/lib_c/arm-linux-gnueabihf/c/pthread.cr index 75146db23d80..5aafb4b6946f 100644 --- a/src/lib_c/arm-linux-gnueabihf/c/pthread.cr +++ b/src/lib_c/arm-linux-gnueabihf/c/pthread.cr @@ -1,6 +1,8 @@ require "./sys/types" lib LibC + PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_cond_broadcast(cond : PthreadCondT*) : Int fun pthread_cond_destroy(cond : PthreadCondT*) : Int fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int @@ -10,6 +12,9 @@ lib LibC fun pthread_detach(th : PthreadT) : Int fun pthread_equal(thread1 : PthreadT, thread2 : PthreadT) : Int fun pthread_join(th : PthreadT, thread_return : Void**) : Int + fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int fun pthread_mutex_destroy(mutex : PthreadMutexT*) : Int fun pthread_mutex_init(mutex : PthreadMutexT*, mutexattr : PthreadMutexattrT*) : Int fun pthread_mutex_lock(mutex : PthreadMutexT*) : Int diff --git a/src/lib_c/i686-linux-gnu/c/pthread.cr b/src/lib_c/i686-linux-gnu/c/pthread.cr index b893050a4ba4..87e847a7af42 100644 --- a/src/lib_c/i686-linux-gnu/c/pthread.cr +++ b/src/lib_c/i686-linux-gnu/c/pthread.cr @@ -1,6 +1,8 @@ require "./sys/types" lib LibC + PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_cond_broadcast(cond : PthreadCondT*) : Int fun pthread_cond_destroy(cond : PthreadCondT*) : Int fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int @@ -9,6 +11,9 @@ lib LibC fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int fun pthread_detach(th : PthreadT) : Int fun pthread_join(th : PthreadT, thread_return : Void**) : Int + fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int fun pthread_mutex_destroy(mutex : PthreadMutexT*) : Int fun pthread_mutex_init(mutex : PthreadMutexT*, mutexattr : PthreadMutexattrT*) : Int fun pthread_mutex_lock(mutex : PthreadMutexT*) : Int diff --git a/src/lib_c/i686-linux-musl/c/pthread.cr b/src/lib_c/i686-linux-musl/c/pthread.cr index d2e9d6e30ccc..bc3c3669110b 100644 --- a/src/lib_c/i686-linux-musl/c/pthread.cr +++ b/src/lib_c/i686-linux-musl/c/pthread.cr @@ -1,6 +1,8 @@ require "./sys/types" lib LibC + PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int @@ -9,6 +11,9 @@ lib LibC fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int fun pthread_join(x0 : PthreadT, x1 : Void**) : Int + fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int diff --git a/src/lib_c/x86_64-linux-gnu/c/pthread.cr b/src/lib_c/x86_64-linux-gnu/c/pthread.cr index b893050a4ba4..87e847a7af42 100644 --- a/src/lib_c/x86_64-linux-gnu/c/pthread.cr +++ b/src/lib_c/x86_64-linux-gnu/c/pthread.cr @@ -1,6 +1,8 @@ require "./sys/types" lib LibC + PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_cond_broadcast(cond : PthreadCondT*) : Int fun pthread_cond_destroy(cond : PthreadCondT*) : Int fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int @@ -9,6 +11,9 @@ lib LibC fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int fun pthread_detach(th : PthreadT) : Int fun pthread_join(th : PthreadT, thread_return : Void**) : Int + fun pthread_mutexattr_destroy(attr : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(attr : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(attr : PthreadMutexattrT*, type : Int) : Int fun pthread_mutex_destroy(mutex : PthreadMutexT*) : Int fun pthread_mutex_init(mutex : PthreadMutexT*, mutexattr : PthreadMutexattrT*) : Int fun pthread_mutex_lock(mutex : PthreadMutexT*) : Int diff --git a/src/lib_c/x86_64-linux-musl/c/pthread.cr b/src/lib_c/x86_64-linux-musl/c/pthread.cr index d2e9d6e30ccc..bc3c3669110b 100644 --- a/src/lib_c/x86_64-linux-musl/c/pthread.cr +++ b/src/lib_c/x86_64-linux-musl/c/pthread.cr @@ -1,6 +1,8 @@ require "./sys/types" lib LibC + PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int @@ -9,6 +11,9 @@ lib LibC fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int fun pthread_join(x0 : PthreadT, x1 : Void**) : Int + fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int diff --git a/src/lib_c/x86_64-macosx-darwin/c/pthread.cr b/src/lib_c/x86_64-macosx-darwin/c/pthread.cr index d2e9d6e30ccc..cac58cd1518c 100644 --- a/src/lib_c/x86_64-macosx-darwin/c/pthread.cr +++ b/src/lib_c/x86_64-macosx-darwin/c/pthread.cr @@ -1,6 +1,8 @@ require "./sys/types" lib LibC + PTHREAD_MUTEX_ERRORCHECK = 1 + fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int @@ -9,6 +11,9 @@ lib LibC fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int fun pthread_join(x0 : PthreadT, x1 : Void**) : Int + fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int diff --git a/src/lib_c/x86_64-portbld-freebsd/c/pthread.cr b/src/lib_c/x86_64-portbld-freebsd/c/pthread.cr index c1471a4083c5..f5805dbf6558 100644 --- a/src/lib_c/x86_64-portbld-freebsd/c/pthread.cr +++ b/src/lib_c/x86_64-portbld-freebsd/c/pthread.cr @@ -2,6 +2,8 @@ require "./sys/types" @[Link("pthread")] lib LibC + PTHREAD_MUTEX_ERRORCHECK = 1 + fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int @@ -10,6 +12,9 @@ lib LibC fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int fun pthread_join(x0 : PthreadT, x1 : Void**) : Int + fun pthread_mutexattr_destroy(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_init(x0 : PthreadMutexattrT*) : Int + fun pthread_mutexattr_settype(x0 : PthreadMutexattrT*, x1 : Int) : Int fun pthread_mutex_destroy(x0 : PthreadMutexT*) : Int fun pthread_mutex_init(x0 : PthreadMutexT*, x1 : PthreadMutexattrT*) : Int fun pthread_mutex_lock(x0 : PthreadMutexT*) : Int diff --git a/src/thread/mutex.cr b/src/thread/mutex.cr index 3c53f80fd55f..ab18f9501fbe 100644 --- a/src/thread/mutex.cr +++ b/src/thread/mutex.cr @@ -5,8 +5,14 @@ class Thread # :nodoc: class Mutex def initialize - ret = LibC.pthread_mutex_init(out @mutex, nil) + attributes = uninitialized LibC::PthreadMutexattrT + LibC.pthread_mutexattr_init(pointerof(attributes)) + LibC.pthread_mutexattr_settype(pointerof(attributes), LibC::PTHREAD_MUTEX_ERRORCHECK) + + ret = LibC.pthread_mutex_init(out @mutex, pointerof(attributes)) raise Errno.new("pthread_mutex_init", ret) unless ret == 0 + + LibC.pthread_mutexattr_destroy(pointerof(attributes)) end def lock From 68d3be32c3f34b3ec97d397bcf7d467ac73d347f Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Sun, 14 Oct 2018 18:01:37 +0200 Subject: [PATCH 3/4] Add Thread.yield --- spec/std/thread_spec.cr | 15 +++++++++++++++ src/lib_c/aarch64-linux-gnu/c/sched.cr | 3 +++ src/lib_c/aarch64-linux-musl/c/sched.cr | 3 +++ src/lib_c/amd64-unknown-openbsd/c/sched.cr | 3 +++ src/lib_c/arm-linux-gnueabihf/c/sched.cr | 3 +++ src/lib_c/i686-linux-gnu/c/sched.cr | 3 +++ src/lib_c/i686-linux-musl/c/sched.cr | 3 +++ src/lib_c/x86_64-linux-gnu/c/sched.cr | 3 +++ src/lib_c/x86_64-linux-musl/c/sched.cr | 3 +++ src/lib_c/x86_64-macosx-darwin/c/sched.cr | 3 +++ src/lib_c/x86_64-portbld-freebsd/c/sched.cr | 3 +++ src/thread.cr | 6 ++++++ 12 files changed, 51 insertions(+) create mode 100644 src/lib_c/aarch64-linux-gnu/c/sched.cr create mode 100644 src/lib_c/aarch64-linux-musl/c/sched.cr create mode 100644 src/lib_c/amd64-unknown-openbsd/c/sched.cr create mode 100644 src/lib_c/arm-linux-gnueabihf/c/sched.cr create mode 100644 src/lib_c/i686-linux-gnu/c/sched.cr create mode 100644 src/lib_c/i686-linux-musl/c/sched.cr create mode 100644 src/lib_c/x86_64-linux-gnu/c/sched.cr create mode 100644 src/lib_c/x86_64-linux-musl/c/sched.cr create mode 100644 src/lib_c/x86_64-macosx-darwin/c/sched.cr create mode 100644 src/lib_c/x86_64-portbld-freebsd/c/sched.cr diff --git a/spec/std/thread_spec.cr b/spec/std/thread_spec.cr index 47e1db6d6bf3..c84e5002f3be 100644 --- a/spec/std/thread_spec.cr +++ b/spec/std/thread_spec.cr @@ -22,4 +22,19 @@ describe Thread do current.should be(thread) current.should_not be(Thread.current) end + + it "yields the processor" do + done = false + + thread = Thread.new do + 3.times { Thread.yield } + done = true + end + + until done + Thread.yield + end + + thread.join + end end diff --git a/src/lib_c/aarch64-linux-gnu/c/sched.cr b/src/lib_c/aarch64-linux-gnu/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/aarch64-linux-gnu/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/lib_c/aarch64-linux-musl/c/sched.cr b/src/lib_c/aarch64-linux-musl/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/aarch64-linux-musl/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/lib_c/amd64-unknown-openbsd/c/sched.cr b/src/lib_c/amd64-unknown-openbsd/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/amd64-unknown-openbsd/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/lib_c/arm-linux-gnueabihf/c/sched.cr b/src/lib_c/arm-linux-gnueabihf/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/arm-linux-gnueabihf/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/lib_c/i686-linux-gnu/c/sched.cr b/src/lib_c/i686-linux-gnu/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/i686-linux-gnu/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/lib_c/i686-linux-musl/c/sched.cr b/src/lib_c/i686-linux-musl/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/i686-linux-musl/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/lib_c/x86_64-linux-gnu/c/sched.cr b/src/lib_c/x86_64-linux-gnu/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/x86_64-linux-gnu/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/lib_c/x86_64-linux-musl/c/sched.cr b/src/lib_c/x86_64-linux-musl/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/x86_64-linux-musl/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/lib_c/x86_64-macosx-darwin/c/sched.cr b/src/lib_c/x86_64-macosx-darwin/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/x86_64-macosx-darwin/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/lib_c/x86_64-portbld-freebsd/c/sched.cr b/src/lib_c/x86_64-portbld-freebsd/c/sched.cr new file mode 100644 index 000000000000..9d83ed18504e --- /dev/null +++ b/src/lib_c/x86_64-portbld-freebsd/c/sched.cr @@ -0,0 +1,3 @@ +lib LibC + fun sched_yield : Int +end diff --git a/src/thread.cr b/src/thread.cr index ed33b6d11f09..2fe324249879 100644 --- a/src/thread.cr +++ b/src/thread.cr @@ -1,4 +1,5 @@ require "c/pthread" +require "c/sched" require "./thread/*" # :nodoc: @@ -106,6 +107,11 @@ class Thread # TODO: consider moving to `kernel.cr` or `crystal/main.cr` self.current = new + def self.yield + ret = LibC.sched_yield + raise Errno.new("sched_yield") unless ret == 0 + end + # Returns the Fiber representing the thread's main stack. def main_fiber @main_fiber.not_nil! From 41045d65b0b635348314271f429e201f62d90fce Mon Sep 17 00:00:00 2001 From: Julien Portalier Date: Sun, 14 Oct 2018 16:25:50 +0200 Subject: [PATCH 4/4] Add Thread::ConditionVariable#wait with timeout. Configures thread condition variables to use the monotonic clock. Adds an overload for `#wait` that takes a timeout and executes the block when the timeout is reached, but doesn't when the condition variable is normally resumed. Refactors & enables Thread::ConditionVariable specs so they don't randomly hang. --- spec/std/thread/condition_variable_spec.cr | 115 +++++++++--------- src/lib_c/aarch64-linux-gnu/c/pthread.cr | 4 + src/lib_c/aarch64-linux-musl/c/pthread.cr | 4 + src/lib_c/amd64-unknown-openbsd/c/pthread.cr | 4 + src/lib_c/arm-linux-gnueabihf/c/pthread.cr | 4 + src/lib_c/i686-linux-gnu/c/pthread.cr | 4 + src/lib_c/i686-linux-musl/c/pthread.cr | 4 + src/lib_c/x86_64-linux-gnu/c/pthread.cr | 4 + src/lib_c/x86_64-linux-musl/c/pthread.cr | 4 + src/lib_c/x86_64-macosx-darwin/c/pthread.cr | 3 + src/lib_c/x86_64-portbld-freebsd/c/pthread.cr | 4 + src/thread/condition_variable.cr | 42 ++++++- 12 files changed, 137 insertions(+), 59 deletions(-) diff --git a/spec/std/thread/condition_variable_spec.cr b/spec/std/thread/condition_variable_spec.cr index 9588c5e0f638..d5f7a5018fbd 100644 --- a/spec/std/thread/condition_variable_spec.cr +++ b/spec/std/thread/condition_variable_spec.cr @@ -1,90 +1,89 @@ require "spec" -# These specs are marked as pending until we add -# support for parallelism, where we'll see if we -# need condition variables. -# -# Also: review these specs! describe Thread::ConditionVariable do - pending "signals" do + it "signals" do mutex = Thread::Mutex.new cond = Thread::ConditionVariable.new - pcond = Thread::ConditionVariable.new - waiting = 0 - signaled = 0 - threads = 3.times.map do + mutex.synchronize do Thread.new do - mutex.synchronize do - waiting += 1 - # TODO: why is this needed? Bug? - pcond.signal - cond.wait mutex - end + mutex.synchronize { cond.signal } end - end.to_a - while signaled < 3 - mutex.synchronize do - if waiting > 0 - waiting -= 1 - signaled += 1 - cond.signal - end - end + cond.wait(mutex) end - threads.map &.join end - pending "broadcasts" do + it "signals & broadcasts" do mutex = Thread::Mutex.new - cond = Thread::ConditionVariable.new - pcond = Thread::ConditionVariable.new + cv1 = Thread::ConditionVariable.new + cv2 = Thread::ConditionVariable.new waiting = 0 - signaled = false - threads = 3.times.map do + 5.times do Thread.new do mutex.synchronize do waiting += 1 - pcond.signal - cond.wait mutex + cv1.wait(mutex) + + waiting -= 1 + cv2.signal end end - end.to_a - - until signaled - mutex.synchronize do - if waiting >= 3 - cond.broadcast - signaled = true - else - pcond.wait mutex - end + end + + # wait for all threads to have acquired the mutex and be waiting on the + # condition variable, otherwise the main thread could acquire the lock + # first, and signal into the void. + # + # since increments to waiting are synchronized, at least 4 threads are + # guaranteed to be waiting when waiting is 5, which is enough for futher + # tests to never hangup. + until waiting == 5 + Thread.yield + end + + # wakes up at least 1 thread: + mutex.synchronize do + cv1.signal + cv2.wait(mutex) + end + waiting.should be < 5 + + # wakes up all waiting threads: + mutex.synchronize do + cv1.broadcast + + until waiting == 0 + cv2.wait(mutex, 1.second) { raise "unexpected wait timeout" } end end + end + + it "timeouts" do + timedout = false + mutex = Thread::Mutex.new + cond = Thread::ConditionVariable.new - threads.map &.join + mutex.synchronize do + cond.wait(mutex, 1.microsecond) { timedout = true } + end + timedout.should be_true end - pending "waits and send signal" do - a = 0 - cv1 = Thread::ConditionVariable.new - cv2 = Thread::ConditionVariable.new - m = Thread::Mutex.new + it "resumes before timeout" do + timedout = false + mutex = Thread::Mutex.new + cond = Thread::ConditionVariable.new - thread = Thread.new do - 3.times do - m.synchronize { cv1.wait(m); a += 1; cv2.signal } + mutex.synchronize do + Thread.new do + mutex.synchronize { cond.signal } end - end - a.should eq(0) - 3.times do |i| - m.synchronize { cv1.signal; cv2.wait(m) } - a.should eq(i + 1) + cond.wait(mutex, 1.second) { timedout = true } end - thread.join + timedout.should be_false end end diff --git a/src/lib_c/aarch64-linux-gnu/c/pthread.cr b/src/lib_c/aarch64-linux-gnu/c/pthread.cr index 5aafb4b6946f..003ab9aa223b 100644 --- a/src/lib_c/aarch64-linux-gnu/c/pthread.cr +++ b/src/lib_c/aarch64-linux-gnu/c/pthread.cr @@ -3,10 +3,14 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int + fun pthread_condattr_init(attr : PthreadCondattrT*) : Int + fun pthread_condattr_setclock(attr : PthreadCondattrT*, type : ClockidT) : Int fun pthread_cond_broadcast(cond : PthreadCondT*) : Int fun pthread_cond_destroy(cond : PthreadCondT*) : Int fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int fun pthread_cond_signal(cond : PthreadCondT*) : Int + fun pthread_cond_timedwait(cond : PthreadCondT*, mutex : PthreadMutexT*, abstime : Timespec*) : Int fun pthread_cond_wait(cond : PthreadCondT*, mutex : PthreadMutexT*) : Int fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int fun pthread_detach(th : PthreadT) : Int diff --git a/src/lib_c/aarch64-linux-musl/c/pthread.cr b/src/lib_c/aarch64-linux-musl/c/pthread.cr index bc3c3669110b..d5da49a49319 100644 --- a/src/lib_c/aarch64-linux-musl/c/pthread.cr +++ b/src/lib_c/aarch64-linux-musl/c/pthread.cr @@ -3,10 +3,14 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int fun pthread_cond_signal(x0 : PthreadCondT*) : Int + fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int diff --git a/src/lib_c/amd64-unknown-openbsd/c/pthread.cr b/src/lib_c/amd64-unknown-openbsd/c/pthread.cr index 6b7e8947c714..018ec685694a 100644 --- a/src/lib_c/amd64-unknown-openbsd/c/pthread.cr +++ b/src/lib_c/amd64-unknown-openbsd/c/pthread.cr @@ -4,10 +4,14 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 1 + fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int fun pthread_cond_signal(x0 : PthreadCondT*) : Int + fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int diff --git a/src/lib_c/arm-linux-gnueabihf/c/pthread.cr b/src/lib_c/arm-linux-gnueabihf/c/pthread.cr index 5aafb4b6946f..003ab9aa223b 100644 --- a/src/lib_c/arm-linux-gnueabihf/c/pthread.cr +++ b/src/lib_c/arm-linux-gnueabihf/c/pthread.cr @@ -3,10 +3,14 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int + fun pthread_condattr_init(attr : PthreadCondattrT*) : Int + fun pthread_condattr_setclock(attr : PthreadCondattrT*, type : ClockidT) : Int fun pthread_cond_broadcast(cond : PthreadCondT*) : Int fun pthread_cond_destroy(cond : PthreadCondT*) : Int fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int fun pthread_cond_signal(cond : PthreadCondT*) : Int + fun pthread_cond_timedwait(cond : PthreadCondT*, mutex : PthreadMutexT*, abstime : Timespec*) : Int fun pthread_cond_wait(cond : PthreadCondT*, mutex : PthreadMutexT*) : Int fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int fun pthread_detach(th : PthreadT) : Int diff --git a/src/lib_c/i686-linux-gnu/c/pthread.cr b/src/lib_c/i686-linux-gnu/c/pthread.cr index 87e847a7af42..944f2ce9c30b 100644 --- a/src/lib_c/i686-linux-gnu/c/pthread.cr +++ b/src/lib_c/i686-linux-gnu/c/pthread.cr @@ -3,10 +3,14 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int + fun pthread_condattr_init(attr : PthreadCondattrT*) : Int + fun pthread_condattr_setclock(attr : PthreadCondattrT*, type : ClockidT) : Int fun pthread_cond_broadcast(cond : PthreadCondT*) : Int fun pthread_cond_destroy(cond : PthreadCondT*) : Int fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int fun pthread_cond_signal(cond : PthreadCondT*) : Int + fun pthread_cond_timedwait(cond : PthreadCondT*, mutex : PthreadMutexT*, abstime : Timespec*) : Int fun pthread_cond_wait(cond : PthreadCondT*, mutex : PthreadMutexT*) : Int fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int fun pthread_detach(th : PthreadT) : Int diff --git a/src/lib_c/i686-linux-musl/c/pthread.cr b/src/lib_c/i686-linux-musl/c/pthread.cr index bc3c3669110b..d5da49a49319 100644 --- a/src/lib_c/i686-linux-musl/c/pthread.cr +++ b/src/lib_c/i686-linux-musl/c/pthread.cr @@ -3,10 +3,14 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int fun pthread_cond_signal(x0 : PthreadCondT*) : Int + fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int diff --git a/src/lib_c/x86_64-linux-gnu/c/pthread.cr b/src/lib_c/x86_64-linux-gnu/c/pthread.cr index 87e847a7af42..944f2ce9c30b 100644 --- a/src/lib_c/x86_64-linux-gnu/c/pthread.cr +++ b/src/lib_c/x86_64-linux-gnu/c/pthread.cr @@ -3,10 +3,14 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_condattr_destroy(attr : PthreadCondattrT*) : Int + fun pthread_condattr_init(attr : PthreadCondattrT*) : Int + fun pthread_condattr_setclock(attr : PthreadCondattrT*, type : ClockidT) : Int fun pthread_cond_broadcast(cond : PthreadCondT*) : Int fun pthread_cond_destroy(cond : PthreadCondT*) : Int fun pthread_cond_init(cond : PthreadCondT*, cond_attr : PthreadCondattrT*) : Int fun pthread_cond_signal(cond : PthreadCondT*) : Int + fun pthread_cond_timedwait(cond : PthreadCondT*, mutex : PthreadMutexT*, abstime : Timespec*) : Int fun pthread_cond_wait(cond : PthreadCondT*, mutex : PthreadMutexT*) : Int fun pthread_create(newthread : PthreadT*, attr : PthreadAttrT*, start_routine : Void* -> Void*, arg : Void*) : Int fun pthread_detach(th : PthreadT) : Int diff --git a/src/lib_c/x86_64-linux-musl/c/pthread.cr b/src/lib_c/x86_64-linux-musl/c/pthread.cr index bc3c3669110b..d5da49a49319 100644 --- a/src/lib_c/x86_64-linux-musl/c/pthread.cr +++ b/src/lib_c/x86_64-linux-musl/c/pthread.cr @@ -3,10 +3,14 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 2 + fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int fun pthread_cond_signal(x0 : PthreadCondT*) : Int + fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int diff --git a/src/lib_c/x86_64-macosx-darwin/c/pthread.cr b/src/lib_c/x86_64-macosx-darwin/c/pthread.cr index cac58cd1518c..5ca68fa77ce2 100644 --- a/src/lib_c/x86_64-macosx-darwin/c/pthread.cr +++ b/src/lib_c/x86_64-macosx-darwin/c/pthread.cr @@ -3,10 +3,13 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 1 + fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int fun pthread_cond_signal(x0 : PthreadCondT*) : Int + fun pthread_cond_timedwait_relative_np(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int diff --git a/src/lib_c/x86_64-portbld-freebsd/c/pthread.cr b/src/lib_c/x86_64-portbld-freebsd/c/pthread.cr index f5805dbf6558..4c7a012c2424 100644 --- a/src/lib_c/x86_64-portbld-freebsd/c/pthread.cr +++ b/src/lib_c/x86_64-portbld-freebsd/c/pthread.cr @@ -4,10 +4,14 @@ require "./sys/types" lib LibC PTHREAD_MUTEX_ERRORCHECK = 1 + fun pthread_condattr_destroy(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_init(x0 : PthreadCondattrT*) : Int + fun pthread_condattr_setclock(x0 : PthreadCondattrT*, x1 : ClockidT) : Int fun pthread_cond_broadcast(x0 : PthreadCondT*) : Int fun pthread_cond_destroy(x0 : PthreadCondT*) : Int fun pthread_cond_init(x0 : PthreadCondT*, x1 : PthreadCondattrT*) : Int fun pthread_cond_signal(x0 : PthreadCondT*) : Int + fun pthread_cond_timedwait(x0 : PthreadCondT*, x1 : PthreadMutexT*, x2 : Timespec*) : Int fun pthread_cond_wait(x0 : PthreadCondT*, x1 : PthreadMutexT*) : Int fun pthread_create(x0 : PthreadT*, x1 : PthreadAttrT*, x2 : Void* -> Void*, x3 : Void*) : Int fun pthread_detach(x0 : PthreadT) : Int diff --git a/src/thread/condition_variable.cr b/src/thread/condition_variable.cr index 8f6f9c4a8000..5c8da3365dd4 100644 --- a/src/thread/condition_variable.cr +++ b/src/thread/condition_variable.cr @@ -5,8 +5,17 @@ class Thread # :nodoc: class ConditionVariable def initialize - ret = LibC.pthread_cond_init(out @cond, nil) + attributes = uninitialized LibC::PthreadCondattrT + LibC.pthread_condattr_init(pointerof(attributes)) + + {% unless flag?(:darwin) %} + LibC.pthread_condattr_setclock(pointerof(attributes), LibC::CLOCK_MONOTONIC) + {% end %} + + ret = LibC.pthread_cond_init(out @cond, pointerof(attributes)) raise Errno.new("pthread_cond_init", ret) unless ret == 0 + + LibC.pthread_condattr_destroy(pointerof(attributes)) end def signal @@ -24,6 +33,37 @@ class Thread raise Errno.new("pthread_cond_wait", ret) unless ret == 0 end + def wait(mutex : Thread::Mutex, time : Time::Span) + ret = + {% if flag?(:darwin) %} + ts = uninitialized LibC::Timespec + ts.tv_sec = time.to_i + ts.tv_nsec = time.nanoseconds + + LibC.pthread_cond_timedwait_relative_np(self, mutex, pointerof(ts)) + {% else %} + LibC.clock_gettime(LibC::CLOCK_MONOTONIC, out ts) + ts.tv_sec += time.to_i + ts.tv_nsec += time.nanoseconds + + if ts.tv_nsec >= 1_000_000_000 + ts.tv_sec += 1 + ts.tv_nsec -= 1_000_000_000 + end + + LibC.pthread_cond_timedwait(self, mutex, pointerof(ts)) + {% end %} + + case ret + when 0 + # normal resume from #signal or #broadcast + when Errno::ETIMEDOUT + yield + else + raise Errno.new("pthread_cond_timedwait", ret) + end + end + def finalize ret = LibC.pthread_cond_destroy(self) raise Errno.new("pthread_cond_broadcast", ret) unless ret == 0