dataflow 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/HISTORY +24 -3
- data/dataflow.rb +29 -6
- data/dataflow/future_queue.rb +24 -0
- data/spec/barrier_spec.rb +25 -0
- data/spec/dataflow_spec.rb +23 -0
- data/spec/flow_spec.rb +25 -0
- data/spec/future_queue_spec.rb +31 -0
- data/spec/inspect_spec.rb +1 -1
- metadata +6 -2
data/HISTORY
CHANGED
@@ -1,4 +1,26 @@
|
|
1
|
-
==
|
1
|
+
== 0.3.0 / 2009-08-29
|
2
|
+
|
3
|
+
* Major enhancements
|
4
|
+
|
5
|
+
* "flow" abstraction for threading (use it to create threads and optionally
|
6
|
+
pass it a variable to be bound output, or override it to modify need_later
|
7
|
+
for other threading strategies such as thread pools)
|
8
|
+
|
9
|
+
* Dataflow::FutureQueue
|
10
|
+
|
11
|
+
* Nested binding through variables possible
|
12
|
+
|
13
|
+
* Minor enhancements
|
14
|
+
|
15
|
+
* Better #inspect and UnificationError debugging output
|
16
|
+
|
17
|
+
* "barrier" abstraction for manually preventing execution until variable
|
18
|
+
arguments have been bound
|
19
|
+
|
20
|
+
* Use mixin methods as class/module methods optionally
|
21
|
+
e.g. Dataflow.local {|v| v }
|
22
|
+
|
23
|
+
== 0.2.1 / 2009-07-29
|
2
24
|
|
3
25
|
* Minor enhancements
|
4
26
|
|
@@ -21,8 +43,7 @@
|
|
21
43
|
|
22
44
|
* Minor enhancements
|
23
45
|
|
24
|
-
* Got the "require_path" set correctly so rubygems can be used =)
|
25
|
-
|
46
|
+
* Got the "require_path" set correctly so rubygems can be used =)
|
26
47
|
|
27
48
|
== 0.1.0 / 2009-06-13
|
28
49
|
|
data/dataflow.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'monitor'
|
2
2
|
|
3
3
|
module Dataflow
|
4
|
-
VERSION = "0.
|
4
|
+
VERSION = "0.3.0"
|
5
5
|
|
6
6
|
def self.included(cls)
|
7
7
|
class << cls
|
@@ -31,13 +31,26 @@ module Dataflow
|
|
31
31
|
Variable.new &block
|
32
32
|
end
|
33
33
|
|
34
|
+
def barrier(*variables)
|
35
|
+
variables.each{|v| v.__wait__ }
|
36
|
+
end
|
37
|
+
|
38
|
+
def flow(output=nil, &block)
|
39
|
+
Thread.new do
|
40
|
+
result = block.call
|
41
|
+
unify output, result if output
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
34
45
|
def need_later(&block)
|
35
46
|
local do |future|
|
36
|
-
|
47
|
+
flow { unify future, block.call }
|
37
48
|
future
|
38
49
|
end
|
39
50
|
end
|
40
51
|
|
52
|
+
extend self
|
53
|
+
|
41
54
|
# Note that this class uses instance variables directly rather than nicely
|
42
55
|
# initialized instance variables in get/set methods for memory and
|
43
56
|
# performance reasons
|
@@ -53,7 +66,8 @@ module Dataflow
|
|
53
66
|
LOCK.synchronize do
|
54
67
|
__activate_trigger__ if @__trigger__
|
55
68
|
if @__bound__
|
56
|
-
|
69
|
+
return @__value__.__unify__(value) if @__value__.__dataflow__? rescue nil
|
70
|
+
raise UnificationError, "#{@__value__.inspect} != #{value.inspect}" if self != value
|
57
71
|
else
|
58
72
|
@__value__ = value
|
59
73
|
@__bound__ = true
|
@@ -70,10 +84,9 @@ module Dataflow
|
|
70
84
|
@__trigger__ = nil # GC
|
71
85
|
end
|
72
86
|
|
73
|
-
def
|
87
|
+
def __wait__
|
74
88
|
LOCK.synchronize do
|
75
|
-
unless @__bound__
|
76
|
-
return '#<Dataflow::Variable unbound>' if name == :inspect
|
89
|
+
unless @__bound__
|
77
90
|
if @__trigger__
|
78
91
|
__activate_trigger__
|
79
92
|
else
|
@@ -81,8 +94,17 @@ module Dataflow
|
|
81
94
|
end
|
82
95
|
end
|
83
96
|
end unless @__bound__
|
97
|
+
end
|
98
|
+
|
99
|
+
def method_missing(name, *args, &block)
|
100
|
+
return "#<Dataflow::Variable:#{__id__} unbound>" if !@__bound__ && name == :inspect
|
101
|
+
__wait__
|
84
102
|
@__value__.__send__(name, *args, &block)
|
85
103
|
end
|
104
|
+
|
105
|
+
def __dataflow__?
|
106
|
+
true
|
107
|
+
end
|
86
108
|
end
|
87
109
|
|
88
110
|
UnificationError = Class.new StandardError
|
@@ -90,3 +112,4 @@ end
|
|
90
112
|
|
91
113
|
require "#{File.dirname(__FILE__)}/dataflow/port"
|
92
114
|
require "#{File.dirname(__FILE__)}/dataflow/actor"
|
115
|
+
require "#{File.dirname(__FILE__)}/dataflow/future_queue"
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Dataflow
|
2
|
+
class FutureQueue
|
3
|
+
include Dataflow
|
4
|
+
declare :push_port, :pop_port
|
5
|
+
|
6
|
+
def initialize
|
7
|
+
local do |pushed, popped|
|
8
|
+
unify push_port, Dataflow::Port.new(pushed)
|
9
|
+
unify pop_port, Dataflow::Port.new(popped)
|
10
|
+
|
11
|
+
Thread.new {
|
12
|
+
loop do
|
13
|
+
barrier pushed.head, popped.head
|
14
|
+
unify popped.head, pushed.head
|
15
|
+
pushed, popped = pushed.tail, popped.tail
|
16
|
+
end
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def push(x) push_port.send x end
|
22
|
+
def pop(x) pop_port.send x end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/spec_helper"
|
2
|
+
|
3
|
+
describe 'A barrier' do
|
4
|
+
it 'waits for variables to be bound before continuing' do
|
5
|
+
local do |x, y, barrier_broken|
|
6
|
+
Thread.new do
|
7
|
+
barrier x, y
|
8
|
+
unify barrier_broken, true
|
9
|
+
end
|
10
|
+
Thread.new { unify x, :x }
|
11
|
+
Thread.new { unify y, :y }
|
12
|
+
barrier_broken.should be_true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'continues for variables that are already bound' do
|
17
|
+
local do |x, y, barrier_broken|
|
18
|
+
unify x, :x
|
19
|
+
unify y, :y
|
20
|
+
barrier x, y
|
21
|
+
unify barrier_broken, true
|
22
|
+
barrier_broken.should be_true
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/spec/dataflow_spec.rb
CHANGED
@@ -126,3 +126,26 @@ context 'Using "declare" for object-specific read-only attributes' do
|
|
126
126
|
end
|
127
127
|
end
|
128
128
|
end
|
129
|
+
|
130
|
+
describe 'Binding a variable that proxies through another' do
|
131
|
+
it 'binds through successfully' do
|
132
|
+
local do |x, y|
|
133
|
+
lambda do
|
134
|
+
unify x, y
|
135
|
+
unify x, 1337
|
136
|
+
x.should == 1337
|
137
|
+
y.should == 1337
|
138
|
+
end.should_not raise_error
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
describe 'Using static/module method' do
|
144
|
+
it 'works like the mixin versions' do
|
145
|
+
Dataflow.local do |big_cat, small_cat|
|
146
|
+
Thread.new { Dataflow.unify big_cat, small_cat.upcase }
|
147
|
+
Dataflow.unify small_cat, 'cat'
|
148
|
+
big_cat.should == 'CAT'
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
data/spec/flow_spec.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/spec_helper"
|
2
|
+
|
3
|
+
describe 'Using flow without a parameter' do
|
4
|
+
it 'works like normal threads' do
|
5
|
+
local do |big_cat, small_cat|
|
6
|
+
flow { unify big_cat, small_cat.upcase }
|
7
|
+
unify small_cat, 'cat'
|
8
|
+
big_cat.should == 'CAT'
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
describe 'Using flow with a parameter' do
|
14
|
+
it 'binds the parameter to the last line of the block' do
|
15
|
+
local do |big_cat, small_cat, output|
|
16
|
+
flow(output) do
|
17
|
+
unify big_cat, small_cat.upcase
|
18
|
+
'returned'
|
19
|
+
end
|
20
|
+
unify small_cat, 'cat'
|
21
|
+
big_cat.should == 'CAT'
|
22
|
+
output.should == 'returned'
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require "#{File.dirname(__FILE__)}/spec_helper"
|
2
|
+
|
3
|
+
describe 'A future queue' do
|
4
|
+
it 'accepts synchronous pushes and pops' do
|
5
|
+
local do |queue, first, second, third|
|
6
|
+
unify queue, Dataflow::FutureQueue.new
|
7
|
+
queue.pop first
|
8
|
+
queue.pop second
|
9
|
+
queue.push 1
|
10
|
+
queue.push 2
|
11
|
+
queue.push 3
|
12
|
+
queue.pop third
|
13
|
+
|
14
|
+
[first, second, third].should == [1, 2, 3]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'accepts asynchronous pushes and pops' do
|
19
|
+
local do |queue, first, second, third|
|
20
|
+
unify queue, Dataflow::FutureQueue.new
|
21
|
+
Thread.new { queue.pop first }
|
22
|
+
Thread.new { queue.pop second }
|
23
|
+
Thread.new { queue.push 1 }
|
24
|
+
Thread.new { queue.push 2 }
|
25
|
+
Thread.new { queue.push 3 }
|
26
|
+
Thread.new { queue.pop third }
|
27
|
+
|
28
|
+
[first, second, third].sort.should == [1, 2, 3]
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/spec/inspect_spec.rb
CHANGED
@@ -3,7 +3,7 @@ require "#{File.dirname(__FILE__)}/spec_helper"
|
|
3
3
|
describe "An unbound variable" do
|
4
4
|
it "should be inspectable for debugging purposes" do
|
5
5
|
local do |unbound|
|
6
|
-
unbound.inspect.should
|
6
|
+
unbound.inspect.should =~ /#<Dataflow::Variable:[0-9]+ unbound>/
|
7
7
|
end
|
8
8
|
end
|
9
9
|
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.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Larry Diehl
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-
|
12
|
+
date: 2009-08-29 00:00:00 -05:00
|
13
13
|
default_executable:
|
14
14
|
dependencies: []
|
15
15
|
|
@@ -29,6 +29,7 @@ files:
|
|
29
29
|
- dataflow.rb
|
30
30
|
- dataflow/actor.rb
|
31
31
|
- dataflow/equality.rb
|
32
|
+
- dataflow/future_queue.rb
|
32
33
|
- dataflow/port.rb
|
33
34
|
- examples/data_driven.rb
|
34
35
|
- examples/dataflow_http_gets.rb
|
@@ -68,9 +69,12 @@ specification_version: 2
|
|
68
69
|
summary: Dataflow concurrency for Ruby (inspired by the Oz language)
|
69
70
|
test_files:
|
70
71
|
- spec/actor_spec.rb
|
72
|
+
- spec/barrier_spec.rb
|
71
73
|
- spec/by_need_spec.rb
|
72
74
|
- spec/dataflow_spec.rb
|
73
75
|
- spec/equality_spec.rb
|
76
|
+
- spec/flow_spec.rb
|
77
|
+
- spec/future_queue_spec.rb
|
74
78
|
- spec/inspect_spec.rb
|
75
79
|
- spec/need_later_spec.rb
|
76
80
|
- spec/port_spec.rb
|