celluloid 0.16.0 → 0.17.3

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 (177) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGES.md +394 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +28 -15
  5. data/culture/CONDUCT.md +28 -0
  6. data/culture/Gemfile +9 -0
  7. data/culture/LICENSE.txt +22 -0
  8. data/culture/README.md +22 -0
  9. data/culture/Rakefile +5 -0
  10. data/culture/SYNC.md +70 -0
  11. data/culture/celluloid-culture.gemspec +18 -0
  12. data/culture/gems/README.md +39 -0
  13. data/culture/gems/dependencies.yml +85 -0
  14. data/culture/gems/loader.rb +101 -0
  15. data/culture/rubocop/README.md +38 -0
  16. data/culture/rubocop/lint.yml +8 -0
  17. data/culture/rubocop/metrics.yml +15 -0
  18. data/culture/rubocop/perf.yml +0 -0
  19. data/culture/rubocop/rubocop.yml +5 -0
  20. data/culture/rubocop/style.yml +57 -0
  21. data/culture/spec/gems_spec.rb +2 -0
  22. data/culture/spec/spec_helper.rb +0 -0
  23. data/culture/spec/sync_spec.rb +2 -0
  24. data/culture/sync.rb +56 -0
  25. data/culture/tasks/rspec.rake +5 -0
  26. data/culture/tasks/rubocop.rake +2 -0
  27. data/examples/basic_usage.rb +49 -0
  28. data/examples/futures.rb +38 -0
  29. data/examples/ring.rb +61 -0
  30. data/examples/simple_pmap.rb +14 -0
  31. data/examples/stack.rb +47 -0
  32. data/examples/timers.rb +72 -0
  33. data/lib/celluloid/actor/manager.rb +7 -0
  34. data/lib/celluloid/actor/system.rb +163 -0
  35. data/lib/celluloid/actor.rb +53 -71
  36. data/lib/celluloid/autostart.rb +1 -1
  37. data/lib/celluloid/backported.rb +2 -0
  38. data/lib/celluloid/call/async.rb +16 -0
  39. data/lib/celluloid/call/block.rb +22 -0
  40. data/lib/celluloid/call/sync.rb +70 -0
  41. data/lib/celluloid/calls.rb +24 -114
  42. data/lib/celluloid/cell.rb +32 -20
  43. data/lib/celluloid/condition.rb +5 -6
  44. data/lib/celluloid/core_ext.rb +1 -1
  45. data/lib/celluloid/current.rb +2 -0
  46. data/lib/celluloid/debug.rb +1 -0
  47. data/lib/celluloid/deprecate.rb +21 -0
  48. data/lib/celluloid/exceptions.rb +22 -16
  49. data/lib/celluloid/fiber.rb +3 -3
  50. data/lib/celluloid/future.rb +46 -8
  51. data/lib/celluloid/group/pool.rb +125 -0
  52. data/lib/celluloid/group/spawner.rb +68 -0
  53. data/lib/celluloid/group.rb +61 -0
  54. data/lib/celluloid/logging.rb +5 -5
  55. data/lib/celluloid/mailbox/evented.rb +74 -0
  56. data/lib/celluloid/mailbox.rb +15 -14
  57. data/lib/celluloid/managed.rb +3 -0
  58. data/lib/celluloid/notices.rb +15 -0
  59. data/lib/celluloid/proxies.rb +11 -0
  60. data/lib/celluloid/proxy/abstract.rb +50 -0
  61. data/lib/celluloid/proxy/actor.rb +37 -0
  62. data/lib/celluloid/proxy/async.rb +15 -0
  63. data/lib/celluloid/proxy/block.rb +28 -0
  64. data/lib/celluloid/proxy/cell.rb +66 -0
  65. data/lib/celluloid/proxy/future.rb +20 -0
  66. data/lib/celluloid/proxy/sync.rb +24 -0
  67. data/lib/celluloid/rspec.rb +67 -9
  68. data/lib/celluloid/system_events.rb +69 -14
  69. data/lib/celluloid/task/fibered.rb +45 -0
  70. data/lib/celluloid/task/threaded.rb +59 -0
  71. data/lib/celluloid/{tasks.rb → task.rb} +30 -38
  72. data/lib/celluloid/test.rb +1 -1
  73. data/lib/celluloid/thread.rb +6 -1
  74. data/lib/celluloid/version.rb +3 -0
  75. data/lib/celluloid.rb +151 -130
  76. data/spec/celluloid/actor/system_spec.rb +83 -0
  77. data/spec/celluloid/actor_spec.rb +2 -2
  78. data/spec/celluloid/block_spec.rb +67 -8
  79. data/spec/celluloid/calls_spec.rb +28 -23
  80. data/spec/celluloid/condition_spec.rb +23 -14
  81. data/spec/celluloid/evented_mailbox_spec.rb +1 -31
  82. data/spec/celluloid/future_spec.rb +15 -12
  83. data/spec/celluloid/group/elastic_spec.rb +0 -0
  84. data/spec/celluloid/group/pool_spec.rb +8 -0
  85. data/spec/celluloid/group/spawner_spec.rb +17 -0
  86. data/spec/celluloid/mailbox/evented_spec.rb +40 -0
  87. data/spec/celluloid/mailbox_spec.rb +1 -3
  88. data/spec/celluloid/misc/leak_spec.rb +73 -0
  89. data/spec/celluloid/proxy_spec.rb +33 -0
  90. data/spec/celluloid/task/fibered_spec.rb +5 -0
  91. data/spec/celluloid/task/threaded_spec.rb +5 -0
  92. data/spec/celluloid/timer_spec.rb +14 -16
  93. data/spec/deprecate/actor_system_spec.rb +72 -0
  94. data/spec/deprecate/block_spec.rb +52 -0
  95. data/spec/deprecate/calls_spec.rb +39 -0
  96. data/spec/deprecate/evented_mailbox_spec.rb +34 -0
  97. data/spec/deprecate/future_spec.rb +32 -0
  98. data/spec/deprecate/internal_pool_spec.rb +4 -0
  99. data/spec/shared/actor_examples.rb +1259 -0
  100. data/spec/shared/group_examples.rb +121 -0
  101. data/spec/shared/mailbox_examples.rb +87 -0
  102. data/{lib/celluloid/rspec → spec/shared}/task_examples.rb +9 -8
  103. data/spec/spec_helper.rb +5 -42
  104. data/spec/support/configure_rspec.rb +77 -0
  105. data/spec/support/coverage.rb +4 -0
  106. data/spec/support/crash_checking.rb +68 -0
  107. data/spec/support/debugging.rb +31 -0
  108. data/spec/support/env.rb +21 -0
  109. data/{lib/celluloid/rspec/example_actor_class.rb → spec/support/examples/actor_class.rb} +21 -2
  110. data/spec/support/examples/call_class.rb +37 -0
  111. data/spec/support/examples/evented_mailbox_class.rb +27 -0
  112. data/spec/support/includer.rb +6 -0
  113. data/spec/support/logging.rb +55 -0
  114. data/spec/support/loose_threads.rb +68 -0
  115. data/spec/support/reset_class_variables.rb +27 -0
  116. data/spec/support/sleep_and_wait.rb +14 -0
  117. data/spec/support/stubbing.rb +14 -0
  118. metadata +276 -78
  119. data/lib/celluloid/actor_system.rb +0 -107
  120. data/lib/celluloid/call_chain.rb +0 -13
  121. data/lib/celluloid/cpu_counter.rb +0 -34
  122. data/lib/celluloid/evented_mailbox.rb +0 -73
  123. data/lib/celluloid/fsm.rb +0 -186
  124. data/lib/celluloid/handlers.rb +0 -41
  125. data/lib/celluloid/internal_pool.rb +0 -159
  126. data/lib/celluloid/legacy.rb +0 -9
  127. data/lib/celluloid/links.rb +0 -36
  128. data/lib/celluloid/logger.rb +0 -93
  129. data/lib/celluloid/logging/incident.rb +0 -21
  130. data/lib/celluloid/logging/incident_logger.rb +0 -129
  131. data/lib/celluloid/logging/incident_reporter.rb +0 -48
  132. data/lib/celluloid/logging/log_event.rb +0 -20
  133. data/lib/celluloid/logging/ring_buffer.rb +0 -65
  134. data/lib/celluloid/method.rb +0 -32
  135. data/lib/celluloid/notifications.rb +0 -83
  136. data/lib/celluloid/pool_manager.rb +0 -146
  137. data/lib/celluloid/probe.rb +0 -73
  138. data/lib/celluloid/properties.rb +0 -24
  139. data/lib/celluloid/proxies/abstract_proxy.rb +0 -20
  140. data/lib/celluloid/proxies/actor_proxy.rb +0 -38
  141. data/lib/celluloid/proxies/async_proxy.rb +0 -31
  142. data/lib/celluloid/proxies/block_proxy.rb +0 -29
  143. data/lib/celluloid/proxies/cell_proxy.rb +0 -68
  144. data/lib/celluloid/proxies/future_proxy.rb +0 -35
  145. data/lib/celluloid/proxies/sync_proxy.rb +0 -36
  146. data/lib/celluloid/receivers.rb +0 -63
  147. data/lib/celluloid/registry.rb +0 -57
  148. data/lib/celluloid/responses.rb +0 -44
  149. data/lib/celluloid/rspec/actor_examples.rb +0 -1054
  150. data/lib/celluloid/rspec/mailbox_examples.rb +0 -84
  151. data/lib/celluloid/signals.rb +0 -23
  152. data/lib/celluloid/stack_dump.rb +0 -133
  153. data/lib/celluloid/supervision_group.rb +0 -169
  154. data/lib/celluloid/supervisor.rb +0 -22
  155. data/lib/celluloid/task_set.rb +0 -49
  156. data/lib/celluloid/tasks/task_fiber.rb +0 -43
  157. data/lib/celluloid/tasks/task_thread.rb +0 -53
  158. data/lib/celluloid/thread_handle.rb +0 -50
  159. data/lib/celluloid/uuid.rb +0 -38
  160. data/spec/celluloid/actor_system_spec.rb +0 -69
  161. data/spec/celluloid/cpu_counter_spec.rb +0 -82
  162. data/spec/celluloid/fsm_spec.rb +0 -107
  163. data/spec/celluloid/internal_pool_spec.rb +0 -52
  164. data/spec/celluloid/links_spec.rb +0 -45
  165. data/spec/celluloid/logging/ring_buffer_spec.rb +0 -38
  166. data/spec/celluloid/notifications_spec.rb +0 -120
  167. data/spec/celluloid/pool_spec.rb +0 -92
  168. data/spec/celluloid/probe_spec.rb +0 -121
  169. data/spec/celluloid/properties_spec.rb +0 -42
  170. data/spec/celluloid/registry_spec.rb +0 -64
  171. data/spec/celluloid/stack_dump_spec.rb +0 -64
  172. data/spec/celluloid/supervision_group_spec.rb +0 -65
  173. data/spec/celluloid/supervisor_spec.rb +0 -103
  174. data/spec/celluloid/tasks/task_fiber_spec.rb +0 -5
  175. data/spec/celluloid/tasks/task_thread_spec.rb +0 -5
  176. data/spec/celluloid/thread_handle_spec.rb +0 -26
  177. data/spec/celluloid/uuid_spec.rb +0 -11
