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.
Files changed (120) hide show
  1. data/History.txt +7 -0
  2. data/License.txt +20 -0
  3. data/Manifest.txt +119 -0
  4. data/README.txt +57 -0
  5. data/Rakefile +4 -0
  6. data/config/hoe.rb +70 -0
  7. data/config/requirements.rb +17 -0
  8. data/examples/README.txt +20 -0
  9. data/examples/auction.rb +90 -0
  10. data/examples/bank/bank.rb +7 -0
  11. data/examples/bank/bank_test.rb +7 -0
  12. data/examples/exception.rb +40 -0
  13. data/examples/fib/conservative.rb +50 -0
  14. data/examples/fib/future.rb +5 -0
  15. data/examples/fib/original.rb +33 -0
  16. data/examples/fib/threads.rb +51 -0
  17. data/examples/im/distributed/chat/client.rb +49 -0
  18. data/examples/im/distributed/chat/screen/fox.rb +92 -0
  19. data/examples/im/distributed/chat/screen.rb +11 -0
  20. data/examples/im/distributed/chat/server.rb +72 -0
  21. data/examples/im/distributed/chat.rb +5 -0
  22. data/examples/im/distributed/client.rb +9 -0
  23. data/examples/im/distributed/run.rb +18 -0
  24. data/examples/im/distributed/server.rb +11 -0
  25. data/examples/im/single/chat/client.rb +50 -0
  26. data/examples/im/single/chat/screen/fox.rb +96 -0
  27. data/examples/im/single/chat/screen/wxs.rb +63 -0
  28. data/examples/im/single/chat/screen.rb +11 -0
  29. data/examples/im/single/chat/server.rb +72 -0
  30. data/examples/im/single/chat.rb +5 -0
  31. data/examples/im/single/fox.rb +18 -0
  32. data/examples/im/single/wxchat.rb +19 -0
  33. data/examples/pingpong/actor.rb +33 -0
  34. data/examples/pingpong/actor_rec.rb +34 -0
  35. data/examples/pingpong/pingpong.txt +315 -0
  36. data/examples/pingpong/scala.rb +41 -0
  37. data/examples/pingpong/serial.rb +26 -0
  38. data/examples/pretty.txt +108 -0
  39. data/examples/telephone/.irbrc +2 -0
  40. data/examples/telephone/3esl.txt +21877 -0
  41. data/examples/telephone/fifth/kid.rb +36 -0
  42. data/examples/telephone/fifth/run.rb +26 -0
  43. data/examples/telephone/first/kid.rb +31 -0
  44. data/examples/telephone/first/run.rb +20 -0
  45. data/examples/telephone/fourth/kid.rb +31 -0
  46. data/examples/telephone/fourth/run.rb +26 -0
  47. data/examples/telephone/mangler.rb +53 -0
  48. data/examples/telephone/second/kid.rb +26 -0
  49. data/examples/telephone/second/run.rb +20 -0
  50. data/examples/telephone/seventh/kid.rb +40 -0
  51. data/examples/telephone/seventh/run.rb +35 -0
  52. data/examples/telephone/seventh/test.rb +28 -0
  53. data/examples/telephone/seventh/test2.rb +10 -0
  54. data/examples/telephone/sixth/kid.rb +39 -0
  55. data/examples/telephone/sixth/run.rb +26 -0
  56. data/examples/telephone/third/kid.rb +31 -0
  57. data/examples/telephone/third/run.rb +21 -0
  58. data/lib/dramatis/actor/interface.rb +118 -0
  59. data/lib/dramatis/actor/name/interface.rb +128 -0
  60. data/lib/dramatis/actor/name.rb +44 -0
  61. data/lib/dramatis/actor.rb +96 -0
  62. data/lib/dramatis/deadlock.rb +123 -0
  63. data/lib/dramatis/error/uncaught.rb +19 -0
  64. data/lib/dramatis/error.rb +125 -0
  65. data/lib/dramatis/future/interface.rb +45 -0
  66. data/lib/dramatis/future.rb +32 -0
  67. data/lib/dramatis/runtime/actor/main.rb +3 -0
  68. data/lib/dramatis/runtime/actor.rb +294 -0
  69. data/lib/dramatis/runtime/gate.rb +244 -0
  70. data/lib/dramatis/runtime/scheduler.rb +374 -0
  71. data/lib/dramatis/runtime/task.rb +390 -0
  72. data/lib/dramatis/runtime/thread_pool.rb +149 -0
  73. data/lib/dramatis/runtime/timer.rb +5 -0
  74. data/lib/dramatis/runtime.rb +129 -0
  75. data/lib/dramatis/shoes/runtime.rb +7 -0
  76. data/lib/dramatis/shoes.rb +14 -0
  77. data/lib/dramatis/version.rb +8 -0
  78. data/lib/dramatis.rb +73 -0
  79. data/log/debug.log +0 -0
  80. data/script/destroy +14 -0
  81. data/script/generate +14 -0
  82. data/script/txt2html +74 -0
  83. data/setup.rb +1585 -0
  84. data/spec/dramatis/actor/become_spec.rb +17 -0
  85. data/spec/dramatis/actor/future_spec.rb +189 -0
  86. data/spec/dramatis/actor/name_spec.rb +141 -0
  87. data/spec/dramatis/actor/task_spec.rb +75 -0
  88. data/spec/dramatis/actor_spec.rb +492 -0
  89. data/spec/dramatis/dramatis_spec.rb +23 -0
  90. data/spec/dramatis/exc_spec.rb +78 -0
  91. data/spec/dramatis/runtime/gate_spec.rb +57 -0
  92. data/spec/dramatis/runtime/thread_pool.rb +30 -0
  93. data/spec/dramatis/shoes_spec.rb +11 -0
  94. data/spec/dramatis/simple_spec.rb +32 -0
  95. data/spec/exp_spec.rb +21 -0
  96. data/spec/simple2_spec.rb +36 -0
  97. data/spec/simple_spec.rb +30 -0
  98. data/spec/spec.opts +0 -0
  99. data/spec/spec_helper.rb +26 -0
  100. data/spec/thread_spec.rb +13 -0
  101. data/tasks/deployment.rake +34 -0
  102. data/tasks/environment.rake +7 -0
  103. data/tasks/rspec.rake +21 -0
  104. data/tasks/website.rake +17 -0
  105. data/test/jruby_lm.rb +13 -0
  106. data/test/test.rb +19 -0
  107. data/test/test10.rb +43 -0
  108. data/test/test11.rb +45 -0
  109. data/test/test12.rb +60 -0
  110. data/test/test13.rb +71 -0
  111. data/test/test2.rb +12 -0
  112. data/test/test3.rb +10 -0
  113. data/test/test4.rb +29 -0
  114. data/test/test5.rb +8 -0
  115. data/test/test6.rb +32 -0
  116. data/test/test7.rb +48 -0
  117. data/test/test8.rb +133 -0
  118. data/test/test9.rb +105 -0
  119. data/test/test_exc.rb +22 -0
  120. metadata +180 -0
