celluloid 0.13.0 → 0.14.0.pre

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 (38) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +1 -2
  3. data/lib/celluloid.rb +84 -32
  4. data/lib/celluloid/actor.rb +35 -30
  5. data/lib/celluloid/autostart.rb +0 -13
  6. data/lib/celluloid/calls.rb +71 -23
  7. data/lib/celluloid/core_ext.rb +3 -14
  8. data/lib/celluloid/cpu_counter.rb +1 -1
  9. data/lib/celluloid/evented_mailbox.rb +82 -0
  10. data/lib/celluloid/fsm.rb +2 -0
  11. data/lib/celluloid/future.rb +4 -4
  12. data/lib/celluloid/internal_pool.rb +11 -8
  13. data/lib/celluloid/legacy.rb +14 -13
  14. data/lib/celluloid/logging/incident_logger.rb +2 -2
  15. data/lib/celluloid/mailbox.rb +16 -0
  16. data/lib/celluloid/method.rb +7 -7
  17. data/lib/celluloid/notifications.rb +1 -1
  18. data/lib/celluloid/proxies/abstract_proxy.rb +1 -1
  19. data/lib/celluloid/proxies/actor_proxy.rb +23 -27
  20. data/lib/celluloid/proxies/async_proxy.rb +18 -6
  21. data/lib/celluloid/proxies/block_proxy.rb +29 -0
  22. data/lib/celluloid/proxies/future_proxy.rb +9 -3
  23. data/lib/celluloid/proxies/sync_proxy.rb +31 -0
  24. data/lib/celluloid/receivers.rb +1 -1
  25. data/lib/celluloid/responses.rb +13 -1
  26. data/lib/celluloid/stack_dump.rb +8 -5
  27. data/lib/celluloid/supervision_group.rb +6 -4
  28. data/lib/celluloid/tasks.rb +63 -2
  29. data/lib/celluloid/tasks/task_fiber.rb +6 -46
  30. data/lib/celluloid/tasks/task_thread.rb +8 -44
  31. data/lib/celluloid/thread.rb +82 -0
  32. data/lib/celluloid/thread_handle.rb +3 -2
  33. data/lib/celluloid/version.rb +1 -1
  34. data/spec/support/actor_examples.rb +116 -53
  35. data/spec/support/example_actor_class.rb +7 -1
  36. data/spec/support/mailbox_examples.rb +29 -3
  37. data/spec/support/task_examples.rb +11 -9
  38. metadata +21 -29
@@ -1,20 +1,9 @@
1
1
  require 'celluloid/fiber'
2
2
 
3
- # Monkeypatch Thread to allow lazy access to its Celluloid::Mailbox
4
3
  class Thread
5
4
  attr_accessor :uuid_counter, :uuid_limit
6
5
 
7
- # Retrieve the mailbox for the current thread or lazily initialize it
8
- def self.mailbox
9
- current[:celluloid_mailbox] ||= Celluloid::Mailbox.new
6
+ def celluloid?
7
+ false
10
8
  end
11
-
12
- # Receive a message either as an actor or through the local mailbox
13
- def self.receive(timeout = nil, &block)
14
- if Celluloid.actor?
15
- Celluloid.receive(timeout, &block)
16
- else
17
- mailbox.receive(timeout, &block)
18
- end
19
- end
20
- end
9
+ end
@@ -12,7 +12,7 @@ module Celluloid
12
12
  Dir["/sys/devices/system/cpu/cpu*"].select { |n| n=~/cpu\d+/ }.count
13
13
  end
14
14
  when 'mingw', 'mswin'
15
- @cores = Integer(`SET NUMBER_OF_PROCESSORS`[/\d+/])
15
+ @cores = Integer(ENV["NUMBER_OF_PROCESSORS"][/\d+/])
16
16
  else
17
17
  @cores = nil
18
18
  end