@@ -4,13 +4,15 @@ module Celluloid
4
4
  attr_reader :method, :arguments, :block
5
5
 
6
6
  def initialize(method, arguments = [], block = nil)
7
- @method, @arguments = method, arguments
7
+ @retry = 0
8
+ @method = method
9
+ @arguments = arguments
8
10
  if block
9
11
  if Celluloid.exclusive?
10
12
  # FIXME: nicer exception
11
- raise "Cannot execute blocks on sender in exclusive mode"
13
+ fail "Cannot execute blocks on sender in exclusive mode"
12
14
  end
13
- @block = BlockProxy.new(self, Celluloid.mailbox, block)
15
+ @block = Proxy::Block.new(Celluloid.mailbox, self, block)
14
16
  else
15
17
  @block = nil
16
18
  end
@@ -22,130 +24,38 @@ module Celluloid
22
24
 
23
25
  def dispatch(obj)
24
26
  check(obj)
25
- _block = @block && @block.to_proc
26
- obj.public_send(@method, *@arguments, &_block)
27
+ _b = @block && @block.to_proc
28
+ obj.public_send(@method, *@arguments, &_b)
29
+ # rescue Celluloid::TaskTimeout => ex
30
+ # raise ex unless ( @retry += 1 ) <= RETRY_CALL_LIMIT
31
+ # puts "retrying"
32
+ # Internals::Logger.warn("TaskTimeout at Call dispatch. Retrying in #{RETRY_CALL_WAIT} seconds. ( Attempt #{@retry} of #{RETRY_CALL_LIMIT} )")
33
+ # sleep RETRY_CALL_WAIT
34
+ # retry
27
35
  end
28
36
 
29
37
  def check(obj)
30
- raise NoMethodError, "undefined method `#{@method}' for #{obj.inspect}" unless obj.respond_to? @method
31
-
38
+ # NOTE: don't use respond_to? here
32
39
  begin
33
- arity = obj.method(@method).arity
40
+ meth = obj.method(@method)
34
41
  rescue NameError
35
- return
42
+ raise NoMethodError, "undefined method `#{@method}' for #<#{obj.class}:0x#{obj.object_id.to_s(16)}>"
36
43
  end
37
44
 
45
+ arity = meth.arity
46
+
38
47
  if arity >= 0
39
- raise ArgumentError, "wrong number of arguments (#{@arguments.size} for #{arity})" if @arguments.size != arity
48
+ fail ArgumentError, "wrong number of arguments (#{@arguments.size} for #{arity})" if @arguments.size != arity
40
49
  elsif arity < -1
41
50
  mandatory_args = -arity - 1
42
- raise ArgumentError, "wrong number of arguments (#{@arguments.size} for #{mandatory_args}+)" if arguments.size < mandatory_args
51
+ fail ArgumentError, "wrong number of arguments (#{@arguments.size} for #{mandatory_args}+)" if arguments.size < mandatory_args
43
52
  end
44
53
  rescue => ex
45
54
  raise AbortError.new(ex)
46
55
  end
47
56
  end
48
-
49
- # Synchronous calls wait for a response
50
- class SyncCall < Call
51
- attr_reader :sender, :task, :chain_id
52
-
53
- def initialize(sender, method, arguments = [], block = nil, task = Thread.current[:celluloid_task], chain_id = CallChain.current_id)
54
- super(method, arguments, block)
55
-
56
- @sender = sender
57
- @task = task
58
- @chain_id = chain_id || Celluloid.uuid
59
- end
60
-
61
- def dispatch(obj)
62
- CallChain.current_id = @chain_id
63
- result = super(obj)
64
- respond SuccessResponse.new(self, result)
65
- rescue Exception => ex
66
- # Exceptions that occur during synchronous calls are reraised in the
67
- # context of the sender
68
- respond ErrorResponse.new(self, ex)
69
-
70
- # Aborting indicates a protocol error on the part of the sender
71
- # It should crash the sender, but the exception isn't reraised
72
- # Otherwise, it's a bug in this actor and should be reraised
73
- raise unless ex.is_a?(AbortError)
74
- ensure
75
- CallChain.current_id = nil
76
- end
77
-
78
- def cleanup
79
- exception = DeadActorError.new("attempted to call a dead actor")
80
- respond ErrorResponse.new(self, exception)
81
- end
82
-
83
- def respond(message)
84
- @sender << message
85
- end
86
-
87
- def response
88
- Celluloid.suspend(:callwait, self)
89
- end
90
-
91
- def value
92
- response.value
93
- end
94
-
95
- def wait
96
- loop do
97
- message = Celluloid.mailbox.receive do |msg|
98
- msg.respond_to?(:call) and msg.call == self
99
- end
100
-
101
- if message.is_a?(SystemEvent)
102
- Thread.current[:celluloid_actor].handle_system_event(message)
103
- else
104
- # FIXME: add check for receiver block execution
105
- if message.respond_to?(:value)
106
- # FIXME: disable block execution if on :sender and (exclusive or outside of task)
107
- # probably now in Call
108
- break message
109
- else
110
- message.dispatch
111
- end
112
- end
113
- end
114
- end
115
- end
116
-
117
- # Asynchronous calls don't wait for a response
118
- class AsyncCall < Call
119
-
120
- def dispatch(obj)
121
- CallChain.current_id = Celluloid.uuid
122
- super(obj)
123
- rescue AbortError => ex
124
- # Swallow aborted async calls, as they indicate the sender made a mistake
125
- Logger.debug("#{obj.class}: async call `#@method` aborted!\n#{Logger.format_exception(ex.cause)}")
126
- ensure
127
- CallChain.current_id = nil
128
- end
129
-
130
- end
131
-
132
- class BlockCall
133
- def initialize(block_proxy, sender, arguments, task = Thread.current[:celluloid_task])
134
- @block_proxy = block_proxy
135
- @sender = sender
136
- @arguments = arguments
137
- @task = task
138
- end
139
- attr_reader :task
140
-
141
- def call
142
- @block_proxy.call
143
- end
144
-
145
- def dispatch
146
- response = @block_proxy.block.call(*@arguments)
147
- @sender << BlockResponse.new(self, response)
148
- end
149
- end
150
-
151
57
  end
