celluloid 0.13.0 → 0.14.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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