blob: 0247f330d944039b5c1d3cd875b2f2bd5a44d4e8 (
plain)
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
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
|
# frozen_string_literal: true
require "test/unit"
require_relative 'scheduler'
class TestFiberThread < Test::Unit::TestCase
def test_thread_join
thread = Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler scheduler
result = nil
Fiber.schedule do
result = Thread.new{:done}.value
end
scheduler.run
result
end
assert_equal :done, thread.value
end
def test_thread_join_timeout
sleeper = nil
thread = Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler scheduler
Fiber.schedule do
sleeper = Thread.new{sleep}
sleeper.join(0.1)
end
scheduler.run
end
thread.join
assert_predicate sleeper, :alive?
ensure
sleeper&.kill&.join
end
def test_thread_join_implicit
sleeping = false
finished = false
thread = Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler scheduler
Fiber.schedule do
sleeping = true
sleep(0.1)
finished = true
end
:done
end
Thread.pass until sleeping
thread.join
assert_equal :done, thread.value
assert finished, "Scheduler thread's task should be finished!"
end
def test_thread_join_blocking
thread = Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler scheduler
result = nil
Fiber.schedule do
Fiber.new(blocking: true) do
# This can deadlock if the blocking state is not taken into account:
Thread.new do
sleep(0)
result = :done
end.join
end.resume
end
scheduler.run
result
end
assert_equal :done, thread.value
end
def test_spurious_unblock_during_thread_join
ready = Thread::Queue.new
target_thread = Thread.new do
ready.pop
:success
end
Thread.pass until target_thread.status == "sleep"
result = nil
thread = Thread.new do
scheduler = Scheduler.new
Fiber.set_scheduler scheduler
# Create a fiber that will join a long-running thread:
joining_fiber = Fiber.schedule do
result = target_thread.value
end
# Create another fiber that spuriously unblocks the joining fiber:
Fiber.schedule do
# This interrupts the join in joining_fiber:
scheduler.unblock(:spurious_wakeup, joining_fiber)
# This allows the unblock to be processed:
sleep(0)
# This allows the target thread to finish:
ready.push(:done)
end
scheduler.run
end
thread.join
assert_equal :success, result
end
def test_broken_unblock
thread = Thread.new do
Thread.current.report_on_exception = false
scheduler = BrokenUnblockScheduler.new
Fiber.set_scheduler scheduler
Fiber.schedule do
Thread.new{
Thread.current.report_on_exception = false
}.join
end
scheduler.run
ensure
scheduler.close
end
assert_raise(RuntimeError) do
thread.join
end
end
def test_thread_join_hang
thread = Thread.new do
scheduler = SleepingUnblockScheduler.new
Fiber.set_scheduler scheduler
Fiber.schedule do
Thread.new{sleep(0.01)}.value
end
end
thread.join
end
end
|