58
+
59
+ require "celluloid/call/sync"
60
+ require "celluloid/call/async"
61
+ require "celluloid/call/block"
@@ -33,57 +33,69 @@ module Celluloid
33
33
  @actor.handle(Call) do |message|
34
34
  invoke(message)
35
35
  end
36
- @actor.handle(BlockCall) do |message|
36
+ @actor.handle(Call::Block) do |message|
37
37
  task(:invoke_block) { message.dispatch }
38
38
  end
39
- @actor.handle(BlockResponse, Response) do |message|
39
+ @actor.handle(Internals::Response::Block, Internals::Response) do |message|
40
40
  message.dispatch
41
41
  end
42
42
 
43
43
  @actor.start
44
- @proxy = (options[:proxy_class] || CellProxy).new(@actor.proxy, @actor.mailbox, @subject.class.to_s)
44
+ @proxy = (options[:proxy_class] || Proxy::Cell).new(@actor.mailbox, @actor.proxy, @subject.class.to_s)
45
45
  end
46
46
  attr_reader :proxy, :subject
47
47
 
48
+ def self.dispatch
49
+ proc do |subject|
50
+ subject[:call].dispatch(subject[:subject])
51
+ subject[:call] = nil
52
+ subject[:subject] = nil
53
+ end
54
+ end
55
+
48
56
  def invoke(call)
49
57
  meth = call.method
50
- if meth == :__send__
51
- meth = call.arguments.first
52
- end
58
+ meth = call.arguments.first if meth == :__send__
53
59
  if @receiver_block_executions && meth
54
60
  if @receiver_block_executions.include?(meth.to_sym)
55
61
  call.execute_block_on_receiver
56
62
  end
57
63
  end
58
64
 
59
- task(:call, meth, :dangerous_suspend => meth == :initialize) {
60
- call.dispatch(@subject)
61
- }
65
+ task(:call, meth, {call: call, subject: @subject},
66
+ dangerous_suspend: meth == :initialize, &Cell.dispatch)
62
67
  end
63
68
 
64
- def task(task_type, method_name = nil, meta = nil, &block)
69
+ def task(task_type, method_name = nil, subject = nil, meta = nil, &_block)
65
70
  meta ||= {}
66
- meta.merge!(:method_name => method_name)
71
+ meta.merge!(method_name: method_name)
67
72
  @actor.task(task_type, meta) do
68
73
  if @exclusive_methods && method_name && @exclusive_methods.include?(method_name.to_sym)
69
- Celluloid.exclusive { yield }
74
+ Celluloid.exclusive { yield subject }
70
75
  else
71
- yield
76
+ yield subject
72
77
  end
73
78
  end
74
79
  end
75
80
 
76
- # Run the user-defined finalizer, if one is set
77
- def shutdown
78
- return unless @finalizer && @subject.respond_to?(@finalizer, true)
79
-
80
- task(:finalizer, @finalizer, :dangerous_suspend => true) do
81
+ def self.shutdown
82
+ proc do |subject|
81
83
  begin
82
- @subject.__send__(@finalizer)
84
+ subject[:subject].__send__(subject[:call])
83
85
  rescue => ex
84
- Logger.crash("#{@subject.class} finalizer crashed!", ex)
86
+ Internals::Logger.crash("#{subject[:subject].class} finalizer crashed!", ex)
85
87
  end
88
+ subject[:call] = nil
89
+ subject[:subject] = nil
86
90
  end
87
91
  end
92
+
93
+ # Run the user-defined finalizer, if one is set
94
+ def shutdown
95
+ return unless @finalizer && @subject.respond_to?(@finalizer, true)
96
+
97
+ task(:finalizer, @finalizer, {call: @finalizer, subject: @subject},
98
+ dangerous_suspend: true, &Cell.shutdown)
99
+ end
88
100
  end
89
101
  end
@@ -1,6 +1,4 @@
1
1
  module Celluloid
2
- class ConditionError < Celluloid::Error; end
3
-
4
2
  # ConditionVariable-like signaling between tasks and threads
5
3
  class Condition
6
4
  class Waiter
@@ -21,7 +19,7 @@ module Celluloid
21
19
  message = @mailbox.receive(@timeout) do |msg|
