agent 0.9.1 → 0.10.0

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 22c5bf05cd41b0171dfce316431f8c42434fa3c1
4
+ data.tar.gz: 96b5280262b5c4412fd366b6eb897031fa6fd709
5
+ SHA512:
6
+ metadata.gz: 3aa2fcea9b6cf2e55abd8ff3f3139f12f88e8fae2304bddb4d9076ce2d5ba99959fe64cd60291a24433da577aeea104fa6c4fcf7f1d9d9f1e98c88b8037a5b0f
7
+ data.tar.gz: dbf9aedd6e99043347dba38415f80d7fa166afa1d36abcea0d3c61964d9b09e53f3b5c681e194836b3e7db390166580a749d801a2318ed5076b95a9c05a92618
@@ -0,0 +1 @@
1
+ agent
@@ -0,0 +1,16 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.8.7
4
+ - ree
5
+ - 1.9.2
6
+ - 1.9.3
7
+ - 2.0
8
+ - 2.1
9
+ - 2.2
10
+ - ruby-head
11
+ - jruby-18mode
12
+ - jruby-19mode
13
+ - jruby-head
14
+ - rbx-2.2
15
+ - rbx-2.4
16
+
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- agent (0.9.0)
4
+ agent (0.9.1)
5
5
 
6
6
  GEM
7
7
  remote: http://rubygems.org/
data/README.md CHANGED
@@ -1,9 +1,9 @@
1
- # Agent
1
+ # Agent [![Build Status](https://travis-ci.org/igrigorik/agent.png)](https://travis-ci.org/igrigorik/agent)
2
2
 
