forked from crystal-lang/crystal
/
thread.cr
111 lines (90 loc) · 2.44 KB
/
thread.cr
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
require "c/pthread"
require "./thread/*"
# :nodoc:
class Thread
@@mutex = Thread::Mutex.new
# Don't use this class, it is used internally by the event scheduler.
# Use spawn and channels instead.
@th : LibC::PthreadT?
@exception : Exception?
@detached = false
property! current_fiber : Fiber?
def initialize(&@func : ->)
@@mutex.synchronize do
@@threads << self
@th = th = uninitialized LibC::PthreadT
ret = GC.pthread_create(pointerof(th), Pointer(LibC::PthreadAttrT).null, ->(data : Void*) {
(data.as(Thread)).start
Pointer(Void).null
}, self.as(Void*))
@th = th
if ret != 0
raise Errno.new("pthread_create")
end
end
end
# Used to initialize the crystal object of the
# existing main thread.
# Note *the* thread initialized with this constructor
# will not call `Thread#start`.
def initialize
@func = ->{}
@@mutex.synchronize do
@@threads << self
@th = LibC.pthread_self
@current_fiber = Fiber.new(self)
end
end
def finalize
GC.pthread_detach(@th.not_nil!) unless @detached
end
def join
GC.pthread_join(@th.not_nil!)
@detached = true
if exception = @exception
raise exception
end
end
# All threads, so the GC can see them (GC doesn't scan thread locals)
# and we can find the current thread on platforms that don't support
# thread local storage (eg: OpenBSD)
@@threads = Set(Thread).new
@@main = new
def self.current : Thread
if main?
return @@main
else
find_current_by_id
end
end
protected def start
# Initialize main fiber of thread once the thread has started.
# Before the thread actually starts there is no fiber information
@current_fiber ||= Fiber.new(self)
begin
@func.call
rescue ex
@exception = ex
ensure
@@threads.delete(self)
end
end
# Checks if the current thread is the main thread
def self.main?
LibC.pthread_main_np == 1
end
# Find the current thread object with a linear search among all threads
protected def self.find_current_by_id : Thread
@@mutex.synchronize do
current_thread_id = LibC.pthread_self
current_thread = @@threads.find do |thread|
LibC.pthread_equal(thread.id, current_thread_id) != 0
end
raise "Error: failed to find current thread" unless current_thread
current_thread
end
end
protected def id
@th.not_nil!
end
end