Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

POSIX threads corrections & enhancements #6944

Merged
merged 4 commits into from
Oct 16, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 57 additions & 58 deletions spec/std/thread/condition_variable_spec.cr
Original file line number Diff line number Diff line change
@@ -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.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since these were pending until somebody works on parallelism, and nobody did that yet, is it worth uncommenting these?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Who said "nobody's working on MT" :)

But sure, I can keep the note.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, I meant all the specs. But if "somebody" ;-) is working on it, then this is fine.

#
# 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
36 changes: 36 additions & 0 deletions spec/std/thread/mutex_spec.cr
Original file line number Diff line number Diff line change
@@ -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
23 changes: 23 additions & 0 deletions spec/std/thread_spec.cr
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,27 @@ 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

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
RX14 marked this conversation as resolved.
Show resolved Hide resolved
end
end
2 changes: 1 addition & 1 deletion src/gc/boehm.cr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
9 changes: 9 additions & 0 deletions src/lib_c/aarch64-linux-gnu/c/pthread.cr
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
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
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
Expand Down
3 changes: 3 additions & 0 deletions src/lib_c/aarch64-linux-gnu/c/sched.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib LibC
fun sched_yield : Int
end
9 changes: 9 additions & 0 deletions src/lib_c/aarch64-linux-musl/c/pthread.cr
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
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
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
Expand Down
3 changes: 3 additions & 0 deletions src/lib_c/aarch64-linux-musl/c/sched.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib LibC
fun sched_yield : Int
end
9 changes: 9 additions & 0 deletions src/lib_c/amd64-unknown-openbsd/c/pthread.cr
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,16 @@ require "./sys/types"

@[Link("pthread")]
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
Expand All @@ -14,6 +20,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
Expand Down
3 changes: 3 additions & 0 deletions src/lib_c/amd64-unknown-openbsd/c/sched.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib LibC
fun sched_yield : Int
end
9 changes: 9 additions & 0 deletions src/lib_c/arm-linux-gnueabihf/c/pthread.cr
Original file line number Diff line number Diff line change
@@ -1,15 +1,24 @@
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
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
Expand Down
3 changes: 3 additions & 0 deletions src/lib_c/arm-linux-gnueabihf/c/sched.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib LibC
fun sched_yield : Int
end
9 changes: 9 additions & 0 deletions src/lib_c/i686-linux-gnu/c/pthread.cr
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
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
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
Expand Down
3 changes: 3 additions & 0 deletions src/lib_c/i686-linux-gnu/c/sched.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib LibC
fun sched_yield : Int
end
9 changes: 9 additions & 0 deletions src/lib_c/i686-linux-musl/c/pthread.cr
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
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
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
Expand Down
3 changes: 3 additions & 0 deletions src/lib_c/i686-linux-musl/c/sched.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
lib LibC
fun sched_yield : Int
end
Loading