22
20
  msg.is_a?(SignalConditionRequest) && msg.task == Thread.current
23
21
  end
24
- rescue TimeoutError
22
+ rescue TimedOut
25
23
  raise ConditionError, "timeout after #{@timeout.inspect} seconds"
26
24
  end until message
27
25
 
@@ -36,7 +34,7 @@ module Celluloid
36
34
 
37
35
  # Wait for the given signal and return the associated value
38
36
  def wait(timeout = nil)
39
- raise ConditionError, "cannot wait for signals while exclusive" if Celluloid.exclusive?
37
+ fail ConditionError, "cannot wait for signals while exclusive" if Celluloid.exclusive?
40
38
 
41
39
  if actor = Thread.current[:celluloid_actor]
42
40
  task = Task.current
@@ -59,7 +57,8 @@ module Celluloid
59
57
 
60
58
  result = Celluloid.suspend :condwait, waiter
61
59
  timer.cancel if timer
62
- raise result if result.is_a? ConditionError
60
+ fail result if result.is_a?(ConditionError)
61
+ return yield(result) if block_given?
63
62
  result
64
63
  end
65
64
 
@@ -69,7 +68,7 @@ module Celluloid
69
68
  if waiter = @waiters.shift
70
69
  waiter << SignalConditionRequest.new(waiter.task, value)
71
70
  else
72
- Logger.with_backtrace(caller(3)) do |logger|
71
+ Internals::Logger.with_backtrace(caller(3)) do |logger|
73
72
  logger.debug("Celluloid::Condition signaled spuriously")
74
73
  end
75
74
  end
@@ -1,4 +1,4 @@
1
- require 'celluloid/fiber'
1
+ require "celluloid/fiber"
2
2
 
3
3
  class Thread
4
4
  attr_accessor :uuid_counter, :uuid_limit
@@ -0,0 +1,2 @@
1
+ $CELLULOID_BACKPORTED = false
2
+ require "celluloid/autostart"
@@ -0,0 +1 @@
1
+ $CELLULOID_DEBUG = true
@@ -0,0 +1,21 @@
1
+ class Thread
2
+ def self.mailbox
3
+ Celluloid.mailbox
4
+ end
5
+
6
+ def self.receive(timeout = nil, &block)
7
+ Celluloid.receive(timeout, &block)
8
+ end
9
+ end
10
+
11
+ # TODO: Remove link to Interal::Logger
12
+ module Celluloid
13
+ SyncCall = Call::Sync
14
+ EventedMailbox = Mailbox::Evented
15
+ InternalPool = Group::Pool
16
+ TaskThread = Task::Threaded
17
+ TaskFiber = Task::Fibered
18
+ ActorSystem = Actor::System
19
+ Task::TerminatedError = TaskTerminated
20
+ Task::TimeoutError = TaskTimeout
21
+ end
@@ -1,23 +1,29 @@
1
1
  module Celluloid
2
- # Base class of all Celluloid errors
3
- Error = Class.new(StandardError)
4
-
5
- # Don't do Actor-like things outside Actor scope
6
- NotActorError = Class.new(Celluloid::Error)
7
-
8
- # Trying to do something to a dead actor
9
- DeadActorError = Class.new(Celluloid::Error)
10
-
11
- # A timeout occured before the given request could complete
12
- TimeoutError = Class.new(Celluloid::Error)
13
-
14
- # The sender made an error, not the current actor
15
- class AbortError < Celluloid::Error
2
+ class Error < StandardError; end
3
+ class Interruption < Exception; end
4
+ class TimedOut < Celluloid::Interruption; end # Distinguished from `Timeout`
5
+ class StillActive < Celluloid::Error; end
6
+ class NotActive < Celluloid::Error; end
7
+ class NotActorError < Celluloid::Error; end # Don't do Actor-like things outside Actor scope
8
+ class DeadActorError < Celluloid::Error; end # Trying to do something to a dead actor
9
+ class NotTaskError < Celluloid::Error; end # Asked to do task-related things outside a task
10
+ class DeadTaskError < Celluloid::Error; end # Trying to resume a dead task
11
+ class TaskTerminated < Celluloid::Interruption; end # Kill a running task after terminate
12
+ class TaskTimeout < Celluloid::TimedOut; end # A timeout occured before the given request could complete
13
+ class ConditionError < Celluloid::Error; end
14
+ class AbortError < Celluloid::Error # The sender made an error, not the current actor
16
15
  attr_reader :cause