3
3
  Agent is an attempt at [Go-like (CSP / pi-calculus) concurrency in Ruby](http://www.igvita.com/2010/12/02/concurrency-with-actors-goroutines-ruby/), but with an additional twist. It is a collection of different [process calculi](http://en.wikipedia.org/wiki/Process_calculus) primitives and patterns, with no specific, idiomatic affiliation to any specific implementation. A few available patterns so far:
4
4
 
5
5
  - Goroutines on top of green Ruby threads
6
- - Named, Typed, Bufferd and Unbufferred in-memory "channels"
6
+ - Named, Typed, Buffered and Unbufferred in-memory "channels"
7
7
  - Selectable "channels"
8
8
 
9
9
  This gem is a work in progress, so treat it as such.
@@ -10,23 +10,20 @@ module Agent
10
10
  end
11
11
 
12
12
  class Channel
13
- attr_reader :name, :direction, :type, :max
13
+ ::Agent::Push::SKIP_MARSHAL_TYPES << ::Agent::Channel
14
14
 
15
- def initialize(*args)
16
- opts = args.last.is_a?(Hash) ? args.pop : {}
17
- @type = args.shift
18
- @max = args.shift || 0
19
- @closed = false
20
- @name = opts[:name] || UUID.generate
21
- @direction = opts[:direction] || :bidirectional
22
- @close_mutex = Mutex.new
23
- @queue = Queues.register(@name, @type, @max)
24
- end
15
+ attr_reader :name, :direction, :type, :max, :queue
25
16
 
26
- def queue
27
- q = @queue
28
- raise Errors::ChannelClosed unless q
29
- q
17
+ def initialize(*args)
18
+ opts = args.last.is_a?(Hash) ? args.pop : {}
19
+ @type = args.shift
20
+ @max = args.shift || 0
21
+ @closed = false
22
+ @name = opts[:name] || UUID.generate
23
+ @direction = opts[:direction] || :bidirectional
24
+ @skip_marshal = opts[:skip_marshal]
25
+ @close_mutex = Mutex.new
26
+ @queue = Queues.register(@name, @type, @max)
30
27
  end
31
28
 
32
29
 
@@ -48,7 +45,9 @@ module Agent
48
45
 
49
46
  def send(object, options={})
50
47
  check_direction(:send)
51
- queue.push(object, options)
48
+ q = queue
49
+ raise Errors::ChannelClosed unless q
50
+ q.push(object, {:skip_marshal => @skip_marshal}.merge(options))
52
51
  end
53
52
  alias :push :send
54
53
  alias :<< :send
@@ -61,7 +60,9 @@ module Agent
61
60
 
62
61
  def receive(options={})
63
62
  check_direction(:receive)
64
- queue.pop(options)
63
+ q = queue
64
+ return [nil, false] unless q
65
+ q.pop(options)
65
66
  end
66
67
  alias :pop :receive
67
68
 
@@ -34,11 +34,9 @@ module Agent
34
34
 
35
35
  def send
36
36
  @mutex.synchronize do
37
- raise Errors::ChannelClosed if @closed
38
-
39
37
  if @blocking_once
40
38
  _, error = @blocking_once.perform do
41
- @object = Marshal.load(yield)
39
+ @object = yield unless @closed
42
40
  @received = true
43
41
  @cvar.signal
44
42
  @notifier.notify(self) if @notifier
@@ -47,7 +45,7 @@ module Agent
47
45
  return error
48
46
  else
49
47
  begin
50
- @object = Marshal.load(yield)
48
+ @object = yield unless @closed
51
49
  @received = true
52
50
  @cvar.signal
53
51
  @notifier.notify(self) if @notifier
@@ -2,10 +2,36 @@ require "agent/errors"
2
2
 
3
3
  module Agent
4
4
  class Push
5
+ SKIP_MARSHAL_TYPES = [
6
+ ::Symbol,
7
+ ::Numeric,
8
+ ::NilClass,
9
+ ::TrueClass,
10
+ ::FalseClass,
11
+ ::Queue,
12
+ ::SizedQueue,
13
+ ::Thread,
14
+ ::Mutex,
15
+ ::Monitor,
16
+ ::Module,
17
+ ::IO,
18
+ ::Proc,
19
+ ::Method
20
+ ]
21
+
5
22
  attr_reader :object, :uuid, :blocking_once, :notifier
6
23
 
7
24
  def initialize(object, options={})
8
- @object = Marshal.dump(object)
25
+ @object = case object
26
+ when *SKIP_MARSHAL_TYPES
27
+ object
28
+ else
29
+ if options[:skip_marshal]
30
+ object
31
+ else
32
+ Marshal.load(Marshal.dump(object))
33
+ end
34
+ end
9
35
  @uuid = options[:uuid] || UUID.generate
10
36
  @blocking_once = options[:blocking_once]
11
37
  @notifier = options[:notifier]
@@ -82,7 +82,7 @@ module Agent
82
82
  pop = Pop.new(options)
83
83
 
84
84
  mutex.synchronize do
85
- raise Errors::ChannelClosed if @closed
85
+ pop.close if @closed
86
86
  operations << pop
87
87
  pops << pop
88
88
  process
@@ -103,7 +103,7 @@ module Agent
103
103
  end
104
104
 
105
105
  def execute_case(operation)
106
- raise Errors::ChannelClosed if operation.closed?
106
+ raise Errors::ChannelClosed if operation.closed? && operation.is_a?(Agent::Push)
107
107
 
108
108
  cse = @cases[operation.uuid]
109
109
  blk, direction = cse.blk, cse.direction
@@ -1,3 +1,3 @@
1
1
  module Agent
2
- VERSION = "0.9.1"
2
+ VERSION = "0.10.0"
3
3
  end
@@ -109,9 +109,9 @@ describe Agent::Channel do
109
109
  lambda { @c.send("a") }.should raise_error(Agent::Errors::ChannelClosed)
110
110
  end
111
111
 
112
- it "should raise an error when receiving from a channel that has already been closed" do
112
+ it "should return [nil, false] when receiving from a channel that has already been closed" do
113
113
  @c.close
114
- lambda { @c.receive }.should raise_error(Agent::Errors::ChannelClosed)
114
+ @c.receive.should == [nil, false]
115
115
  end
116
116
  end
117
117
 
@@ -213,4 +213,28 @@ describe Agent::Channel do
213
213
  end
214
214
  end
215
215
 
216
+ context "marshaling" do
217
+ it "marshals data by default" do
218
+ c = channel!(String, 1)
219
+ string = "foo"
220
+ c.send(string)
221
+ string_copy = c.receive[0]
222
+ string_copy.should == string
223
+ string_copy.object_id.should_not == string.object_id
224
+ end
225
+
226
+ it "skips marshaling when configured to" do
227
+ c = channel!(String, 1, :skip_marshal => true)
228
+ string = "foo"
229
+ c.send(string)
230
+ c.receive[0].object_id.should == string.object_id
231
+ end
232
+
233
+ it "skips marshaling for channels by default" do
234
+ c = channel!(Agent::Channel, 1)
235
+ c.send(c)
236
+ c.receive[0].object_id.should == c.object_id
237
+ end
238
+ end
216
239
  end
240
+
@@ -15,16 +15,16 @@ describe Agent::Pop do
15
15
  end
16
16
 
17
17
  it "should run multiple times" do
18
- @pop.send{Marshal.dump(1)}
18
+ @pop.send{1}
19
19
  @pop.should be_received
20
- @pop.send{Marshal.dump(2)}
20
+ @pop.send{2}
21
21
  @pop.object.should == 2
22
22
  end
23
23
 
24
24
  it "should continue when received" do
25
25
  go!{ @pop.wait; @ack.send(Time.now) }
26
26
  sleep 0.2
27
- @pop.send{Marshal.dump(1)}
27
+ @pop.send{1}
28
28
 
29
29
  s, _ = @ack.receive
30
30
 
@@ -41,11 +41,23 @@ describe Agent::Pop do
41
41
  (Time.now - s).should be_within(0.01).of(0)
42
42
  end
43
43
 
44
- it "be able to be gracefully rolled back" do
44
+ it "should be able to be gracefully rolled back" do
45
45
  @pop.should_not be_received
46
46
  @pop.send{ raise Agent::Errors::Rollback }
47
47
  @pop.should_not be_received
48
48
  end
49
+
50
+ it "should continue when it was already closed" do
51
+ @pop.close
52
+
53
+ go!{ @pop.wait; @ack.send(Time.now) }
54
+
55
+ sleep 0.2
56
+
57
+ s, _ = @ack.receive
58
+
59
+ (Time.now - s).should be_within(0.01).of(0.2)
60
+ end
49
61
  end
50
62
 
51
63
  context "with a blocking_once" do
@@ -56,11 +68,12 @@ describe Agent::Pop do
56
68
 
57
69
  it "should only send only once" do
58
70
  @blocking_once.should_not be_performed
59
- @pop.send{Marshal.dump(1)}
71
+ @pop.send{1}
60
72
  @pop.should be_received
61
73
  @blocking_once.should be_performed
74
+ @pop.object.should == 1
62
75
 
63
- @pop.send{Marshal.dump(2)}
76
+ @pop.send{2}
64
77
  @pop.object.should == 1
65
78
 
66
79
  lambda{@pop.send{raise "an error"} }.should_not raise_error
@@ -73,6 +86,20 @@ describe Agent::Pop do
73
86
  @blocking_once.should_not be_performed
74
87
  @pop.should_not be_received
75
88
  end
89
+
90
+ it "should send only once even when it is closed" do
91
+ @pop.close
92
+ @blocking_once.should_not be_performed
93
+ @pop.send{1}
94
+ @pop.should be_received
95
+ @blocking_once.should be_performed
96
+ @pop.object.should == nil
97
+
98
+ @pop.send{2}
99
+ @pop.object.should == nil
100
+
101
+ lambda{@pop.send{raise "an error"} }.should_not raise_error
102
+ end
76
103
  end
77
104
 
78
105
  context "with a notifier" do
@@ -83,7 +110,7 @@ describe Agent::Pop do
83
110
 
84
111
  it "should notify when being sent" do
85
112
  @notifier.should_not be_notified
86
- @pop.send{Marshal.dump(1)}
113
+ @pop.send{1}
87
114
  @notifier.should be_notified
88
115
  end
89
116
 
@@ -29,7 +29,7 @@ describe Agent::Push do
29
29
 
30
30
  s, _ = @ack.receive
31
31
 
32
- (Time.now - s).should be_within(0.01).of(0)
32
+ (Time.now - s).should be_within(0.02).of(0)
33
33
  end
34
34
 
35
35
  it "should raise an error on the waiter when closed" do
@@ -44,6 +44,35 @@ describe Agent::Push do
44
44
  end
45
45
  end
46
46
 
47
+ context "marshaling" do
48
+ let(:object){ "foo" }
49
+ let(:skip_marshal){ false }
50
+ let(:push){ Agent::Push.new(object, :skip_marshal => skip_marshal) }
51
+
52
+ it "makes a copy of the object" do
53
+ push.object.should == object
54
+ push.object.object_id.should_not == object.object_id
55
+ end
56
+
57
+ context "with an object type that skips marshaling" do
58
+ let(:object){ ::Queue.new }
59
+
60
+ it "does not make a copy of the object" do
61
+ push.object.should == object
62
+ push.object.object_id.should == object.object_id
63
+ end
64
+ end
65
+
66
+ context "when skip_marshal is true" do
67
+ let(:skip_marshal){ true }
68
+
69
+ it "does not make a copy of the object" do
70
+ push.object.should == object
71
+ push.object.object_id.should == object.object_id
72
+ end
73
+ end
74
+ end
75
+
47
76
  context "with a blocking_once" do
48
77
  before do
49
78
  @blocking_once = Agent::BlockingOnce.new
@@ -158,11 +158,20 @@ describe Agent::Queue do
158
158
  @queue.queue.size.should == 0
159
159
  end
160
160
 
161
- it "should raise an error when being acted upon afterwards" do
162
- @queue.close
163
- lambda{ @queue.close }.should raise_error(Agent::Errors::ChannelClosed)
164
- lambda{ @queue.push("1") }.should raise_error(Agent::Errors::ChannelClosed)
165
- lambda{ @queue.pop }.should raise_error(Agent::Errors::ChannelClosed)
161
+ context "after it is closed" do
162
+ before{ @queue.close }
163
+
164
+ it "should raise an error when #close is called again" do
165
+ lambda{ @queue.close }.should raise_error(Agent::Errors::ChannelClosed)
166
+ end
167
+
168
+ it "should raise an error when a value is pushed onto the queue" do
169
+ lambda{ @queue.push("1") }.should raise_error(Agent::Errors::ChannelClosed)
170
+ end
171
+
172
+ it "should return [nil, false] when popping from the queue" do
173
+ @queue.pop.should == [nil, false]
174
+ end
166
175
  end
167
176
  end
168
177
 
@@ -322,11 +331,20 @@ describe Agent::Queue do
322
331
  @queue.pushes.size.should == 0
323
332
  end
324
333
 
325
- it "should raise an error when being acted upon afterwards" do
326
- @queue.close
327
- lambda{ @queue.close }.should raise_error(Agent::Errors::ChannelClosed)
328
- lambda{ @queue.push("1") }.should raise_error(Agent::Errors::ChannelClosed)
329
- lambda{ @queue.pop }.should raise_error(Agent::Errors::ChannelClosed)
334
+ context "after it is closed" do
335
+ before{ @queue.close }
336
+
337
+ it "should raise an error when #close is called again" do
338
+ lambda{ @queue.close }.should raise_error(Agent::Errors::ChannelClosed)
339
+ end
340
+
341
+ it "should raise an error when a value is pushed onto the queue" do
342
+ lambda{ @queue.push("1") }.should raise_error(Agent::Errors::ChannelClosed)
343
+ end
344
+
345
+ it "should return [nil, false] when popping from the queue" do
346
+ @queue.pop.should == [nil, false]
347
+ end
330
348
  end
331
349
  end
332
350
 
@@ -108,17 +108,26 @@ describe Agent::Selector do
108
108
  r.first.should == :default
109
109
  end
110
110
 
111
- it "should raise an error if the channel is closed out from under it" do
111
+ it "should raise an error if the channel is closed out from under it and you are sending to it" do
112
112
  go!{ sleep 0.25; @c.close }
113
113
 
114
114
  lambda {
115
115
  select! do |s|
116
116
  s.case(@c, :send, 1)
117
- s.case(@c, :receive)
118
117
  end
119
118
  }.should raise_error(Agent::Errors::ChannelClosed)
120
119
  end
121
120
 
121
+ it "should not raise an error if the channel is closed out from under it and you are receiving from it" do
122
+ go!{ sleep 0.25; @c.close }
123
+
124
+ lambda {
125
+ select! do |s|
126
+ s.case(@c, :receive){}
127
+ end
128
+ }.should_not raise_error
129
+ end
130
+
122
131
  context "select immediately available channel" do
123
132
  it "should select read channel" do
124
133
  c = channel!(Integer)
@@ -291,7 +300,7 @@ describe Agent::Selector do
291
300
  r.first.should == :default
292
301
  end
293
302
 
294
- it "should raise an error if the channel is closed out from under it" do
303
+ it "should raise an error if the channel is closed out from under it and you are sending to it" do
295
304
  @c.send(1)
296
305
 
297
306
  go!{ sleep 0.25; @c.close }
@@ -299,11 +308,20 @@ describe Agent::Selector do
299
308
  lambda {
300
309
  select! do |s|
301
310
  s.case(@c, :send, 1)
302
- s.case(@c, :send, 2)
303
311
  end
304
312
  }.should raise_error(Agent::Errors::ChannelClosed)
305
313
  end
306
314
 
315
+ it "should not raise an error if the channel is closed out from under it and you are receiving from it" do
316
+ go!{ sleep 0.25; @c.close }
317
+
318
+ lambda {
319
+ select! do |s|
320
+ s.case(@c, :receive){}
321
+ end
322
+ }.should_not raise_error
323
+ end
324
+
307
325
  context "select immediately available channel" do
308
326
  it "should select read channel" do
309
327
  c = channel!(Integer, 1)
@@ -14,6 +14,7 @@ RSpec.configure do |config|
14
14
  config.filter_run :focus
15
15
 
16
16
  config.filter_run_excluding :vm => lambda { |version|
17
- !(Config::CONFIG['ruby_install_name'] =~ /^#{version.to_s}/)
17
+ c = defined?(RbConfig) ? RbConfig : Config
18
+ !(c::CONFIG['ruby_install_name'] =~ /^#{version.to_s}/)
18
19
  }
19
20
  end
metadata CHANGED
@@ -1,46 +1,41 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: agent
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.1
5
- prerelease:
4
+ version: 0.10.0
6
5
  platform: ruby
7
6
  authors:
8
7
  - Ilya Grigorik
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
- date: 2013-02-20 00:00:00.000000000 Z
11
+ date: 2015-02-12 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
14
  name: rake
16
15
  requirement: !ruby/object:Gem::Requirement
17
- none: false
18
16
  requirements:
19
- - - ! '>='
17
+ - - ">="
20
18
  - !ruby/object:Gem::Version
21
19
  version: '0'
22
20
  type: :development
23
21
  prerelease: false
24
22
  version_requirements: !ruby/object:Gem::Requirement
25
- none: false
26
23
  requirements:
27
- - - ! '>='
24
+ - - ">="
28
25
  - !ruby/object:Gem::Version
29
26
  version: '0'
30
27
  - !ruby/object:Gem::Dependency
31
28
  name: rspec
32
29
  requirement: !ruby/object:Gem::Requirement
33
- none: false
34
30
  requirements:
35
- - - ! '>='
31
+ - - ">="
36
32
  - !ruby/object:Gem::Version
37
33
  version: '0'
38
34
  type: :development
39
35
  prerelease: false
40
36
  version_requirements: !ruby/object:Gem::Requirement
41
- none: false
42
37
  requirements:
43
- - - ! '>='
38
+ - - ">="
44
39
  - !ruby/object:Gem::Version
45
40
  version: '0'
46
41
  description: Agent is a diverse family of related approaches for modelling concurrent
@@ -51,8 +46,10 @@ executables: []
51
46
  extensions: []
52
47
  extra_rdoc_files: []
53
48
  files:
54
- - .gitignore
55
- - .rspec
49
+ - ".gitignore"
50
+ - ".rspec"
51
+ - ".ruby-gemset"
52
+ - ".travis.yml"
56
53
  - Gemfile
57
54
  - Gemfile.lock
58
55
  - README.md
@@ -106,27 +103,26 @@ files:
106
103
  - spec/wait_group_spec.rb
107
104
  homepage: https://github.com/igrigorik/agent
108
105
  licenses: []
106
+ metadata: {}
109
107
  post_install_message:
110
108
  rdoc_options: []
111
109
  require_paths:
112
110
  - lib
113
111
  required_ruby_version: !ruby/object:Gem::Requirement
114
- none: false
115
112
  requirements:
116
- - - ! '>='
113
+ - - ">="
117
114
  - !ruby/object:Gem::Version
118
115
  version: '0'
119
116
  required_rubygems_version: !ruby/object:Gem::Requirement
120
- none: false
121
117
  requirements:
122
- - - ! '>='
118
+ - - ">="
123
119
  - !ruby/object:Gem::Version
124
120
  version: '0'
125
121
  requirements: []
126
122
  rubyforge_project: agent
127
- rubygems_version: 1.8.24
123
+ rubygems_version: 2.4.5
128
124
  signing_key:
129
- specification_version: 3
125
+ specification_version: 4
130
126
  summary: Agent is a diverse family of related approaches for modelling concurrent
131
127
  systems, in Ruby
132
128
  test_files:
@@ -149,4 +145,3 @@ test_files:
149
145
  - spec/spec_helper.rb
150
146
  - spec/uuid_spec.rb
151
147
  - spec/wait_group_spec.rb
152
- has_rdoc: