dramatis 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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,390 @@
|
|
1
|
+
module Dramatis; end
|
2
|
+
class Dramatis::Runtime; end
|
3
|
+
|
4
|
+
require 'dramatis/runtime/scheduler'
|
5
|
+
require 'dramatis/future'
|
6
|
+
require 'thread'
|
7
|
+
|
8
|
+
class Dramatis::Runtime::Task #:nodoc: all
|
9
|
+
|
10
|
+
attr_reader :actor
|
11
|
+
|
12
|
+
def type
|
13
|
+
@dest
|
14
|
+
end
|
15
|
+
|
16
|
+
def method
|
17
|
+
@args[0]
|
18
|
+
end
|
19
|
+
|
20
|
+
def arguments
|
21
|
+
@args[1,@args.length]
|
22
|
+
end
|
23
|
+
|
24
|
+
attr_reader :call_thread
|
25
|
+
|
26
|
+
def initialize actor, dest, args, options
|
27
|
+
@actor = actor
|
28
|
+
@dest = dest
|
29
|
+
@args = args.dup
|
30
|
+
|
31
|
+
@call_thread = nil
|
32
|
+
|
33
|
+
name = Dramatis::Runtime::Scheduler.actor
|
34
|
+
actor = name.instance_eval { @actor }
|
35
|
+
|
36
|
+
object_id = actor.object.object_id
|
37
|
+
|
38
|
+
@args.each_with_index do |arg,i|
|
39
|
+
if arg.object_id == object_id
|
40
|
+
@args[i] = name
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if actor.call_threading?
|
45
|
+
# warn "oct #{options[:call_thread]} act #{actor.call_thread}"
|
46
|
+
raise "hell" if options[:call_thread] and
|
47
|
+
actor.call_thread and
|
48
|
+
options[:call_thread] != actor.call_thread
|
49
|
+
@call_thread = actor.call_thread
|
50
|
+
if @call_thread == nil
|
51
|
+
@call_thread = self.to_s
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# warn "task #{self} #{args[0]} call thread [ #{@call_thread} ] #{options.to_a.join(' ')}"
|
56
|
+
|
57
|
+
case options[:continuation]
|
58
|
+
when :none
|
59
|
+
@continuation = Continuation::None.new name, @call_thread
|
60
|
+
when :rpc
|
61
|
+
@continuation = Continuation::RPC.new name, @call_thread, options[:nonblocking]
|
62
|
+
when :future
|
63
|
+
@continuation = Continuation::Future.new name, @call_thread
|
64
|
+
when Proc
|
65
|
+
@continuation = Continuation::Proc.new name, @call_thread, options[:continuation],
|
66
|
+
options[:exception]
|
67
|
+
else
|
68
|
+
raise Dramatis::Internal.new( "invalid contiunation type" )
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def exception e
|
73
|
+
@continuation.exception e
|
74
|
+
end
|
75
|
+
|
76
|
+
def queued
|
77
|
+
@continuation.queued
|
78
|
+
end
|
79
|
+
|
80
|
+
def deliver
|
81
|
+
@actor.deliver @dest, @args, @continuation, @call_thread
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
module Continuation
|
87
|
+
|
88
|
+
class None
|
89
|
+
|
90
|
+
include Dramatis
|
91
|
+
|
92
|
+
def queued
|
93
|
+
end
|
94
|
+
|
95
|
+
def result result
|
96
|
+
end
|
97
|
+
|
98
|
+
def exception exception
|
99
|
+
# warn "except nil #{exception}"
|
100
|
+
# true and pp exception.backtrace
|
101
|
+
interface( release( @name ) ).exception exception
|
102
|
+
end
|
103
|
+
|
104
|
+
def initialize name, call_thread
|
105
|
+
@name = name
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
class RPC
|
110
|
+
|
111
|
+
include Dramatis
|
112
|
+
|
113
|
+
def actor
|
114
|
+
@actor.instance_eval { @actor }
|
115
|
+
end
|
116
|
+
|
117
|
+
def initialize name, call_thread, nonblocking
|
118
|
+
# all the synchronizaton here probably gets tossed
|
119
|
+
# proof:
|
120
|
+
# to create the continuation, you have to have the actor lock
|
121
|
+
# to deliver the continuation you have to have the actor lock
|
122
|
+
# QED
|
123
|
+
# when this wasn't a message send, you could try to execute before
|
124
|
+
# but now that's not possible; it'll get queued
|
125
|
+
# i think
|
126
|
+
# of course, it should be harmless
|
127
|
+
# fix ... might want to seperate the two parts of a continuation
|
128
|
+
# the value part and the thread state part
|
129
|
+
@blocking = !nonblocking
|
130
|
+
@state = :start
|
131
|
+
@mutex = Mutex.new
|
132
|
+
@wait = ConditionVariable.new
|
133
|
+
@call_thread = call_thread
|
134
|
+
# warn "contiunation to #{actor}"
|
135
|
+
@actor = interface( Dramatis::Runtime::Scheduler.actor ).send :continuation, self, :call_thread => call_thread
|
136
|
+
end
|
137
|
+
|
138
|
+
def queued
|
139
|
+
|
140
|
+
@mutex.synchronize do
|
141
|
+
raise "hell " + @state.to_s if @state != :start and @state != :signaled
|
142
|
+
if @state == :start
|
143
|
+
@state = :waiting
|
144
|
+
begin
|
145
|
+
tag = to_s
|
146
|
+
call_thread = @call_thread
|
147
|
+
blocking = @blocking
|
148
|
+
@actor.instance_eval do
|
149
|
+
@actor.instance_eval do
|
150
|
+
# warn "#{self} ct [ #{call_thread} ]"
|
151
|
+
@call_thread = call_thread
|
152
|
+
end
|
153
|
+
if blocking
|
154
|
+
@actor.gate.only [ :continuation, tag ], :tag => tag
|
155
|
+
end
|
156
|
+
@actor.schedule self
|
157
|
+
end
|
158
|
+
begin
|
159
|
+
Dramatis::Runtime::Scheduler.current.suspend_notification self
|
160
|
+
@wait.wait @mutex
|
161
|
+
# this causes a deadlock if the waking thread, which may be
|
162
|
+
# retiring, does so before this thead has awakend and notified
|
163
|
+
# the scheduler
|
164
|
+
# sleep 1
|
165
|
+
ensure
|
166
|
+
# Dramatis::Runtime::Scheduler.current.wakeup_notification self
|
167
|
+
end
|
168
|
+
ensure
|
169
|
+
tag = to_s
|
170
|
+
@actor.instance_eval do
|
171
|
+
@actor.gate.default_by_tag tag
|
172
|
+
end
|
173
|
+
end
|
174
|
+
raise "hell" if @state != :done
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
raise "hell " + @type.inspect if ![ :return, :exception ].include? @type
|
179
|
+
case @type
|
180
|
+
when :return
|
181
|
+
return @value
|
182
|
+
when :exception
|
183
|
+
# if Dramatis::Deadlock === @value
|
184
|
+
# @value = Dramatis::Deadlock.new nil, :next => @value
|
185
|
+
# end
|
186
|
+
# pp "reraise", caller
|
187
|
+
@value._dramatis_reraise
|
188
|
+
raise @value
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def result result
|
193
|
+
@actor.result result
|
194
|
+
end
|
195
|
+
|
196
|
+
def exception exception
|
197
|
+
# warn "4 exception " + exception.to_s
|
198
|
+
# warn "4 exception " + exception.backtrace.join("\n")
|
199
|
+
@actor.exception exception
|
200
|
+
# warn "4 delivered ".to_s
|
201
|
+
end
|
202
|
+
|
203
|
+
def continuation_result result
|
204
|
+
@mutex.synchronize do
|
205
|
+
raise "hell" if @state != :start and @state != :waiting
|
206
|
+
@type = :return
|
207
|
+
@value = result
|
208
|
+
if @state == :start
|
209
|
+
@state = :signaled
|
210
|
+
else
|
211
|
+
@state = :done
|
212
|
+
Dramatis::Runtime::Scheduler.current.wakeup_notification self
|
213
|
+
@wait.signal
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
|
218
|
+
def continuation_exception exception
|
219
|
+
# warn "except rpc"
|
220
|
+
@mutex.synchronize do
|
221
|
+
raise "hell" if @state != :start and @state != :waiting
|
222
|
+
@type = :exception
|
223
|
+
@value = exception
|
224
|
+
if @state == :start
|
225
|
+
@state = :signaled
|
226
|
+
else
|
227
|
+
@state = :done
|
228
|
+
Dramatis::Runtime::Scheduler.current.wakeup_notification self
|
229
|
+
@wait.signal
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
class Proc
|
237
|
+
|
238
|
+
include Dramatis
|
239
|
+
|
240
|
+
def initialize name, call_thread, result, except
|
241
|
+
# p "p.n #{call_thread} #{result} #{except}"
|
242
|
+
@result_block = result
|
243
|
+
@exception_block = except
|
244
|
+
@name = name
|
245
|
+
@continuation = \
|
246
|
+
interface( Dramatis::Runtime::Scheduler.actor ) \
|
247
|
+
.send :continuation, self, :call_thread => call_thread
|
248
|
+
end
|
249
|
+
|
250
|
+
def queued; end
|
251
|
+
|
252
|
+
def result result
|
253
|
+
@continuation.result result
|
254
|
+
end
|
255
|
+
|
256
|
+
def exception exception
|
257
|
+
@continuation.exception exception
|
258
|
+
end
|
259
|
+
|
260
|
+
def continuation_result result
|
261
|
+
@result_block.call result
|
262
|
+
end
|
263
|
+
|
264
|
+
def continuation_exception exception
|
265
|
+
# warn "delivering #{exception} => #{@exception_block}"
|
266
|
+
# pp exception.backtrace
|
267
|
+
if @exception_block
|
268
|
+
@exception_block.call exception
|
269
|
+
else
|
270
|
+
release( @name ).dramatis_exception exception
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
end
|
275
|
+
|
276
|
+
class Future
|
277
|
+
|
278
|
+
include Dramatis
|
279
|
+
|
280
|
+
def initialize name, call_thread
|
281
|
+
@state = :start
|
282
|
+
@mutex = Mutex.new
|
283
|
+
@wait = ConditionVariable.new
|
284
|
+
@call_thread = call_thread
|
285
|
+
# warn "contiunation to #{actor}"
|
286
|
+
@actor = interface( Dramatis::Runtime::Scheduler.actor ) \
|
287
|
+
.send :continuation, self, :call_thread => call_thread
|
288
|
+
end
|
289
|
+
|
290
|
+
def ready?
|
291
|
+
@mutex.synchronize { @state == :done or @state == :signaled }
|
292
|
+
end
|
293
|
+
|
294
|
+
def value
|
295
|
+
@mutex.synchronize do
|
296
|
+
if @state == :start
|
297
|
+
@state = :waiting
|
298
|
+
begin
|
299
|
+
tag = to_s
|
300
|
+
call_thread = @call_thread
|
301
|
+
@actor.instance_eval do
|
302
|
+
@actor.instance_eval do
|
303
|
+
@call_thread = call_thread
|
304
|
+
end
|
305
|
+
@actor.gate.only [ :continuation, tag ], :tag => tag
|
306
|
+
@actor.schedule self
|
307
|
+
end
|
308
|
+
begin
|
309
|
+
Dramatis::Runtime::Scheduler.current.suspend_notification self
|
310
|
+
@wait.wait @mutex
|
311
|
+
# this causes a deadlock if the waking thread, which may be
|
312
|
+
# retiring, does so before this thead has awakend and notified
|
313
|
+
# the scheduler
|
314
|
+
# sleep 1
|
315
|
+
ensure
|
316
|
+
# Dramatis::Runtime::Scheduler.current.wakeup_notification self
|
317
|
+
end
|
318
|
+
ensure
|
319
|
+
tag = to_s
|
320
|
+
@actor.instance_eval do
|
321
|
+
@actor.gate.default_by_tag tag
|
322
|
+
end
|
323
|
+
end
|
324
|
+
raise "hell" if @state != :done
|
325
|
+
end
|
326
|
+
end
|
327
|
+
|
328
|
+
raise "hell " + @type.inspect if ![ :return, :exception ].include? @type
|
329
|
+
case @type
|
330
|
+
when :return
|
331
|
+
return @value
|
332
|
+
when :exception
|
333
|
+
begin
|
334
|
+
# raise "hell for #{@value}"
|
335
|
+
rescue Exception => e
|
336
|
+
pp "#{e}", e.backtrace
|
337
|
+
end
|
338
|
+
raise @value
|
339
|
+
end
|
340
|
+
end
|
341
|
+
|
342
|
+
def queued
|
343
|
+
Dramatis::Future.new( self )
|
344
|
+
end
|
345
|
+
|
346
|
+
def result result
|
347
|
+
@actor.result result
|
348
|
+
end
|
349
|
+
|
350
|
+
def exception exception
|
351
|
+
# warn "4 exception " + exception.to_s
|
352
|
+
# warn "4 exception " + exception.backtrace.join("\n")
|
353
|
+
@actor.exception exception
|
354
|
+
# warn "4 delivered ".to_s
|
355
|
+
end
|
356
|
+
|
357
|
+
def continuation_result result
|
358
|
+
@mutex.synchronize do
|
359
|
+
raise "hell" if @state != :start and @state != :waiting
|
360
|
+
@type = :return
|
361
|
+
@value = result
|
362
|
+
if @state == :start
|
363
|
+
@state = :signaled
|
364
|
+
else
|
365
|
+
@state = :done
|
366
|
+
Dramatis::Runtime::Scheduler.current.wakeup_notification self
|
367
|
+
@wait.signal
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
371
|
+
|
372
|
+
def continuation_exception exception
|
373
|
+
# warn "except rpc"
|
374
|
+
@mutex.synchronize do
|
375
|
+
raise "hell" if @state != :start and @state != :waiting
|
376
|
+
@type = :exception
|
377
|
+
@value = exception
|
378
|
+
if @state == :start
|
379
|
+
@state = :signaled
|
380
|
+
else
|
381
|
+
@state = :done
|
382
|
+
Dramatis::Runtime::Scheduler.current.wakeup_notification self
|
383
|
+
@wait.signal
|
384
|
+
end
|
385
|
+
end
|
386
|
+
end
|
387
|
+
end
|
388
|
+
end
|
389
|
+
|
390
|
+
end
|
@@ -0,0 +1,149 @@
|
|
1
|
+
module Dramatis; end
|
2
|
+
class Dramatis::Runtime; end
|
3
|
+
|
4
|
+
class Dramatis::Runtime::ThreadPool; end
|
5
|
+
class Dramatis::Runtime::ThreadPool::PoolThread < Thread; end
|
6
|
+
|
7
|
+
class Dramatis::Runtime::ThreadPool #:nodoc: all
|
8
|
+
|
9
|
+
def reset
|
10
|
+
shutdown
|
11
|
+
@state = :running
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize
|
15
|
+
super
|
16
|
+
@mutex = Mutex.new
|
17
|
+
@threads = []
|
18
|
+
@state = :running
|
19
|
+
end
|
20
|
+
|
21
|
+
def new &block
|
22
|
+
checkout( &block )
|
23
|
+
end
|
24
|
+
|
25
|
+
def length
|
26
|
+
@mutex.synchronize do
|
27
|
+
@threads.length
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
|
33
|
+
def shutdown
|
34
|
+
@mutex.synchronize do
|
35
|
+
@state = :exiting
|
36
|
+
@threads.each do |thread|
|
37
|
+
thread.send :shutdown
|
38
|
+
end
|
39
|
+
@threads.each do |thread|
|
40
|
+
thread.true_join
|
41
|
+
end
|
42
|
+
@threads = []
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def checkin thread
|
47
|
+
@mutex.synchronize do
|
48
|
+
if @state != :exiting
|
49
|
+
@threads << thread
|
50
|
+
else
|
51
|
+
warn("POST EXIT CHECKIN")
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def checkout &block
|
57
|
+
raise "hell" if @state == :exiting
|
58
|
+
t = nil
|
59
|
+
@mutex.synchronize do
|
60
|
+
if @threads.length == 0
|
61
|
+
pt = PoolThread.new self
|
62
|
+
@threads << pt
|
63
|
+
end
|
64
|
+
t = @threads.pop
|
65
|
+
end
|
66
|
+
t.send :awaken, &block
|
67
|
+
t
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
|
72
|
+
class Dramatis::Runtime::ThreadPool::PoolThread < Thread
|
73
|
+
|
74
|
+
def initialize pool
|
75
|
+
@pool = pool
|
76
|
+
@mutex = Mutex.new
|
77
|
+
@wait = ConditionVariable.new
|
78
|
+
@state = :running
|
79
|
+
super() do
|
80
|
+
self.abort_on_exception = true
|
81
|
+
target
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
alias true_join join
|
86
|
+
|
87
|
+
def join
|
88
|
+
# I've thought about this. It would be cool to implement join. It's not
|
89
|
+
# too hard to do it when threads aren't reused ... which is kinda dumb.
|
90
|
+
# It's possible to do it when threads are reused, by coding an allocation
|
91
|
+
# counter in the "thread" object (but not the native thread). It'd be
|
92
|
+
# cool, but I don't need it, so, oh well.
|
93
|
+
raise "not implemented"
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def shutdown
|
99
|
+
@mutex.synchronize do
|
100
|
+
old_state, @state = @state, :exiting
|
101
|
+
# p "#{Thread.current} shutdown #{old_state} #{@state}"
|
102
|
+
if old_state == :waiting
|
103
|
+
@wait.signal
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def awaken &block
|
109
|
+
@mutex.synchronize do
|
110
|
+
@block = block
|
111
|
+
old_state, @state = @state, :called
|
112
|
+
if old_state == :waiting
|
113
|
+
# p "#{Thread.current} signalling"
|
114
|
+
@wait.signal
|
115
|
+
elsif old_state == :exiting
|
116
|
+
warn("AWAKEN AFTER EXIT")
|
117
|
+
raise "AWAKEN AFTER EXIT"
|
118
|
+
elsif old_state != :running
|
119
|
+
warn("AWAKEN BAD STATE " + old_state)
|
120
|
+
raise "AWAKEN BAD STATE " + old_state
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def target
|
126
|
+
while true do
|
127
|
+
@mutex.synchronize do
|
128
|
+
if @state == :exiting
|
129
|
+
return
|
130
|
+
elsif @state == :running
|
131
|
+
@state = :waiting
|
132
|
+
@wait.wait @mutex
|
133
|
+
raise "hell" if @state == :waiting
|
134
|
+
elsif @state == :called
|
135
|
+
begin
|
136
|
+
@block.call
|
137
|
+
ensure
|
138
|
+
@state = :running
|
139
|
+
@pool.send :checkin, self
|
140
|
+
end
|
141
|
+
else
|
142
|
+
warn( "!!FAIL!! " + @state)
|
143
|
+
raise "!!FAIL!! " + @state
|
144
|
+
end
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
end
|
@@ -0,0 +1,129 @@
|
|
1
|
+
module Dramatis; end
|
2
|
+
|
3
|
+
require 'thread'
|
4
|
+
|
5
|
+
# Dramatis::Runtime is the top level class managing the running of the
|
6
|
+
# various pieces of the dramatis runtime. Typically programs don't
|
7
|
+
# need to deal with the runtime directly, though some functions are
|
8
|
+
# useful, particularly for debugging and testing.
|
9
|
+
|
10
|
+
class Dramatis::Runtime
|
11
|
+
|
12
|
+
# Returns a reference to the current Dramatis::Runtime object.
|
13
|
+
|
14
|
+
def self.current
|
15
|
+
@@current ||= self.new
|
16
|
+
end
|
17
|
+
|
18
|
+
# Resets the current runtime instance. Note that this method hard
|
19
|
+
# resets counters and ignore exceptions which is generally a bad
|
20
|
+
# idea. It is typical only used in unit test after methods to keep
|
21
|
+
# failing tests from cascading.
|
22
|
+
|
23
|
+
def self.reset
|
24
|
+
# this swallows exceptions: it's assumed to be used to clean up
|
25
|
+
# a failed test so there's no connection between tests
|
26
|
+
begin
|
27
|
+
Dramatis::Runtime.current.quiesce
|
28
|
+
rescue Exception => e
|
29
|
+
end
|
30
|
+
Dramatis::Runtime::Scheduler.reset
|
31
|
+
Dramatis::Runtime::Actor::Main.reset
|
32
|
+
@@current = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
# Causes the runtime to suspend the current thread until there are
|
36
|
+
# no more tasks that can be executed. If no tasks remain, returns
|
37
|
+
# normally. If tasks remain but are gated off,
|
38
|
+
# Dramtis::Error::Deadlock is raised.
|
39
|
+
#
|
40
|
+
# As a side effect, this method releases the current actor to
|
41
|
+
# process messages but does not change the task gate.
|
42
|
+
|
43
|
+
def quiesce
|
44
|
+
Dramatis::Runtime::Scheduler.current.quiesce
|
45
|
+
maybe_raise_exceptions true
|
46
|
+
end
|
47
|
+
|
48
|
+
def maybe_raise_exceptions quiescing #:nodoc:
|
49
|
+
@mutex.synchronize do
|
50
|
+
if !@exceptions.empty?
|
51
|
+
# warn "no maybe about it"
|
52
|
+
if !quiescing and warnings?
|
53
|
+
warn "the following #{@exceptions.length} exception(s) were raised and not caught"
|
54
|
+
@exceptions.each do |exception|
|
55
|
+
# warn "#{exception}"
|
56
|
+
# pp exception.backtrace
|
57
|
+
end
|
58
|
+
end
|
59
|
+
raise Dramatis::Error::Uncaught.new( @exceptions )
|
60
|
+
end
|
61
|
+
@exceptions.clear
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Returns the list of uncaught exceptions.
|
66
|
+
|
67
|
+
def exceptions
|
68
|
+
result = nil
|
69
|
+
@mutex.synchronize do
|
70
|
+
result = @exceptions.dup
|
71
|
+
end
|
72
|
+
result
|
73
|
+
end
|
74
|
+
|
75
|
+
# Clears the list of uncaught exceptions. Used in unit tests to
|
76
|
+
# clear expected exceptions. If exceptions are raised and not
|
77
|
+
# clear, they will be raised at the end of the program via a
|
78
|
+
# Dramatis::Error::Uncaught.
|
79
|
+
|
80
|
+
def clear_exceptions
|
81
|
+
@mutex.synchronize do
|
82
|
+
# warn "runtime clearing exceptions"
|
83
|
+
@exceptions.clear
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def exception exception #:nodoc:
|
88
|
+
@mutex.synchronize do
|
89
|
+
@exceptions << exception
|
90
|
+
warn "runtime recording exception: #{exception} [#{@exceptions.length}]" if warnings?
|
91
|
+
# backtrace
|
92
|
+
# pp exception.backtrace
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def backtrace #:nodoc:
|
97
|
+
begin
|
98
|
+
raise "backtrace"
|
99
|
+
rescue ::Exception => e
|
100
|
+
pp e.backtrace
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Enables or disables printing warnings, e.g., when uncaugh
|
105
|
+
# exceptions are detected.
|
106
|
+
|
107
|
+
def warnings= value
|
108
|
+
@warnings = value
|
109
|
+
end
|
110
|
+
|
111
|
+
# Returns true if warnings are enabled.
|
112
|
+
|
113
|
+
def warnings?
|
114
|
+
@warnings
|
115
|
+
end
|
116
|
+
|
117
|
+
def at_exit #:nodoc:
|
118
|
+
Dramatis::Runtime::Actor::Main.current.finalize
|
119
|
+
end
|
120
|
+
|
121
|
+
private
|
122
|
+
|
123
|
+
def initialize #:nodoc:
|
124
|
+
@warnings = true
|
125
|
+
@mutex = Mutex.new
|
126
|
+
@exceptions = []
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|