celluloid 0.14.0 → 0.14.1.pre

Sign up to get free protection for your applications and to get access to all the features.
@@ -27,7 +27,7 @@ module Celluloid
27
27
  # normal Ruby objects wrapped in threads which communicate with asynchronous
28
28
  # messages.
29
29
  class Actor
30
- attr_reader :subject, :proxy, :tasks, :links, :mailbox, :thread, :name, :locals
30
+ attr_reader :subject, :proxy, :tasks, :links, :mailbox, :thread, :name
31
31
 
32
32
  class << self
33
33
  extend Forwardable
@@ -78,8 +78,7 @@ module Celluloid
78
78
  def all
79
79
  actors = []
80
80
  Thread.list.each do |t|
81
- next unless t.celluloid?
82
- next if t.task
81
+ next unless t.celluloid? && t.role == :actor
83
82
  actors << t.actor.proxy if t.actor && t.actor.respond_to?(:proxy)
84
83
  end
85
84
  actors
@@ -152,9 +151,8 @@ module Celluloid
152
151
  @running = true
153
152
  @exclusive = false
154
153
  @name = nil
155
- @locals = {}
156
154
 
157
- @thread = ThreadHandle.new do
155
+ @thread = ThreadHandle.new(:actor) do
158
156
  setup_thread
159
157
  run
160
158
  end
@@ -3,11 +3,30 @@ module Celluloid
3
3
 
4
4
  # ConditionVariable-like signaling between tasks and actors
5
5
  class Condition
6
+ class Waiter
7
+ def initialize(condition, task, mailbox)
8
+ @condition = condition
9
+ @task = task
10
+ @mailbox = mailbox
11
+ end
12
+ attr_reader :condition, :task
13
+
14
+ def <<(message)
15
+ @mailbox << message
16
+ end
17
+
18
+ def wait
19
+ message = @mailbox.receive do |msg|
20
+ msg.is_a?(SignalConditionRequest) && msg.task == Thread.current
21
+ end
22
+ message.value
23
+ end
24
+ end
25
+
6
26
  attr_reader :owner
7
27
 
8
28
  def initialize
9
29
  @mutex = Mutex.new
10
- @owner = Thread.current[:celluloid_actor]
11
30
  @tasks = []
12
31
  end
13
32
 
@@ -15,14 +34,18 @@ module Celluloid
15
34
  def wait
16
35
  raise ConditionError, "cannot wait for signals while exclusive" if Celluloid.exclusive?
17
36
 
37
+ if Thread.current[:celluloid_actor]
38
+ task = Task.current
39
+ else
40
+ task = Thread.current
41
+ end
42
+ waiter = Waiter.new(self, task, Celluloid.mailbox)
43
+
18
44
  @mutex.synchronize do
19
- actor = Thread.current[:celluloid_actor]
20
- raise ConditionError, "can't wait for conditions outside actors" unless actor
21
- raise ConditionError, "can't wait unless owner" unless actor == @owner
22
- @tasks << Task.current
45
+ @tasks << waiter
23
46
  end
24
47
 
25
- result = Task.suspend :condwait
48
+ result = Celluloid.suspend :condwait, waiter
26
49
  raise result if result.is_a? ConditionError
27
50
  result
28
51
  end
@@ -30,10 +53,8 @@ module Celluloid
30
53
  # Send a signal to the first task waiting on this condition
31
54
  def signal(value = nil)
32
55
  @mutex.synchronize do
33
- raise ConditionError, "no owner for this condition" unless @owner
34
-
35
- if task = @tasks.shift
36
- @owner.mailbox << SignalConditionRequest.new(task, value)
56
+ if waiter = @tasks.shift
57
+ waiter << SignalConditionRequest.new(waiter.task, value)
37
58
  else
38
59
  Logger.debug("Celluloid::Condition signaled spuriously")
39
60
  end
@@ -43,28 +64,11 @@ module Celluloid
43
64
  # Broadcast a value to all waiting tasks
44
65
  def broadcast(value = nil)
45
66
  @mutex.synchronize do
46
- raise ConditionError, "no owner for this condition" unless @owner
47
-
48
- @tasks.each { |task| @owner.mailbox << SignalConditionRequest.new(task, value) }
67
+ @tasks.each { |waiter| waiter << SignalConditionRequest.new(waiter.task, value) }
49
68
  @tasks.clear
50
69
  end
51
70
  end
52
71
 
53
- # Change the owner of this condition
54
- def owner=(actor)
55
- @mutex.synchronize do
56
- if @owner != actor
57
- @tasks.each do |task|
58
- ex = ConditionError.new("ownership changed")
59
- @owner.mailbox << SignalConditionRequest.new(task, ex)
60
- end
61
- @tasks.clear
62
- end
63
-
64
- @owner = actor
65
- end
66
- end
67
-
68
72
  alias_method :inspect, :to_s
69
73
  end
70
74
  end
@@ -4,38 +4,30 @@ module Celluloid
4
4
  # Celluloid::Future objects allow methods and blocks to run in the
5
5
  # background, their values requested later
6
6
  class Future
7
+ def self.new(*args, &block)
8
+ return super unless block
9
+
10
+ future = new
11
+ Celluloid.internal_pool.get do
12
+ Thread.current.role = :future
13
+ begin
14
+ call = SyncCall.new(future, :call, args)
15
+ call.dispatch(block)
16
+ rescue
17
+ # Exceptions in blocks will get raised when the value is retrieved
18
+ end
19
+ end
20
+ future
21
+ end
22
+
7
23
  attr_reader :address
8
-
9
- # Create a future bound to a given receiver, or with a block to compute
10
- def initialize(*args, &block)
24
+
25
+ def initialize
11
26
  @address = Celluloid.uuid
12
27
  @mutex = Mutex.new
13
28
  @ready = false
14
29
  @result = nil
15
30
  @forwards = nil
16
-
17
- if block
18
- @call = SyncCall.new(self, :call, args)
19
- Celluloid.internal_pool.get do
20
- begin
21
- @call.dispatch(block)
22
- rescue
23
- # Exceptions in blocks will get raised when the value is retrieved
24
- end
25
- end
26
- else
27
- @call = nil
28
- end
29
- end
30
-
31
- # Execute the given method in future context
32
- def execute(receiver, method, args, block)
33
- @mutex.synchronize do
34
- raise "already calling" if @call
35
- @call = SyncCall.new(self, method, args, block)
36
- end
37
-
38
- receiver << @call
39
31
  end
40
32
 
41
33
  # Check if this future has a value yet
@@ -49,7 +41,6 @@ module Celluloid
49
41
 
50
42
  begin
51
43
  @mutex.lock
52
- raise "no call requested" unless @call
53
44
 
54
45
  if @ready
55
46
  ready = true
@@ -42,7 +42,7 @@ module Celluloid
42
42
  if @pool.size >= @max_idle
43
43
  thread[:celluloid_queue] << nil
44
44
  else
45
- thread.recycle
45
+ clean_thread_locals(thread)
46
46
  @pool << thread
47
47
  @idle_size += 1
48
48
  @busy_size -= 1
@@ -69,6 +69,16 @@ module Celluloid
69
69
  thread
70
70
  end
71
71
 
72
+ # Clean the thread locals of an incoming thread
73
+ def clean_thread_locals(thread)
74
+ thread.keys.each do |key|
75
+ next if key == :celluloid_queue
76
+
77
+ # Ruby seems to lack an API for deleting thread locals. WTF, Ruby?
78
+ thread[key] = nil
79
+ end
80
+ end
81
+
72
82
  def shutdown
73
83
  @mutex.synchronize do
74
84
  @max_idle = 0
@@ -17,8 +17,16 @@ module Celluloid
17
17
  # FIXME: nicer exception