@@ -0,0 +1,82 @@
1
+ module Celluloid
2
+ # An alternative implementation of Celluloid::Mailbox using Reactor
3
+ class EventedMailbox < Celluloid::Mailbox
4
+ attr_reader :reactor
5
+
6
+ def initialize(reactor_class)
7
+ super()
8
+ # @condition won't be used in the class.
9
+ @reactor = reactor_class.new
10
+ end
11
+
12
+ # Add a message to the Mailbox
13
+ def <<(message)
14
+ @mutex.lock
15
+ begin
16
+ if mailbox_full
17
+ Logger.debug "Discarded message: #{message}"
18
+ return
19
+ end
20
+ if message.is_a?(SystemEvent)
21
+ # Silently swallow system events sent to dead actors
22
+ return if @dead
23
+
24
+ # SystemEvents are high priority messages so they get added to the
25
+ # head of our message queue instead of the end
26
+ @messages.unshift message
27
+ else
28
+ raise MailboxError, "dead recipient" if @dead
29
+ @messages << message
30
+ end
31
+
32
+ current_actor = Thread.current[:celluloid_actor]
33
+ @reactor.wakeup unless current_actor && current_actor.mailbox == self
34
+ rescue IOError
35
+ raise MailboxError, "dead recipient"
36
+ ensure
37
+ @mutex.unlock rescue nil
38
+ end
39
+ nil
40
+ end
41
+
42
+ # Receive a message from the Mailbox
43
+ def receive(timeout = nil, &block)
44
+ message = next_message(block)
45
+
46
+ until message
47
+ if timeout
48
+ now = Time.now
49
+ wait_until ||= now + timeout
50
+ wait_interval = wait_until - now
51
+ return if wait_interval < 0
52
+ else
53
+ wait_interval = nil
54
+ end
55
+
56
+ @reactor.run_once(wait_interval)
57
+ message = next_message(block)
58
+ end
59
+
60
+ message
61
+ rescue IOError
62
+ shutdown # force shutdown of the mailbox
63
+ raise MailboxShutdown, "mailbox shutdown called during receive"
64
+ end
65
+
66
+ # Obtain the next message from the mailbox that matches the given block
67
+ def next_message(block)
68
+ @mutex.lock
69
+ begin
70
+ super(&block)
71
+ ensure
72
+ @mutex.unlock rescue nil
73
+ end
74
+ end
75
+
76
+ # Cleanup any IO objects this Mailbox may be using
77
+ def shutdown
78
+ @reactor.shutdown
79
+ super
80
+ end
81
+ end
82
+ end
@@ -62,6 +62,7 @@ module Celluloid
62
62
  # Be kind and call super if you must redefine initialize
63
63
  def initialize(actor = nil)
64
64
  @state = self.class.default_state
65
+ @delayed_transition = nil
65
66
  @actor = actor
66
67
  @actor ||= Celluloid.current_actor if Celluloid.actor?
67
68
  end
@@ -166,6 +167,7 @@ module Celluloid
166
167
 
167
168
  def initialize(name, transitions = nil, &block)
168
169
  @name, @block = name, block
170
+ @transitions = nil
169
171
  @transitions = Array(transitions).map { |t| t.to_sym } if transitions
170
172
  end
171
173
 
@@ -57,11 +57,11 @@ module Celluloid
57
57
  else
58
58
  case @forwards
59
59
  when Array
60
- @forwards << Thread.mailbox
60
+ @forwards << Celluloid.mailbox
61
61
  when NilClass
62
- @forwards = Thread.mailbox
62
+ @forwards = Celluloid.mailbox
63
63
  else
64
- @forwards = [@forwards, Thread.mailbox]
64
+ @forwards = [@forwards, Celluloid.mailbox]
65
65
  end
66
66
  end
67
67
  ensure
@@ -69,7 +69,7 @@ module Celluloid
69
69
  end
70
70
 
71
71
  unless ready
72
- result = Thread.receive(timeout) do |msg|
72
+ result = Celluloid.receive(timeout) do |msg|
73
73
  msg.is_a?(Future::Result) && msg.future == self
74
74
  end
75
75
  end
@@ -10,6 +10,10 @@ module Celluloid
10
10
  @mutex = Mutex.new
11
11
  @busy_size = @idle_size = 0
12
12
 
13
+ reset
14
+ end
15
+
16
+ def reset
13
17
  # TODO: should really adjust this based on usage
14
18
  @max_idle = 16
15
19
  end
@@ -38,7 +42,7 @@ module Celluloid
38
42
  if @pool.size >= @max_idle
39
43
  thread[:celluloid_queue] << nil
40
44
  else
41
- clean_thread_locals(thread)
45
+ thread.recycle
42
46
  @pool << thread
43
47
  @idle_size += 1
44
48
  @busy_size -= 1
@@ -65,13 +69,12 @@ module Celluloid
65
69
  thread
66
70
  end
67
71
 
68
- # Clean the thread locals of an incoming thread
69
- def clean_thread_locals(thread)
70
- thread.keys.each do |key|
71
- next if key == :celluloid_queue
72
-
73
- # Ruby seems to lack an API for deleting thread locals. WTF, Ruby?
74
- thread[key] = nil
72
+ def shutdown
73
+ @mutex.synchronize do
74
+ @max_idle = 0
75
+ @pool.each do |thread|
76
+ thread[:celluloid_queue] << nil
77
+ end
75
78
  end
76
79
  end
77
80
  end
@@ -9,9 +9,9 @@ module Celluloid
9
9
 
10
10
  unbanged_meth = meth.to_s
11
11
  unbanged_meth.slice!(-1, 1)
12
- Actor.async @mailbox, unbanged_meth, *args, &block
12
+ async unbanged_meth, *args, &block
13
13
  else
