dataflow 0.3.0 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -58,6 +58,20 @@ local do |x, y, z|
58
58
  end
59
59
  </pre>
60
60
 
61
+ <pre>
62
+ # Module methods version
63
+
64
+ Dataflow.local do |x, y, z|
65
+ # notice how the order automatically gets resolved
66
+ Thread.new { Dataflow.unify y, x + 2 }
67
+ Thread.new { Dataflow.unify z, y + 3 }
68
+ Thread.new { Dataflow.unify x, 1 }
69
+ z #=> 6
70
+ end
71
+
72
+ # Note that a gobal Dataflow.declare is not supported
73
+ </pre>
74
+
61
75
  <pre>
62
76
  # Instance variables
63
77
  class AnimalHouse
@@ -118,6 +132,91 @@ local do |x, y, z|
118
132
  end
119
133
  </pre>
120
134
 
135
+ <pre>
136
+ include Dataflow
137
+
138
+ # flow without parameters
139
+ local do |x|
140
+ flow do
141
+ # other stuff
142
+ unify x, 1337
143
+ end
144
+ x #=> 1337
145
+ end
146
+
147
+ # flow with an output parameter
148
+ local do |x|
149
+ flow(x) do
150
+ # other stuff
151
+ 1337
152
+ end
153
+ x #=> 1337
154
+ end
155
+ </pre>
156
+
157
+ <pre>
158
+ # barrier
159
+ include Dataflow
160
+
161
+ local do |lock1, lock2|
162
+ flow { unify lock1, :unlocked }
163
+ flow { unify lock2, :unlocked }
164
+ barrier lock1, lock2
165
+ puts "Barrier broken!"
166
+ end
167
+ </pre>
168
+
169
+ <pre>
170
+ # FutureQueue
171
+ include Dataflow
172
+
173
+ local do |queue, first, second|
174
+ unify queue, Dataflow::FutureQueue.new
175
+ queue.pop first
176
+ queue.push 1
177
+ queue.push 2
178
+ queue.pop second
179
+ first #=> 1
180
+ second #=> 2
181
+ end
182
+ </pre>
183
+
184
+ h1. Anonymous variables
185
+
186
+ Sometimes you may want to pack a data structure with variables that do not need to be referenced with labels. For those cases anonymous variables are a good choice, here are some options:
187
+
188
+ <pre>
189
+ include Dataflow
190
+ Array.new(3) { Dataflow::Variable.new }
191
+ Array.new(3) { Dataflow.local }
192
+ Array.new(3) { local }
193
+ # and technically not anonymous
194
+ Array.new(3) { local {|v| v } }
195
+ </pre>
196
+
197
+ h1. Debugging
198
+
199
+ If you are having trouble and need to debug dataflow variables, simply call #inspect.
200
+
201
+ If the variable has already been bound, it call inspect on its bound value like normal.However, if the variable is not bound yet then you will get a special string that contains the proxies #__id__ that you can use to track down which proxy objects are being passed around to which parts of your program:
202
+ <pre>
203
+ include Dataflow
204
+ local do |my_var|
205
+ my_var.inspect # => #<Dataflow::Variable:2637860 unbound>
206
+ end
207
+ </pre>
208
+
209
+ h1. Fork method customization
210
+
211
+ By default both #flow and #need_later use Thread.fork as their fork method. Youc an access the fork method via Dataflow.forker.
212
+
213
+ If you would like to use a custom forker, simple set it to an object that responds to #call and internally calls a block passed to it (for an example of a synchronous forker, see spec/forker_spec.rb):
214
+ <pre>
215
+ Dataflow.forker = MyClass.method(:fork_with_threadpool)
216
+ </pre>
217
+
218
+ Also note that #flow is used interally by #need_later, in case you want to override that specifically.
219
+
121
220
  h1. Ports using Dataflow
122
221
 
123
222
  Ports are an extension of the declarative concurrent model to support nondeterministic behavior. They accomplish this through the use of a single state variable. Ports are also inspired by the Oz language.
@@ -1,7 +1,11 @@
1
1
  require 'monitor'
2
2
 
3
3
  module Dataflow
4
- VERSION = "0.3.0"
4
+ VERSION = "0.3.1"
5
+ class << self
6
+ attr_accessor :forker
7
+ end
8
+ self.forker = Thread.method(:fork)
5
9
 
6
10
  def self.included(cls)
7
11
  class << cls
@@ -19,6 +23,7 @@ module Dataflow
19
23
  end
20
24
 
21
25
  def local(&block)
26
+ return Variable.new unless block_given?
22
27
  vars = Array.new(block.arity) { Variable.new }
23
28
  block.call *vars
24
29
  end