17
-
18
16
  def initialize(cause)
19
17
  @cause = cause
20
- super "caused by #{cause.inspect}: #{cause.to_s}"
18
+ super "caused by #{cause.inspect}: #{cause}"
19
+ end
20
+ end
21
+ class ThreadLeak < Celluloid::Error; end
22
+ module Feature
23
+ module Requires
24
+ class RubiniusOrJRuby < Celluloid::Error; end
25
+ class Rubinius < Celluloid::Error; end
26
+ class JRuby < Celluloid::Error; end
21
27
  end
22
28
  end
23
29
  end
@@ -1,6 +1,6 @@
1
1
  # Fibers are hard... let's go shopping!
2
2
  begin
3
- require 'fiber'
3
+ require "fiber"
4
4
  rescue LoadError => ex
5
5
  if defined? JRUBY_VERSION
6
6
  if RUBY_VERSION < "1.9.2"
@@ -9,9 +9,9 @@ rescue LoadError => ex
9
9
 
10
10
  # Fibers are broken on JRuby 1.6.5. This works around the issue
11
11
  if JRUBY_VERSION[/^1\.6\.5/]
12
- require 'jruby'
12
+ require "jruby"
13
13
  org.jruby.ext.fiber.FiberExtLibrary.new.load(JRuby.runtime, false)
14
- class org::jruby::ext::fiber::ThreadFiber
14
+ class org.jruby.ext.fiber::ThreadFiber
15
15
  field_accessor :state
16
16
  end
17
17
 
@@ -1,4 +1,4 @@
1
- require 'thread'
1
+ require "thread"
2
2
 
3
3
  module Celluloid
4
4
  # Celluloid::Future objects allow methods and blocks to run in the
@@ -8,9 +8,13 @@ module Celluloid
8
8
  return super unless block
9
9
 
10
10
  future = new
11
- Celluloid::ThreadHandle.new(Celluloid.actor_system, :future) do
11
+ # task = Thread.current[:celluloid_task]
12
+ # actor = Thread.current[:celluloid_actor]
13
+ Internals::ThreadHandle.new(Celluloid.actor_system, :future) do
12
14
  begin
13
- call = SyncCall.new(future, :call, args)
15
+ # Thread.current[:celluloid_task] = task
16
+ # Thread.current[:celluloid_actor] = actor
17
+ call = Call::Sync.new(future, :call, args)
14
18
  call.dispatch(block)
15
19
  rescue
16
20
  # Exceptions in blocks will get raised when the value is retrieved
@@ -21,12 +25,36 @@ module Celluloid
21
25
 
22
26
  attr_reader :address
23
27
 
24
- def initialize
28
+ def initialize(&block)
25
29
  @address = Celluloid.uuid
26
30
  @mutex = Mutex.new
27
31
  @ready = false
28
32
  @result = nil
29
33
  @forwards = nil
34
+ @cancelled = false
35
+
36
+ if block
37
+ @call = Call::Sync.new(self, :call, args)
38
+ Celluloid.internal_pool.get do
39
+ begin
40
+ @call.dispatch(block)
41
+ rescue
42
+ # Exceptions in blocks will get raised when the value is retrieved
43
+ end
44
+ end
45
+ else
46
+ @call = nil
47
+ end
48
+ end
49
+
50
+ # Execute the given method in future context
51
+ def execute(receiver, method, args, block)
52
+ @mutex.synchronize do
53
+ fail "already calling" if @call
54
+ @call = Call::Sync.new(self, method, args, block)
55
+ end
56
+
57
+ receiver << @call
30
58
  end
31
59
 
32
60
  # Check if this future has a value yet
@@ -65,19 +93,20 @@ module Celluloid
65
93
  end
66
94
 
67
95
  if result
68
- result.value
96
+ (result.respond_to?(:value)) ? result.value : result
69
97
  else
70
- raise TimeoutError, "Timed out"
98
+ fail TimedOut, "Timed out"
71
99
  end
72
100
  end
73
101
  alias_method :call, :value
74
102
 
75
103
  # Signal this future with the given result value
76
104
  def signal(value)
105
+ return if @cancelled
77
106
  result = Result.new(value, self)
78
107
 
79
108
  @mutex.synchronize do
80
- raise "the future has already happened!" if @ready
109
+ fail "the future has already happened!" if @ready
81
110
 
82
111
  if @forwards
