agent 0.9.1 → 0.10.0

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