18
18
  raise "Cannot use blocks with futures yet"
19
19
  end
20
+
20
21
  future = Future.new
21
- future.execute(@mailbox, meth, args, block)
22
+ call = SyncCall.new(future, meth, args, block)
23
+
24
+ begin
25
+ @mailbox << call
26
+ rescue MailboxError
27
+ raise DeadActorError, "attempted to call a dead actor"
28
+ end
29
+
22
30
  future
23
31
  end
24
32
  end
@@ -25,7 +25,7 @@ module Celluloid
25
25
  def snapshot
26
26
  Thread.list.each do |thread|
27
27
  if thread.celluloid?
28
- next if thread.task
28
+ next unless thread.role == :actor
29
29
  @actors << snapshot_actor(thread.actor) if thread.actor
30
30
  else
31
31
  @threads << snapshot_thread(thread)
@@ -57,6 +57,7 @@ module Celluloid
57
57
  def initialize(task, value)
58
58
  @task, @value = task, value
59
59
  end
60
+ attr_reader :task, :value
60
61
 
61
62
  def call
62
63
  @task.resume(@value)
@@ -6,6 +6,8 @@ module Celluloid
6
6
 
7
7
  def create
8
8
  @fiber = Fiber.new do
9
+ # FIXME: cannot use the writer as specs run inside normal Threads
10
+ Thread.current[:celluloid_role] = :actor
9
11
  yield
10
12
  end
11
13
  end
@@ -30,4 +32,4 @@ module Celluloid
30
32
  # If we're getting this the task should already be dead
31
33
  end
32
34
  end
33
- end
35
+ end
@@ -13,6 +13,7 @@ module Celluloid
13
13
 
14
14
  def create
15
15
  @thread = Celluloid.internal_pool.get do
16
+ Thread.current.role = :task
16
17
  begin
17
18
  ex = @resume_queue.pop
18
19
  raise ex if ex.is_a?(Task::TerminatedError)
@@ -2,20 +2,19 @@ require 'celluloid/fiber'
2
2
 
3
3
  module Celluloid
4
4
  class Thread < ::Thread
5
- # FIXME: these should be replaced using APIs on Celluloid::Thread itself
6
- # e.g. Thread.current[:celluloid_actor] => Thread.current.actor
7
- CELLULOID_LOCALS = [
8
- :celluloid_actor,
9
- :celluloid_mailbox,
10
- :celluloid_queue,
11
- :celluloid_task,
12
- :celluloid_chain_id
13
- ]
14
-
15
5
  def celluloid?
16
6
  true
17
7
  end
18
8
 
9
+ # Obtain the role of this thread
10
+ def role
11
+ self[:celluloid_role]
12
+ end
13
+
14
+ def role=(role)
15
+ self[:celluloid_role] = role
16
+ end
17
+
19
18
  # Obtain the Celluloid::Actor object for this thread
20
19
  def actor
21
20
  self[:celluloid_actor]
@@ -35,58 +34,5 @@ module Celluloid
35
34
  def call_chain_id
36
35
  self[:celluloid_chain_id]
37
36
  end
38
-
39
- #
40
- # Override default thread local behavior, making thread locals actor-local
41
- #
42
-
43
- # Obtain an actor-local value
44
- def [](key)
45
- actor = super(:celluloid_actor)
46
- if !actor || CELLULOID_LOCALS.include?(key)
47
- super(key)
48
- else
49
- actor.locals[key]
50
- end
51
- end
52
-
53
- # Set an actor-local value
54
- def []=(key, value)
55
- actor = self[:celluloid_actor]
56
- if !actor || CELLULOID_LOCALS.include?(key)
57
- super(key, value)
58
- else
59
- actor.locals[key] = value
60
- end
61
- end
62
-
63
- # Obtain the keys to all actor-locals
64
- def keys
65
- actor = self[:celluloid_actor]
66
- if actor
67
- actor.locals.keys
68
- else
69
- super
70
- end
71
- end
72
-
73
- # Is the given actor local set?
74
- def key?(key)
75
- actor = self[:celluloid_actor]
76
- if actor
77
- actor.locals.has_key?(key)
78
- else
79
- super
80
- end
81
- end
82
-
83
- # Clear thread state so it can be reused via thread pools
84
- def recycle
85
- # This thread local mediates access to the others, so we must clear it first
86
- self[:celluloid_actor] = nil
87
-
88
- # Clearing :celluloid_queue would break the thread pool!
89
- keys.each { |key| self[key] = nil unless key == :celluloid_queue }
90
- end
91
37
  end