83
112
  @forwards.is_a?(Array) ? @forwards.each { |f| f << result } : @forwards << result
@@ -89,6 +118,14 @@ module Celluloid
89
118
  end
90
119
  alias_method :<<, :signal
91
120
 
121
+ def cancel(error)
122
+ response = ErrorResponse.new(@call, error)
123
+ signal response
124
+ @mutex.synchronize do
125
+ @cancelled = true
126
+ end
127
+ end
128
+
92
129
  # Inspect this Celluloid::Future
93
130
  alias_method :inspect, :to_s
94
131
 
@@ -97,7 +134,8 @@ module Celluloid
97
134
  attr_reader :future
98
135
 
99
136
  def initialize(result, future)
100
- @result, @future = result, future
137
+ @result = result
138
+ @future = future
101
139
  end
102
140
 
103
141
  def value
@@ -0,0 +1,125 @@
1
+ require "thread"
2
+
3
+ module Celluloid
4
+ class Group
5
+ class Pool < Group
6
+ # You do not want to use this. Truly, you do not. There is no scenario when you will.
7
+ # But. If you somehow do.. `Celluloid.group_class = Celluloid::Group::Pool` and weep.
8
+
9
+ attr_accessor :max_idle
10
+
11
+ def initialize
12
+ super
13
+ @mutex = Mutex.new
14
+ @idle_threads = []
15
+ @group = []
16
+ @busy_size = 0
17
+ @idle_size = 0
18
+
19
+ # TODO: should really adjust this based on usage
20
+ @max_idle = 16
21
+ end
22
+
23
+ def idle?
24
+ busy_size.count == 0
25
+ end
26
+
27
+ def busy?
28
+ busy_size.count > 0
29
+ end
30
+
31
+ attr_reader :busy_size
32
+
33
+ attr_reader :idle_size
34
+
35
+ # Get a thread from the pool, running the given block
36
+ def get(&block)
37
+ @mutex.synchronize do
38
+ assert_active
39
+
40
+ begin
41
+ if @idle_threads.empty?
42
+ thread = create
43
+ else
44
+ thread = @idle_threads.pop
45
+ @idle_size = @idle_threads.length
46
+ end
47
+ end until thread.status # handle crashed threads
48
+
49
+ thread.busy = true
50
+ @busy_size += 1
51
+ thread[:celluloid_queue] << block
52
+ thread
53
+ end
54
+ end
55
+
56
+ # Return a thread to the pool
57
+ def put(thread)
58
+ @mutex.synchronize do
59
+ thread.busy = false
60
+ if idle_size + 1 >= @max_idle
61
+ thread[:celluloid_queue] << nil
62
+ @busy_size -= 1
63
+ @group.delete(thread)
64
+ else
65
+ @idle_threads.push thread
66
+ @busy_size -= 1
67
+ @idle_size = @idle_threads.length
68
+ clean_thread_locals(thread)
69
+ end
70
+ end
71
+ end
72
+
73
+ def shutdown
74
+ @running = false
75
+ @mutex.synchronize do
76
+ finalize
77
+ @group.each do |thread|
78
+ thread[:celluloid_queue] << nil
79
+ end
80
+ @group.shift.kill until @group.empty?
81
+ @idle_threads.clear
82
+ @busy_size = 0
83
+ @idle_size = 0
84
+ end
85
+ end
86
+
87
+ private
88
+
89
+ # Create a new thread with an associated queue of procs to run
90
+ def create
91
+ queue = Queue.new
92
+ thread = Thread.new do
93
+ while proc = queue.pop
94
+ begin
95
+ proc.call
96
+ rescue ::Exception => ex
97
+ Internals::Logger.crash("thread crashed", ex)
98
+ ensure
99
+ put thread
100
+ end
101
+ end
102
+ end
103
+
104
+ thread[:celluloid_queue] = queue
105
+ # @idle_threads << thread
106
+ @group << thread
107
+ thread
108
+ end
109
+
110
+ # Clean the thread locals of an incoming thread
111
+ def clean_thread_locals(thread)
112
+ thread.keys.each do |key|
113
+ next if key == :celluloid_queue
114
+
115
+ # Ruby seems to lack an API for deleting thread locals. WTF, Ruby?
116
+ thread[key] = nil
117
+ end
118
+ end
119
+
120
+ def finalize
121
+ @max_idle = 0
122
+ end
123
+ end
124
+ end
125
+ end