14
- Actor.call @mailbox, meth, *args, &block
14
+ super
15
15
  end
16
16
  end
17
17
  end
@@ -27,20 +27,21 @@ module Celluloid
27
27
  unbanged_meth = meth.to_s.sub(/!$/, '')
28
28
  args.unshift unbanged_meth
29
29
 
30
- call = AsyncCall.new(:__send__, args, block)
31
- begin
32
- Thread.current[:celluloid_actor].mailbox << call
33
- rescue MailboxError
34
- # Silently swallow asynchronous calls to dead actors. There's no way
35
- # to reliably generate DeadActorErrors for async calls, so users of
36
- # async calls should find other ways to deal with actors dying
37
- # during an async call (i.e. linking/supervisors)
38
- end
39
-
30
+ async :__send__, *args, &block
40
31
  return
41
32
  end
42
33
 
43
34
  super
44
35
  end
45
36
  end
46
- end
37
+ end
38
+
39
+ class Thread
40
+ def self.mailbox
41
+ Celluloid.mailbox
42
+ end
43
+
44
+ def self.receive(timeout = nil, &block)
45
+ Celluloid.receive(timeout, &block)
46
+ end
47
+ end
@@ -49,9 +49,9 @@ module Celluloid
49
49
  @sizelimit = options[:sizelimit] || 100
50
50
 
51
51
  @buffer_mutex = Mutex.new
52
- @buffers = Hash.new do |progname_hash, progname|
52
+ @buffers = Hash.new do |progname_hash, _progname|
53
53
  @buffer_mutex.synchronize do
54
- progname_hash[progname] = Hash.new do |severity_hash, severity|
54
+ progname_hash[_progname] = Hash.new do |severity_hash, severity|
55
55
  severity_hash[severity] = RingBuffer.new(@sizelimit)
56
56
  end
57
57
  end
@@ -11,6 +11,7 @@ module Celluloid
11
11
 
12
12
  # A unique address at which this mailbox can be found
13
13
  attr_reader :address
14
+ attr_accessor :max_size
14
15
 
15
16
  def initialize
16
17
  @address = Celluloid.uuid
@@ -18,12 +19,17 @@ module Celluloid
18
19
  @mutex = Mutex.new
19
20
  @dead = false
20
21
  @condition = ConditionVariable.new
22
+ @max_size = nil
21
23
  end
22
24
 
23
25
  # Add a message to the Mailbox
24
26
  def <<(message)
25
27
  @mutex.lock
26
28
  begin
29
+ if mailbox_full
30
+ Logger.debug "Discarded message: #{message}"
31
+ return
32
+ end
27
33
  if message.is_a?(SystemEvent)
28
34
  # Silently swallow system events sent to dead actors
29
35
  return if @dead
@@ -125,5 +131,15 @@ module Celluloid
125
131
  def inspect
126
132
  "#<#{self.class}:#{object_id.to_s(16)} @messages=[#{map { |m| m.inspect }.join(', ')}]>"
127
133
  end
134
+
135
+ # Number of messages in the Mailbox
136
+ def size
137
+ @mutex.synchronize { @messages.size }
138
+ end
139
+
140
+ private
141
+ def mailbox_full
142
+ @max_size && @messages.size >= @max_size
143
+ end
128
144
  end
129
145
  end
@@ -2,23 +2,23 @@ module Celluloid
2
2
  # Method handles that route through an actor proxy
3
3
  class Method
4
4
 
5
- def initialize(actor, name)
6
- raise NameError, "undefined method `#{name}'" unless actor.respond_to? name
5
+ def initialize(proxy, name)
6
+ raise NameError, "undefined method `#{name}'" unless proxy.respond_to? name
7
7
 
8
- @actor, @name = actor, name
9
- @klass = @actor.class
8
+ @proxy, @name = proxy, name
9
+ @klass = @proxy.class
10
10
  end
11
11
 
12
12
  def arity
13
- @actor._send_(:method, @name).arity
13
+ @proxy.method_missing(:method, @name).arity
14
14
  end
15
15
 
16
16
  def call(*args, &block)
17
- @actor._send_(@name, *args, &block)
17
+ @proxy.__send__(@name, *args, &block)
18
18
  end
19
19
 
20
20
  def inspect
21
- "#<Celluloid::Method #{@klass}#{@name}>"
21
+ "#<Celluloid::Method #{@klass}##{@name}>"
22
22
  end
23
23
  end
24
24
  end
@@ -67,7 +67,7 @@ module Celluloid
67
67
  end
68
68
 
69
69
  def publish(pattern, *args)
70
- Actor.async(actor.mailbox, method, pattern, *args)
70
+ actor.async method, pattern, *args
71
71
  end
72
72
 
73
73
  def subscribed_to?(pattern)
@@ -5,7 +5,7 @@ module Celluloid
5
5
  needed = [:object_id, :__id__, :hash] - instance_methods
6
6
  if needed.any?
7
7
  include ::Kernel.dup.module_eval {
8
- undef_method *(instance_methods - needed)
8
+ undef_method(*(instance_methods - needed))
9
9
  self
10
10
  }
11
11
 
@@ -1,55 +1,54 @@
1
1
  module Celluloid
2
2
  # A proxy object returned from Celluloid::Actor.new/new_link which converts
3
3
  # the normal Ruby method protocol into an inter-actor message protocol
4
- class ActorProxy
5
- attr_reader :mailbox, :thread
4
+ class ActorProxy < SyncProxy
5
+ attr_reader :thread
6
6
 
7
7
  def initialize(actor)
8
- @mailbox, @thread, @klass = actor.mailbox, actor.thread, actor.subject.class.to_s
8
+ @thread = actor.thread
9
9
 
10
- @async_proxy = AsyncProxy.new(actor)
11
- @future_proxy = FutureProxy.new(actor)
10
+ super(actor.mailbox, actor.subject.class.to_s)
11
+ @sync_proxy = SyncProxy.new(@mailbox, @klass)
12
+ @async_proxy = AsyncProxy.new(@mailbox, @klass)
13
+ @future_proxy = FutureProxy.new(@mailbox, @klass)
12
14
  end
13
-
14
- # allow querying the real class
15
- alias :__class__ :class
16
-
15
+
17
16
  def class
18
- Actor.call @mailbox, :__send__, :class
17
+ method_missing :__send__, :class
19
18
  end
20
19
 
21
20
  def send(meth, *args, &block)
22
- Actor.call @mailbox, :send, meth, *args, &block
21
+ method_missing :send, meth, *args, &block
23
22
  end
24
23
 
25
24
  def _send_(meth, *args, &block)
26
- Actor.call @mailbox, :__send__, meth, *args, &block
25
+ method_missing :__send__, meth, *args, &block
27
26
  end
28
27
 
29
28
  def inspect
30
- Actor.call(@mailbox, :inspect)
29
+ method_missing :inspect
31
30
  rescue DeadActorError
32
- "#<Celluloid::Actor(#{@klass}) dead>"
31
+ "#<Celluloid::ActorProxy(#{@klass}) dead>"
33
32
  end
34
33
 
35
34
  def name
36
- Actor.call @mailbox, :name
35
+ method_missing :name
37
36
  end
38
37
 
39
38
  def is_a?(klass)
40
- Actor.call @mailbox, :is_a?, klass
39
+ method_missing :is_a?, klass
41
40
  end
42
41
 
43
42
  def kind_of?(klass)
44
- Actor.call @mailbox, :kind_of?, klass
43
+ method_missing :kind_of?, klass
45
44
  end
46
45
 
47
46
  def respond_to?(meth, include_private = false)
48
- Actor.call @mailbox, :respond_to?, meth, include_private
47
+ method_missing :respond_to?, meth, include_private
49
48
  end
50
49
 
51
50
  def methods(include_ancestors = true)
52
- Actor.call @mailbox, :methods, include_ancestors
51
+ method_missing :methods, include_ancestors
53
52
  end
54
53
 
55
54
  def method(name)
@@ -61,13 +60,15 @@ module Celluloid
61
60
  end
62
61
 
63
62
  def to_s
64
- Actor.call @mailbox, :to_s
63
+ method_missing :to_s
65
64
  end
66
65
 
66
+ alias_method :sync, :method_missing
67
+
67
68
  # Obtain an async proxy or explicitly invoke a named async method
68
69
  def async(method_name = nil, *args, &block)
69
70
  if method_name
70
- Actor.async @mailbox, method_name, *args, &block
71
+ @async_proxy.method_missing method_name, *args, &block
71
72
  else
72
73
  @async_proxy
73
74
  end
@@ -76,7 +77,7 @@ module Celluloid
76
77
  # Obtain a future proxy or explicitly invoke a named future method
77
78
  def future(method_name = nil, *args, &block)
78
79
  if method_name
79
- Actor.future @mailbox, method_name, *args, &block
80
+ @future_proxy.method_missing method_name, *args, &block
80
81
  else
81
82
  @future_proxy
82
83
  end
@@ -94,10 +95,5 @@ module Celluloid
94
95
  ::Kernel.raise DeadActorError, "actor already terminated" unless alive?
95
96
  @mailbox << TerminationRequest.new
96
97
  end
97
-
98
- # method_missing black magic to call bang predicate methods asynchronously
99
- def method_missing(meth, *args, &block)
100
- Actor.call @mailbox, meth, *args, &block
101
- end
102
98
  end
103
99
  end