92
38
  end
@@ -3,11 +3,12 @@ module Celluloid
3
3
  # accidentally do things to threads which have been returned to the pool,
4
4
  # such as, say, killing them
5
5
  class ThreadHandle
6
- def initialize
6
+ def initialize(role = nil)
7
7
  @mutex = Mutex.new
8
8
  @join = ConditionVariable.new
9
9
 
10
10
  @thread = Celluloid.internal_pool.get do
11
+ Thread.current.role = role
11
12
  begin
12
13
  yield
13
14
  ensure
@@ -1,4 +1,4 @@
1
1
  module Celluloid
2
- VERSION = '0.14.0'
2
+ VERSION = '0.14.1.pre'
3
3
  def self.version; VERSION; end
4
4
  end
@@ -323,41 +323,6 @@ shared_examples "Celluloid::Actor examples" do |included_module, task_klass|
323
323
  end
324
324
  end
325
325
 
326
- context "thread locals" do
327
- let(:example_class) do
328
- Class.new do
329
- include included_module
330
- task_class task_klass
331
-
332
- def initialize(value)
333
- Thread.current[:example_thread_local] = value
334
- end
335
-
336
- def value
337
- Thread.current[:example_thread_local]
338
- end
339
-
340
- def deferred_value
341
- defer do
342
- Thread.current[:example_thread_local]
343
- end
344
- end
345
- end
346
- end
347
-
348
- let(:example_value) { "foobar" }
349
-
350
- it "preserves thread locals between tasks" do
351
- actor = example_class.new(example_value)
352
- actor.value.should eq example_value
353
- end
354
-
355
- it "isolates thread locals in defer blocks" do
356
- actor = example_class.new(example_value)
357
- actor.deferred_value.should eq nil
358
- end
359
- end
360
-
361
326
  context :linking do
362
327
  before :each do
363
328
  @kevin = actor_class.new "Kevin Bacon" # Some six degrees action here
metadata CHANGED
@@ -1,83 +1,94 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: celluloid
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.14.0
4
+ version: 0.14.1.pre
5
+ prerelease: 7
5
6
  platform: ruby
6
7
  authors:
7
8
  - Tony Arcieri
8
9
  autorequire:
9
10
  bindir: bin
10
11
  cert_chain: []
11
- date: 2013-05-07 00:00:00.000000000 Z
12
+ date: 2013-05-31 00:00:00.000000000 Z
12
13
  dependencies:
13
14
  - !ruby/object:Gem::Dependency
14
15
  name: timers
15
16
  requirement: !ruby/object:Gem::Requirement
17
+ none: false
16
18
  requirements:
17
- - - '>='
19
+ - - ! '>='
18
20
  - !ruby/object:Gem::Version
19
21
  version: 1.0.0
20
22
  type: :runtime
21
23
  prerelease: false
22
24
  version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
23
26
  requirements:
24
- - - '>='
27
+ - - ! '>='
25
28
  - !ruby/object:Gem::Version
26
29
  version: 1.0.0
27
30
  - !ruby/object:Gem::Dependency
28
31
  name: rake
29
32
  requirement: !ruby/object:Gem::Requirement
33
+ none: false
30
34
  requirements:
31
- - - '>='
35
+ - - ! '>='
32
36
  - !ruby/object:Gem::Version
33
37
  version: '0'
34
38
  type: :development
35
39
  prerelease: false
36
40
  version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
37
42
  requirements:
38
- - - '>='
43
+ - - ! '>='
39
44
  - !ruby/object:Gem::Version
40
45
  version: '0'
41
46
  - !ruby/object:Gem::Dependency
42
47
  name: rspec
43
48
  requirement: !ruby/object:Gem::Requirement
49
+ none: false
44
50
  requirements:
45
- - - '>='
51
+ - - ! '>='
46
52
  - !ruby/object:Gem::Version
47
53
  version: '0'
48
54
  type: :development
49
55
  prerelease: false
50
56
  version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
51
58
  requirements:
52
- - - '>='
59
+ - - ! '>='
53
60
  - !ruby/object:Gem::Version
54
61
  version: '0'
55
62
  - !ruby/object:Gem::Dependency
56
63
  name: guard-rspec
57
64
  requirement: !ruby/object:Gem::Requirement
65
+ none: false
58
66
  requirements:
59
- - - '>='
67
+ - - ! '>='
60
68
  - !ruby/object:Gem::Version
61
69
  version: '0'
62
70
  type: :development
63
71
  prerelease: false
64
72
  version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
65
74
  requirements:
66
- - - '>='
75
+ - - ! '>='
67
76
  - !ruby/object:Gem::Version
68
77
  version: '0'
69
78
  - !ruby/object:Gem::Dependency
70
79
  name: benchmark_suite
71
80
  requirement: !ruby/object:Gem::Requirement
81
+ none: false
72
82
  requirements:
73
- - - '>='
83
+ - - ! '>='
74
84
  - !ruby/object:Gem::Version
75
85
  version: '0'
76
86
  type: :development
77
87
  prerelease: false
78
88
  version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
79
90
  requirements:
80
- - - '>='
91
+ - - ! '>='
81
92
  - !ruby/object:Gem::Version
82
93
  version: '0'
83
94
  description: Celluloid enables people to build concurrent programs out of concurrent
@@ -143,25 +154,26 @@ files:
143
154
  homepage: https://github.com/celluloid/celluloid
144
155
  licenses:
145
156
  - MIT
146
- metadata: {}
147
157
  post_install_message:
148
158
  rdoc_options: []
149
159
  require_paths:
150
160
  - lib
151
161
  required_ruby_version: !ruby/object:Gem::Requirement
162
+ none: false
152
163
  requirements:
153
- - - '>='
164
+ - - ! '>='
154
165
  - !ruby/object:Gem::Version
155
166
  version: 1.9.2
156
167
  required_rubygems_version: !ruby/object:Gem::Requirement
168
+ none: false
157
169
  requirements:
158
- - - '>='
170
+ - - ! '>='
159
171
  - !ruby/object:Gem::Version
160
172
  version: 1.3.6
161
173
  requirements: []
162
174
  rubyforge_project:
163
- rubygems_version: 2.0.3
175
+ rubygems_version: 1.8.23
164
176
  signing_key:
165
- specification_version: 4
177
+ specification_version: 3
166
178
  summary: Actor-based concurrent object framework for Ruby
167
179
  test_files: []
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: 14c9f61bd8924c3c5ab4265d3a7ca3d6c43e0fcc
4
- data.tar.gz: 65e57a0119a677575a6fbf1663717812f3ca5e1c
5
- SHA512:
6
- metadata.gz: 0f92f13dde36f666893891344073856a4283c402bf259bffa6d076aec33d3c607805ca84373fba854d198b17fe27415eee41283485b7cc4957f715aff00e9f06
7
- data.tar.gz: 4b1cd7b742b028163e9a1af68343b5f3489997ced697383ae9e1737d01f0d9b4f16bc3406e8ea22f8c8668533e4a1ca2cb5a3184ce4b0b5cd7481b3e33e0944d