@@ -36,7 +41,7 @@ module Dataflow
36
41
  end
37
42
 
38
43
  def flow(output=nil, &block)
39
- Thread.new do
44
+ Dataflow.forker.call do
40
45
  result = block.call
41
46
  unify output, result if output
42
47
  end
@@ -44,7 +49,7 @@ module Dataflow
44
49
 
45
50
  def need_later(&block)
46
51
  local do |future|
47
- flow { unify future, block.call }
52
+ flow(future) { block.call }
48
53
  future
49
54
  end
50
55
  end
@@ -0,0 +1,9 @@
1
+ require "#{File.dirname(__FILE__)}/../dataflow"
2
+ include Dataflow
3
+
4
+ local do |lock1, lock2|
5
+ flow { unify lock1, :unlocked }
6
+ flow { unify lock2, :unlocked }
7
+ barrier lock1, lock2
8
+ puts "Barrier broken!"
9
+ end
@@ -0,0 +1,20 @@
1
+ require "#{File.dirname(__FILE__)}/../dataflow"
2
+ include Dataflow
3
+
4
+ # flow without parameters
5
+ local do |x|
6
+ flow do
7
+ # other stuff
8
+ unify x, 1337
9
+ end
10
+ puts x
11
+ end
12
+
13
+ # flow with an output parameter
14
+ local do |x|
15
+ flow(x) do
16
+ # other stuff
17
+ 1337
18
+ end
19
+ puts x
20
+ end
@@ -0,0 +1,11 @@
1
+ require "#{File.dirname(__FILE__)}/../dataflow"
2
+ include Dataflow
3
+
4
+ local do |queue, first, second|
5
+ unify queue, Dataflow::FutureQueue.new
6
+ queue.pop first
7
+ queue.push 1
8
+ queue.push 2
9
+ queue.pop second
10
+ puts "first: #{first}, second: #{second}"
11
+ end
@@ -0,0 +1,21 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe 'Using an anonymous variable' do
4
+ it 'works with Variable instances' do
5
+ container = [Dataflow::Variable.new]
6
+ unify container.first, 1337
7
+ container.first.should == 1337
8
+ end
9
+
10
+ it 'works with Dataflow.local' do
11
+ container = [Dataflow.local]
12
+ unify container.first, 1337
13
+ container.first.should == 1337
14
+ end
15
+
16
+ it 'works with #local' do
17
+ container = [local]
18
+ unify container.first, 1337
19
+ container.first.should == 1337
20
+ end
21
+ end
@@ -0,0 +1,28 @@
1
+ require "#{File.dirname(__FILE__)}/spec_helper"
2
+
3
+ describe 'Setting a customer forker' do
4
+ before(:all) do
5
+ @original_forker = Dataflow.forker
6
+ Dataflow.forker = Class.new do
7
+ def self.synchronous_forker(&block)
8
+ block.call
9
+ end
10
+ end.method(:synchronous_forker)
11
+ end
12
+
13
+ after(:all) do
14
+ Dataflow.forker = @original_forker
15
+ end
16
+
17
+ it 'uses the custom forker in #flow' do
18
+ local do |my_var|
19
+ flow(my_var) { 1337 }
20
+ my_var.should == 1337
21
+ end
22
+ end
23
+
24
+ it 'uses the custom forker in #need_later' do
25
+ my_var = need_later { 1337 }
26
+ my_var.should == 1337
27
+ end
28
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dataflow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.3.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Larry Diehl
@@ -31,9 +31,12 @@ files:
31
31
  - dataflow/equality.rb
32
32
  - dataflow/future_queue.rb
33
33
  - dataflow/port.rb
34
+ - examples/barrier.rb
34
35
  - examples/data_driven.rb
35
36
  - examples/dataflow_http_gets.rb
37
+ - examples/flow.rb
36
38
  - examples/future_http_gets.rb
39
+ - examples/future_queue.rb
37
40
  - examples/instance_variables.rb
38
41
  - examples/laziness.rb
39
42
  - examples/local_variables.rb
@@ -69,11 +72,13 @@ specification_version: 2
69
72
  summary: Dataflow concurrency for Ruby (inspired by the Oz language)
70
73
  test_files:
71
74
  - spec/actor_spec.rb
75
+ - spec/anonymous_variables_spec.rb
72
76
  - spec/barrier_spec.rb
73
77
  - spec/by_need_spec.rb
74
78
  - spec/dataflow_spec.rb
75
79
  - spec/equality_spec.rb
76
80
  - spec/flow_spec.rb
81
+ - spec/forker_spec.rb
77
82
  - spec/future_queue_spec.rb
78
83
  - spec/inspect_spec.rb
79
84
  - spec/need_later_spec.rb