dataflow 0.2.1 → 0.3.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.
- 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
|