celluloid 0.14.0 → 0.14.1.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.
@@ -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