dramatis 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/History.txt +7 -0
- data/License.txt +20 -0
- data/Manifest.txt +119 -0
- data/README.txt +57 -0
- data/Rakefile +4 -0
- data/config/hoe.rb +70 -0
- data/config/requirements.rb +17 -0
- data/examples/README.txt +20 -0
- data/examples/auction.rb +90 -0
- data/examples/bank/bank.rb +7 -0
- data/examples/bank/bank_test.rb +7 -0
- data/examples/exception.rb +40 -0
- data/examples/fib/conservative.rb +50 -0
- data/examples/fib/future.rb +5 -0
- data/examples/fib/original.rb +33 -0
- data/examples/fib/threads.rb +51 -0
- data/examples/im/distributed/chat/client.rb +49 -0
- data/examples/im/distributed/chat/screen/fox.rb +92 -0
- data/examples/im/distributed/chat/screen.rb +11 -0
- data/examples/im/distributed/chat/server.rb +72 -0
- data/examples/im/distributed/chat.rb +5 -0
- data/examples/im/distributed/client.rb +9 -0
- data/examples/im/distributed/run.rb +18 -0
- data/examples/im/distributed/server.rb +11 -0
- data/examples/im/single/chat/client.rb +50 -0
- data/examples/im/single/chat/screen/fox.rb +96 -0
- data/examples/im/single/chat/screen/wxs.rb +63 -0
- data/examples/im/single/chat/screen.rb +11 -0
- data/examples/im/single/chat/server.rb +72 -0
- data/examples/im/single/chat.rb +5 -0
- data/examples/im/single/fox.rb +18 -0
- data/examples/im/single/wxchat.rb +19 -0
- data/examples/pingpong/actor.rb +33 -0
- data/examples/pingpong/actor_rec.rb +34 -0
- data/examples/pingpong/pingpong.txt +315 -0
- data/examples/pingpong/scala.rb +41 -0
- data/examples/pingpong/serial.rb +26 -0
- data/examples/pretty.txt +108 -0
- data/examples/telephone/.irbrc +2 -0
- data/examples/telephone/3esl.txt +21877 -0
- data/examples/telephone/fifth/kid.rb +36 -0
- data/examples/telephone/fifth/run.rb +26 -0
- data/examples/telephone/first/kid.rb +31 -0
- data/examples/telephone/first/run.rb +20 -0
- data/examples/telephone/fourth/kid.rb +31 -0
- data/examples/telephone/fourth/run.rb +26 -0
- data/examples/telephone/mangler.rb +53 -0
- data/examples/telephone/second/kid.rb +26 -0
- data/examples/telephone/second/run.rb +20 -0
- data/examples/telephone/seventh/kid.rb +40 -0
- data/examples/telephone/seventh/run.rb +35 -0
- data/examples/telephone/seventh/test.rb +28 -0
- data/examples/telephone/seventh/test2.rb +10 -0
- data/examples/telephone/sixth/kid.rb +39 -0
- data/examples/telephone/sixth/run.rb +26 -0
- data/examples/telephone/third/kid.rb +31 -0
- data/examples/telephone/third/run.rb +21 -0
- data/lib/dramatis/actor/interface.rb +118 -0
- data/lib/dramatis/actor/name/interface.rb +128 -0
- data/lib/dramatis/actor/name.rb +44 -0
- data/lib/dramatis/actor.rb +96 -0
- data/lib/dramatis/deadlock.rb +123 -0
- data/lib/dramatis/error/uncaught.rb +19 -0
- data/lib/dramatis/error.rb +125 -0
- data/lib/dramatis/future/interface.rb +45 -0
- data/lib/dramatis/future.rb +32 -0
- data/lib/dramatis/runtime/actor/main.rb +3 -0
- data/lib/dramatis/runtime/actor.rb +294 -0
- data/lib/dramatis/runtime/gate.rb +244 -0
- data/lib/dramatis/runtime/scheduler.rb +374 -0
- data/lib/dramatis/runtime/task.rb +390 -0
- data/lib/dramatis/runtime/thread_pool.rb +149 -0
- data/lib/dramatis/runtime/timer.rb +5 -0
- data/lib/dramatis/runtime.rb +129 -0
- data/lib/dramatis/shoes/runtime.rb +7 -0
- data/lib/dramatis/shoes.rb +14 -0
- data/lib/dramatis/version.rb +8 -0
- data/lib/dramatis.rb +73 -0
- data/log/debug.log +0 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/script/txt2html +74 -0
- data/setup.rb +1585 -0
- data/spec/dramatis/actor/become_spec.rb +17 -0
- data/spec/dramatis/actor/future_spec.rb +189 -0
- data/spec/dramatis/actor/name_spec.rb +141 -0
- data/spec/dramatis/actor/task_spec.rb +75 -0
- data/spec/dramatis/actor_spec.rb +492 -0
- data/spec/dramatis/dramatis_spec.rb +23 -0
- data/spec/dramatis/exc_spec.rb +78 -0
- data/spec/dramatis/runtime/gate_spec.rb +57 -0
- data/spec/dramatis/runtime/thread_pool.rb +30 -0
- data/spec/dramatis/shoes_spec.rb +11 -0
- data/spec/dramatis/simple_spec.rb +32 -0
- data/spec/exp_spec.rb +21 -0
- data/spec/simple2_spec.rb +36 -0
- data/spec/simple_spec.rb +30 -0
- data/spec/spec.opts +0 -0
- data/spec/spec_helper.rb +26 -0
- data/spec/thread_spec.rb +13 -0
- data/tasks/deployment.rake +34 -0
- data/tasks/environment.rake +7 -0
- data/tasks/rspec.rake +21 -0
- data/tasks/website.rake +17 -0
- data/test/jruby_lm.rb +13 -0
- data/test/test.rb +19 -0
- data/test/test10.rb +43 -0
- data/test/test11.rb +45 -0
- data/test/test12.rb +60 -0
- data/test/test13.rb +71 -0
- data/test/test2.rb +12 -0
- data/test/test3.rb +10 -0
- data/test/test4.rb +29 -0
- data/test/test5.rb +8 -0
- data/test/test6.rb +32 -0
- data/test/test7.rb +48 -0
- data/test/test8.rb +133 -0
- data/test/test9.rb +105 -0
- data/test/test_exc.rb +22 -0
- metadata +180 -0
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
module Dramatis; end
|
|
2
|
+
class Dramatis::Runtime; end
|
|
3
|
+
|
|
4
|
+
require 'thread'
|
|
5
|
+
|
|
6
|
+
require 'dramatis/runtime/thread_pool'
|
|
7
|
+
require 'dramatis/runtime/actor/main'
|
|
8
|
+
require 'dramatis/runtime'
|
|
9
|
+
require 'dramatis'
|
|
10
|
+
|
|
11
|
+
begin require 'pp'; rescue Exception; end
|
|
12
|
+
|
|
13
|
+
class Dramatis::Runtime::Scheduler #:nodoc: all
|
|
14
|
+
|
|
15
|
+
def checkio; false; end
|
|
16
|
+
|
|
17
|
+
@@thread_pools = []
|
|
18
|
+
|
|
19
|
+
def self.reset
|
|
20
|
+
@@thread_pools.each do |thread_pool|
|
|
21
|
+
thread_pool.reset
|
|
22
|
+
end
|
|
23
|
+
@@current.reset
|
|
24
|
+
@@current = nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
@@current = nil
|
|
28
|
+
|
|
29
|
+
def self.current
|
|
30
|
+
@@current ||= self.new
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def reset
|
|
34
|
+
# pp @suspended_continuations
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def schedule task
|
|
38
|
+
@mutex.synchronize do
|
|
39
|
+
# warn "sched #{@queue.length} #{@state} #{task}"
|
|
40
|
+
begin
|
|
41
|
+
raise "bad bad bad" if task == nil
|
|
42
|
+
rescue Exception => e
|
|
43
|
+
p "very bad very #{e}"
|
|
44
|
+
pp e.backtrace
|
|
45
|
+
raise e
|
|
46
|
+
end
|
|
47
|
+
@queue << task
|
|
48
|
+
if @queue.length == 1
|
|
49
|
+
if @state == :waiting
|
|
50
|
+
@wait.signal
|
|
51
|
+
elsif @state == :idle
|
|
52
|
+
@state = :running
|
|
53
|
+
@running_threads = 1
|
|
54
|
+
checkio and warn "#{Thread.current} checkout main #{Thread.main} #{@running_threads}"
|
|
55
|
+
@thread = Thread.new { run }
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# must be called with @mutex locked
|
|
62
|
+
# must be called after @running_threads decremented
|
|
63
|
+
def maybe_deadlock
|
|
64
|
+
# warn "maybe_deadlock #{Thread.current} #{Thread.main} threads #{@running_threads} queue #{@queue.length} #{Thread.list.join(" ")} qg #{@quiescing} scl #{@suspended_continuations.length}"
|
|
65
|
+
if @running_threads == 0 and @queue.length == 0 and @suspended_continuations.length > 0 and !@quiescing
|
|
66
|
+
# p "DEADLOCK!"
|
|
67
|
+
raise Dramatis::Deadlock.new
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def suspend_notification continuation
|
|
72
|
+
@mutex.synchronize do
|
|
73
|
+
if @state == :idle
|
|
74
|
+
@state = :running
|
|
75
|
+
@running_threads = 1
|
|
76
|
+
checkio and warn "#{Thread.current} checkout -1 #{Thread.main} #{@running_threads}"
|
|
77
|
+
@thread = Thread.new { run }
|
|
78
|
+
end
|
|
79
|
+
checkio and warn "#{Thread.current} checkin 0 #{Thread.main} #{@running_threads}"
|
|
80
|
+
@running_threads -= 1
|
|
81
|
+
begin
|
|
82
|
+
raise "cane" if @running_threads < 0
|
|
83
|
+
rescue ::Exception => e
|
|
84
|
+
pp e.backtrace
|
|
85
|
+
end
|
|
86
|
+
if @state == :waiting
|
|
87
|
+
@wait.signal
|
|
88
|
+
end
|
|
89
|
+
@suspended_continuations[continuation.to_s] = continuation
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def wakeup_notification continuation
|
|
94
|
+
@mutex.synchronize do
|
|
95
|
+
@suspended_continuations.delete continuation.to_s
|
|
96
|
+
@running_threads += 1
|
|
97
|
+
checkio and warn "#{Thread.current} checkout #{@running_threads}"
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def quiesce
|
|
102
|
+
Dramatis::Runtime::Actor::Main.current.quiesce
|
|
103
|
+
main_at_exit true
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def main_at_exit quiescing = false
|
|
107
|
+
# warn "quiescing" if quiescing
|
|
108
|
+
# warn "main has exited: waiting" if !quiescing
|
|
109
|
+
@mutex.synchronize do
|
|
110
|
+
@quiescing = quiescing
|
|
111
|
+
checkio and warn "#{Thread.current} main maybe checkin 1 #{@running_threads} #{@state} #{@main_state} #{quiescing}"
|
|
112
|
+
if @state != :idle
|
|
113
|
+
@running_threads -= 1
|
|
114
|
+
if @state == :waiting
|
|
115
|
+
@wait.signal
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
raise "hell #{@main_state.to_s}" if @main_state != :running and @main_state != :may_finish
|
|
120
|
+
checkio and warn "#{Thread.current} main signaled #{@running_threads} #{@state} #{@main_state} #{quiescing}"
|
|
121
|
+
|
|
122
|
+
@main_mutex.synchronize do
|
|
123
|
+
if @main_state == :running
|
|
124
|
+
if @state != :idle
|
|
125
|
+
@main_state = :waiting
|
|
126
|
+
begin
|
|
127
|
+
@main_wait.wait @mutex
|
|
128
|
+
rescue Exception => e
|
|
129
|
+
# pp "wait said #{e}", e.backtrace
|
|
130
|
+
raise e
|
|
131
|
+
end
|
|
132
|
+
@main_join.join
|
|
133
|
+
@main_join = nil
|
|
134
|
+
raise "hell #{@main_state.to_s}" if @main_state != :may_finish
|
|
135
|
+
else
|
|
136
|
+
begin
|
|
137
|
+
maybe_deadlock
|
|
138
|
+
rescue Dramatis::Deadlock => deadlock
|
|
139
|
+
warn "Deadlock at exit: uncompleted tasks exist"
|
|
140
|
+
raise deadlock
|
|
141
|
+
end
|
|
142
|
+
@main_state = :may_finish
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
@main_state = :running if @quiescing
|
|
146
|
+
end
|
|
147
|
+
@quiescing = false
|
|
148
|
+
end
|
|
149
|
+
raise "hell #{@main_state.to_s}" if @main_state != :may_finish and @main_state != :running
|
|
150
|
+
# warn "?threads? #{Thread.list.join(' ')}"
|
|
151
|
+
# warn "main has exited: done"
|
|
152
|
+
@thread_pool.reset()
|
|
153
|
+
Dramatis::Runtime.current.maybe_raise_exceptions quiescing
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def << actor
|
|
157
|
+
@actors << actor
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def thread_count
|
|
161
|
+
@mutex.synchronize do
|
|
162
|
+
@@thread_pools.inject(0) { |a,b| a+b.length }
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
def initialize
|
|
169
|
+
@thread_pool = Dramatis::Runtime::ThreadPool.new
|
|
170
|
+
@@thread_pools << @thread_pool
|
|
171
|
+
# Thread.abort_on_exception = true
|
|
172
|
+
@mutex = Mutex.new
|
|
173
|
+
@wait = ConditionVariable.new
|
|
174
|
+
@running_threads = 0
|
|
175
|
+
@suspended_continuations = {}
|
|
176
|
+
@queue = []
|
|
177
|
+
@state = :idle
|
|
178
|
+
|
|
179
|
+
@main_mutex = Mutex.new
|
|
180
|
+
@main_wait = ConditionVariable.new
|
|
181
|
+
@main_state = :running
|
|
182
|
+
@quiescing = false
|
|
183
|
+
|
|
184
|
+
@thread = nil
|
|
185
|
+
|
|
186
|
+
@actors = []
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
class Done < ::Exception
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def run
|
|
193
|
+
|
|
194
|
+
checkio and warn "#{Thread.current} scheduler starting #{@state}"
|
|
195
|
+
|
|
196
|
+
begin
|
|
197
|
+
while true
|
|
198
|
+
@mutex.synchronize do
|
|
199
|
+
# warn "qe #{@queue.empty?} tr '#{@running_threads}'"
|
|
200
|
+
while @queue.empty? and @running_threads != 0
|
|
201
|
+
@state = :waiting
|
|
202
|
+
begin
|
|
203
|
+
raise "hell" if @running_threads == 0
|
|
204
|
+
@wait.wait @mutex
|
|
205
|
+
rescue Exception => exception
|
|
206
|
+
warn "wait exception: #{exception}"
|
|
207
|
+
ensure
|
|
208
|
+
@state = :running
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
begin
|
|
214
|
+
@mutex.synchronize { maybe_deadlock }
|
|
215
|
+
rescue Dramatis::Deadlock => deadlock
|
|
216
|
+
actors = @mutex.synchronize { @actors.dup }
|
|
217
|
+
thread = Thread.current
|
|
218
|
+
actors.each do |actor|
|
|
219
|
+
thread[:dramatis_actor] = actor.name
|
|
220
|
+
actor.deadlock deadlock
|
|
221
|
+
end
|
|
222
|
+
thread[:dramatis_actor] = nil
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
@mutex.synchronize { maybe_deadlock }
|
|
226
|
+
|
|
227
|
+
@mutex.synchronize do
|
|
228
|
+
|
|
229
|
+
raise Done if @queue.empty? and @running_threads == 0
|
|
230
|
+
|
|
231
|
+
if @queue.length > 0
|
|
232
|
+
|
|
233
|
+
task = @queue.shift
|
|
234
|
+
|
|
235
|
+
# p "task #{task}"
|
|
236
|
+
|
|
237
|
+
begin
|
|
238
|
+
raise "hell!!!!" if task == nil
|
|
239
|
+
rescue Exception => e
|
|
240
|
+
p "bad bad #{e}"
|
|
241
|
+
pp e.backtrace
|
|
242
|
+
raise e
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
@running_threads += 1
|
|
246
|
+
|
|
247
|
+
# p "tasket #{task}"
|
|
248
|
+
|
|
249
|
+
@thread_pool.new do
|
|
250
|
+
|
|
251
|
+
# "tasky", task
|
|
252
|
+
|
|
253
|
+
# "tasketx #{task}"
|
|
254
|
+
|
|
255
|
+
checkio and warn "#{Thread.current} spining up #{@running_threads}"
|
|
256
|
+
begin
|
|
257
|
+
deliver task
|
|
258
|
+
rescue Exception => e
|
|
259
|
+
warn "unexptected deliver error #{e}"
|
|
260
|
+
raise e
|
|
261
|
+
ensure
|
|
262
|
+
@mutex.synchronize do
|
|
263
|
+
checkio and warn "#{Thread.current} checkin 2 #{@running_threads} #{@state}"
|
|
264
|
+
@running_threads -= 1
|
|
265
|
+
if @state == :waiting
|
|
266
|
+
@wait.signal
|
|
267
|
+
else
|
|
268
|
+
# maybe_deadlock
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
end
|
|
272
|
+
checkio and warn "#{Thread.current} retiring #{@running_threads}"
|
|
273
|
+
end
|
|
274
|
+
end
|
|
275
|
+
end
|
|
276
|
+
end
|
|
277
|
+
# warn "after loop"
|
|
278
|
+
rescue Done
|
|
279
|
+
rescue Exception => exception
|
|
280
|
+
warn "1 *? exception " + exception.to_s
|
|
281
|
+
warn "smp!!! " + exception.backtrace.join("\n")
|
|
282
|
+
# pp exception.backtrace
|
|
283
|
+
Dramatis::Runtime.current.exception exception
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
checkio and warn "scheduler giving up the ghost #{@queue.length} #{Thread.current}"
|
|
287
|
+
|
|
288
|
+
begin
|
|
289
|
+
@mutex.synchronize do
|
|
290
|
+
maybe_deadlock
|
|
291
|
+
end
|
|
292
|
+
rescue Dramatis::Deadlock => deadlock
|
|
293
|
+
actors = @mutex.synchronize { @actors.dup }
|
|
294
|
+
actors.each { |actor| actor.deadlock deadlock }
|
|
295
|
+
rescue Exception => exception
|
|
296
|
+
warn "2 exception " + exception.to_s
|
|
297
|
+
Dramatis::Runtime.current.exception exception
|
|
298
|
+
end
|
|
299
|
+
|
|
300
|
+
checkio and warn "scheduler giving up after final deadlock check #{@queue.length} #{Thread.current}"
|
|
301
|
+
|
|
302
|
+
# FIX need to check all the mutex nesting between main_mutex and mutex
|
|
303
|
+
# I think this should be the main mutex ...
|
|
304
|
+
@mutex.synchronize do
|
|
305
|
+
raise "hell #{@main_state.to_s}" if @main_state != :running and @main_state != :waiting
|
|
306
|
+
state = @main_state
|
|
307
|
+
@main_state = :may_finish
|
|
308
|
+
# warn "main is #{state}"
|
|
309
|
+
if state == :waiting
|
|
310
|
+
@main_join = Thread.current
|
|
311
|
+
@main_wait.signal
|
|
312
|
+
end
|
|
313
|
+
# should this be synchronized?
|
|
314
|
+
# if there is more than one main (non-dramatis) thread ... well, I'm not sure
|
|
315
|
+
# how protected we are for that around shutdown/startup
|
|
316
|
+
# I guess it won't hurt, but I'm not sure it helps
|
|
317
|
+
# @mutex.synchronize do
|
|
318
|
+
# warn "s #{Thread.current} m #{Thread.main} l #{Thread.list.join(' ')} "
|
|
319
|
+
raise "hell" if @queue.length > 0
|
|
320
|
+
@state = :idle
|
|
321
|
+
@thread = nil
|
|
322
|
+
# end
|
|
323
|
+
end
|
|
324
|
+
|
|
325
|
+
checkio and warn "#{Thread.current} scheduler ending"
|
|
326
|
+
|
|
327
|
+
end
|
|
328
|
+
|
|
329
|
+
def deliver task
|
|
330
|
+
thread = Thread.current
|
|
331
|
+
begin
|
|
332
|
+
raise "hel!!!" if !task
|
|
333
|
+
rescue Exception => e
|
|
334
|
+
p "very bad!! #{e}"
|
|
335
|
+
pp e.backtrace
|
|
336
|
+
raise e
|
|
337
|
+
end
|
|
338
|
+
thread[:dramatis_actor] = task.actor.name
|
|
339
|
+
xx = task.actor.name
|
|
340
|
+
xx = xx.instance_eval { @actor }
|
|
341
|
+
# warn "assign #{xx} to #{thread}"
|
|
342
|
+
begin
|
|
343
|
+
# warn "deliver " + task.inspect
|
|
344
|
+
task.deliver
|
|
345
|
+
# warn "delivered " + task.inspect
|
|
346
|
+
rescue Exception => exception
|
|
347
|
+
warn "2 exception " + exception.to_s
|
|
348
|
+
# pp exception.backtrace
|
|
349
|
+
Dramatis::Runtime.current.exception exception
|
|
350
|
+
ensure
|
|
351
|
+
thread[:dramatis_actor] = nil
|
|
352
|
+
end
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
def self.actor
|
|
356
|
+
thread = Thread.current
|
|
357
|
+
actor = thread[:dramatis_actor]
|
|
358
|
+
if !actor
|
|
359
|
+
if thread == Thread.main
|
|
360
|
+
# p "here", actor
|
|
361
|
+
actor = Dramatis::Runtime::Actor::Main.current.name
|
|
362
|
+
# p "there", actor
|
|
363
|
+
end
|
|
364
|
+
else
|
|
365
|
+
# this is a debugging path; can go away
|
|
366
|
+
raise "hell" if thread == Thread.main and
|
|
367
|
+
actor != Dramatis::Runtime::Actor::Main.current
|
|
368
|
+
raise "hell" if thread != Thread.main and
|
|
369
|
+
actor == Dramatis::Runtime::Actor::Main.current
|
|
370
|
+
end
|
|
371
|
+
actor
|
|
372
|
+
end
|
|
373
|
+
|
|
374
|
+
end
|