@@ -0,0 +1,294 @@
1
+ module Dramatis; end
2
+ class Dramatis::Runtime; end
3
+
4
+ require 'dramatis/runtime/task'
5
+ require 'dramatis/runtime/gate'
6
+ require 'dramatis/runtime/timer'
7
+ require 'dramatis/actor/interface'
8
+ require 'dramatis/actor/name'
9
+ require 'thread'
10
+
11
+ begin require 'pp'; rescue Exception; end
12
+
13
+ class Dramatis::Runtime::Actor #:nodoc: all
14
+
15
+ def name
16
+ @name ||= Dramatis::Actor::Name.new self
17
+ end
18
+
19
+ attr_reader :object_interface
20
+ attr_reader :object
21
+ attr_reader :gate
22
+ attr_reader :call_thread
23
+
24
+ def initialize object = nil
25
+ @call_threading = false
26
+ @call_thread = nil
27
+ @object = object
28
+ @gate = Dramatis::Runtime::Gate.new
29
+ if !object
30
+ @gate.refuse :object
31
+ end
32
+ @gate.always( ( [ :object, :dramatis_exception ] ), true )
33
+ blocked!
34
+ @queue = []
35
+ @mutex = Mutex.new
36
+ @continuations = {}
37
+ @object_interface = Dramatis::Actor::Interface.new self
38
+ Dramatis::Runtime::Scheduler.current << self
39
+ end
40
+
41
+ def call_threading?
42
+ @call_threading
43
+ end
44
+
45
+ def enable_call_threading
46
+ @call_threading = true
47
+ end
48
+
49
+ def current_call_thread? that
50
+ # warn "current_call_thread? #{@call_thread} #{(@call_thread and (@call_thread == that)).inspect} #{that}"
51
+ @call_thread and @call_thread == that
52
+ end
53
+
54
+ def object_initialize *args
55
+ @gate.accept :object
56
+ @object.send :initialize, *args
57
+ end
58
+
59
+ def yield; end
60
+
61
+ def bind object
62
+ raise Dramatis::Error::Bind if @object
63
+ @object = object
64
+ @gate.accept :object
65
+ name
66
+ end
67
+
68
+ def exception exception
69
+ if @object.respond_to? :dramatis_exception
70
+ @object.dramatis_exception exception
71
+ else
72
+ Dramatis::Runtime.current.exception exception
73
+ end
74
+ self
75
+ end
76
+
77
+ def deadlock e
78
+ tasks = nil
79
+ @mutex.synchronize do
80
+ tasks = @queue.dup
81
+ @queue.clear
82
+ end
83
+ tasks.each { |task| task.exception e }
84
+ end
85
+
86
+ def register_continuation c
87
+ # p "selfish", self, @continuations
88
+ # pp "csr", c.to_s
89
+ @continuations[c.to_s] = c
90
+ end
91
+
92
+ def actor_send args, opts
93
+ common_send :actor, args, opts
94
+ end
95
+
96
+ def object_send args, opts
97
+ t = nil
98
+ if opts[:continuation_send]
99
+ t = :continuation
100
+ args.unshift opts[:continuation_send]
101
+ else
102
+ t = :object
103
+ end
104
+ common_send t, args, opts
105
+ end
106
+
107
+ def common_send dest, args, opts
108
+
109
+ # warn "common send #{self} #{dest} #{args[0]}"
110
+ # warn "common send #{self} #{dest} #{args.join(' ')} #{opts.to_a.join(' ' )}"
111
+
112
+ task = Dramatis::Runtime::Task.new( self, dest, args, opts )
113
+
114
+ # warn "#{task.type} #{task.method}"
115
+ # warn "#{self} #{Thread.current} common send r? #{runnable?} g? #{@gate.accepts?( *( [ task.type, task.method ] + task.arguments ) ) } q #{@queue.length}"
116
+ @mutex.synchronize do
117
+ # FIX arguments to gate
118
+ if !runnable? and ( @gate.accepts?( *( [ task.type, task.method ] + task.arguments ) ) or current_call_thread?( task.call_thread ) )
119
+ runnable!
120
+ Dramatis::Runtime::Scheduler.current.schedule task
121
+ else
122
+ # warn "+>schd #{self} #{@queue.join(' ')}"
123
+ @queue << task
124
+ # warn "+<schd #{self} #{@queue.join(' ')}"
125
+ end
126
+ end
127
+
128
+ task.queued
129
+
130
+ end
131
+
132
+ def deliver dest, args, continuation, call_thread
133
+ old_call_thread = @call_thread
134
+ begin
135
+ @call_thread = call_thread
136
+ method = args.shift
137
+ # warn "switch " + dest.to_s + " " + method.to_s
138
+ result =
139
+ case dest
140
+ when :actor
141
+ # FIX: do name folding; needs test
142
+ self.send method, *args
143
+ # p "sent actor #{method}"
144
+ when :object
145
+ # p "send object #{@object} #{method} #{args.length}"
146
+ v = @object.send method, *args
147
+ if v.object_id == @object.object_id
148
+ v = name
149
+ end
150
+ # p "sent object #{method}"
151
+ v
152
+ when :continuation
153
+ # FIX: name folding?
154
+ # p "send continuation #{method}"
155
+ continuation_name = method
156
+ # warn "c is #{continuation_name}"
157
+ c = @continuations[continuation_name]
158
+ # pp "cs", @continuations.keys
159
+ raise "hell 0 #{Thread.current}" if !c
160
+ method = args.shift
161
+ method = case method
162
+ when :result; :continuation_result
163
+ when :exception; :continuation_exception
164
+ else; raise "hell *"
165
+ end
166
+ # pp c.to_s, "send", method, args
167
+ c.send method, *args
168
+ @continuations.delete continuation_name
169
+ # pp "csd", continuation_name, @continuations.keys
170
+ else
171
+ raise "hell 1: " + @dest.to_s
172
+ end
173
+ # p "call c '#{result}'"
174
+ # p continuation.to_s
175
+ continuation.result result
176
+ # p "called c #{result}"
177
+ rescue Exception => exception
178
+ # pp "0 exception ", exception
179
+ # pp exception.backtrace
180
+ begin
181
+ continuation.exception exception
182
+ rescue Exception => e
183
+ warn "double exception fault: #{e}"
184
+ pp e
185
+ raise e
186
+ end
187
+ ensure
188
+ @call_thread = old_call_thread
189
+ schedule
190
+ end
191
+ end
192
+
193
+ # note called from task.rb, too
194
+ def schedule continuation = nil
195
+ @mutex.synchronize do
196
+ # warn ">schd #{self} #{@state} #{@queue.join(' ')}"
197
+ task = nil
198
+ index = 0
199
+ while task == nil and index < @queue.length do
200
+ candidate = @queue[index]
201
+ # FIX arugments?
202
+ if @gate.accepts?( *( [ candidate.type, candidate.method ] + candidate.arguments ) ) or
203
+ current_call_thread? candidate.call_thread
204
+ task = candidate
205
+ @queue[index,1] = []
206
+ end
207
+ index += 1
208
+ end
209
+ if task
210
+ Dramatis::Runtime::Scheduler.current.schedule task
211
+ else
212
+ blocked!
213
+ end
214
+ # warn "<schd #{self} #{@queue.join(' ')} #{@state} #{Thread.current}"
215
+ end
216
+ end
217
+
218
+ def blocked!
219
+ # warn "blocked! #{self} #{@state}"
220
+ @state = :blocked
221
+ end
222
+
223
+ def runnable!
224
+ # warn "runnable! #{self} #{@state}"
225
+ @state = :runnable
226
+ end
227
+
228
+ def runnable?
229
+ # warn "runnable? #{self} #{@state}"
230
+ @state == :runnable
231
+ end
232
+
233
+ def timeout value, *args
234
+ @timer ||= Dramatis::Runtime::Timer.new
235
+ end
236
+
237
+ end
238
+
239
+ # Might be nice if this was broken out into another file ... YAGNI?
240
+ # This needs to be reworked. External threads and the main thread are
241
+ # related but different (there can only be one main thread). Mutliple
242
+ # at_exit calls are suspect. Of course, if the main actor tracks multilple
243
+ # runtimes, it might be okay.
244
+
245
+ class Dramatis::Runtime::Actor::Main < Dramatis::Runtime::Actor #:nodoc: all
246
+
247
+ class DefaultBehavior
248
+
249
+ class Exception < ::Exception; end
250
+
251
+ def method_missing *args
252
+ raise Exception.new( "must use Actor#become to enable main actor" )
253
+ end
254
+
255
+ def dramatis_exception e
256
+ if Dramatis::Runtime.current.warnings?
257
+ warn "exception on main thread: #{e}"
258
+ # pp caller
259
+ end
260
+ Dramatis::Runtime.current.exception e
261
+ end
262
+
263
+ end
264
+
265
+ @@current = nil
266
+
267
+ def self.current
268
+ @@current ||= self.new
269
+ end
270
+
271
+ def self.reset
272
+ @@current = nil
273
+ end
274
+
275
+ def quiesce
276
+ schedule
277
+ end
278
+
279
+ def finalize
280
+ if !@at_exit_run
281
+ @at_exit_run = true
282
+ schedule
283
+ Dramatis::Runtime::Scheduler.current.main_at_exit
284
+ end
285
+ end
286
+
287
+ def initialize
288
+ super DefaultBehavior.new
289
+ @at_exit_run = false
290
+ at_exit { finalize }
291
+ runnable!
292
+ end
293
+
294
+ end
@@ -0,0 +1,244 @@
1
+ module Dramatis; end
2
+ class Dramatis::Runtime; end
3
+
4
+ begin require 'pp'; rescue Exception; end
5
+
6
+ class Dramatis::Runtime::Gate #:nodoc: all
7
+
8
+ def self.new
9
+ gate = Case.new
10
+ gate.accept Object
11
+ gate.accept :actor
12
+ gate.accept :continuation
13
+ gate.accept :object
14
+ gate
15
+ end
16
+
17
+ class Case
18
+ def always args, value, options = {}
19
+ _change @always, Array( args ), value, options
20
+ end
21
+ def only args, options = {}
22
+ _change @list, [ :object ], false, options
23
+ _change @list, [ :continuation ], false, options
24
+ _change @list, [ :continuation, Object, :exception ], true, options
25
+ _change @list, Array( args ), true, options
26
+ end
27
+ def default args, options = {}
28
+ _change @list, args, nil, options
29
+ end
30
+ def default_by_tag tag
31
+ _change @list, nil, nil, { :tag => tag }
32
+ end
33
+ def _change list, args, value, options = {}
34
+ inplace = options[ :inplace ]
35
+ tag = options[ :tag ]
36
+ # pp ">> #{args and args.join(' ')} #{value} #{inplace} #{tag}", list
37
+ prepend = true
38
+ tbd = []
39
+ list.each_with_index do |entry, list_index|
40
+ vector, result, entry_tag = entry
41
+ matches = true
42
+ if tag and entry_tag != tag
43
+ # p "#{tag} tag and #{entry_tag} dont' match"
44
+ next
45
+ end
46
+ if args
47
+ args.each_with_index do |arg, arg_index|
48
+ # p "compare #{vector[arg_index]} #{arg}"
49
+ if vector[arg_index] != arg
50
+ # p "matches"
51
+ matches = false
52
+ break
53
+ end
54
+ end
55
+ end
56
+ if matches
57
+ # p "matched"
58
+ if inplace and value != nil
59
+ # p "inplace"
60
+ list[list_index][1] = value
61
+ # FIX: tag?
62
+ prepend = false
63
+ else
64
+ # p "schedule remove #{list[list_index].join(' ')} at #{list_index}"
65
+ tbd << list_index
66
+ # list[list_index,1] = []
67
+ end
68
+ # break
69
+ end
70
+ end
71
+ tbd.reverse.each do |index|
72
+ # p "remove #{index} #{list[index].join(' ')} at #{index}"
73
+ list[index,1] = []
74
+ end
75
+ if prepend and value != nil
76
+ list.unshift [ args, value, tag ]
77
+ end
78
+ # pp "<< #{args and args.join(' ')} #{value} #{inplace} #{tag}", list
79
+ end
80
+ def change args, value, inplace
81
+ # pp "> #{args.join(' ')} #{value} #{inplace}", @list
82
+ prepend = true
83
+ @list.each_with_index do |entry, list_index|
84
+ vector, result = entry
85
+ matches = true
86
+ args.each_with_index do |arg, arg_index|
87
+ # p "compare #{vector[arg_index]} #{arg}"
88
+ if vector[arg_index] != arg
89
+ # p "matches"
90
+ matches = false
91
+ break
92
+ end
93
+ end
94
+ if matches
95
+ # p "matched"
96
+ if inplace
97
+ # p "inplace"
98
+ @list[list_index][1] = value
99
+ # FIX: tag?
100
+ prepend = false
101
+ else
102
+ @list[list_index,1] = []
103
+ end
104
+ break
105
+ end
106
+ end
107
+ if prepend
108
+ @list.unshift [ args, value ]
109
+ end
110
+ # pp "<", @list
111
+ end
112
+ def accept *args
113
+ change args, true, false
114
+ end
115
+ def refuse *args
116
+ change args, false, false
117
+ end
118
+ def update value, *args
119
+ end
120
+ def accepts? *args
121
+ # p "accepts? #{args.join(" ")}"
122
+ accepted = nil
123
+ ( @always + @list ).each do |entry|
124
+ vector, result = entry
125
+ matches = true
126
+ vector.each_with_index do |v, i|
127
+ # warn "#{v} #{args[i]} => #{v === args[i]} so #{result}"
128
+ if not( v === args[i] )
129
+ matches = false
130
+ break
131
+ end
132
+ end
133
+ if matches
134
+ accepted = result
135
+ break
136
+ end
137
+ end
138
+ # warn "last '#{accepted}'"
139
+ # p "accepts? #{args.join(" ")} => '#{accepted}'"
140
+ accepted == true
141
+ end
142
+ def initialize
143
+ @always = []
144
+ @list = []
145
+ end
146
+ def list
147
+ @list
148
+ end
149
+ end
150
+
151
+ class Constant
152
+ def initialize constant
153
+ @constant = constant
154
+ end
155
+ def accepts? *args
156
+ @constant
157
+ end
158
+ def refuse *args
159
+ warn "HELLL"
160
+ raise "hell"
161
+ end
162
+ def accept *args
163
+ warn "HELLL"
164
+ raise "hell"
165
+ end
166
+ def default *args
167
+ warn "HELLL"
168
+ raise "hell"
169
+ end
170
+ def set_default *args
171
+ warn "HELLL"
172
+ raise "hell"
173
+ end
174
+ end
175
+
176
+ ACCEPT = Constant.new true
177
+ REJECT = Constant.new false
178
+
179
+ class Hash
180
+
181
+ def initialize default, hash = {}
182
+ @default = default
183
+ @hash = hash.clone
184
+ @queued = {}
185
+ track and warn "create default = #{@default} hash = [#{@hash.keys.join(' ')}]"
186
+ @always = []
187
+ @call_thread = nil
188
+ end
189
+
190
+ def track; false; end
191
+
192
+ def always *args
193
+ track and warn "always [#{args.join(' ')}]"
194
+ @always << args
195
+ end
196
+
197
+ def refuse *args
198
+ track and warn "refuse [#{args.join(' ')}]"
199
+ if args.length == 1
200
+ @hash[args[0]] = REJECT
201
+ else
202
+ @hash[args[0]].refuse( *args[1,args.length] )
203
+ end
204
+ end
205
+
206
+ def accept *args
207
+ track and warn "accept [#{args.join(' ')}]"
208
+ if args.length == 1
209
+ @hash[args[0]] = ACCEPT
210
+ else
211
+ @hash[args[0]].accept( *args[1,args.length] )
212
+ end
213
+ end
214
+
215
+ def default *args
216
+ track and warn "default [#{args.join(' ')}]"
217
+ if args.length == 1
218
+ @hash.delete args[0]
219
+ else
220
+ @hash[args[0]].default( *args[1,args.length] )
221
+ end
222
+ end
223
+
224
+ def set_default value, *args
225
+ track and warn "default = #{value} [#{args.join(' ')}] #{args.length}"
226
+ if args.length == 0
227
+ @default = value
228
+ else
229
+ @hash[args[0]].set_default value, *args[1,args.length]
230
+ end
231
+ end
232
+
233
+ def accepts? *args
234
+ v = nil
235
+ if v == nil
236
+ v = @hash.has_key?( args[0] ) ? @hash[args[0]].accepts?( *args[1,args.length] ) : @default
237
+ end
238
+ track and warn "accepts? [#{args.join(' ')}] => #{v}"
239
+ v
240
+ end
241
+
242
+ end
243
+
244
+ end