tupelo 0.9 → 0.10
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +31 -1
- data/bugs/read-take.rb +19 -0
- data/bugs/take-write.rb +7 -7
- data/example/app-and-tup.rb +11 -8
- data/example/async-transaction.rb +7 -7
- data/example/balance-xfer-locking.rb +13 -13
- data/example/balance-xfer-retry.rb +16 -16
- data/example/balance-xfer.rb +9 -9
- data/example/boolean-match.rb +5 -5
- data/example/bounded-retry.rb +9 -9
- data/example/broker-locking.rb +14 -14
- data/example/broker-optimistic.rb +7 -7
- data/example/cancel.rb +7 -7
- data/example/concurrent-transactions.rb +17 -17
- data/example/custom-class.rb +9 -9
- data/example/custom-search.rb +8 -8
- data/example/fail-and-retry.rb +11 -11
- data/example/hash-tuples.rb +12 -10
- data/example/increment.rb +8 -8
- data/example/load-balancer.rb +2 -1
- data/example/lock-mgr-with-queue.rb +18 -18
- data/example/lock-mgr.rb +18 -18
- data/example/map-reduce-v2.rb +11 -11
- data/example/map-reduce.rb +11 -11
- data/example/matching.rb +5 -5
- data/example/message-bus.rb +6 -3
- data/example/notify.rb +17 -17
- data/example/optimist.rb +9 -9
- data/example/parallel.rb +16 -8
- data/example/pregel/distributed.rb +129 -0
- data/example/pregel/pagerank.rb +72 -0
- data/example/pregel/pregel.rb +102 -0
- data/example/pregel/remote.rb +165 -0
- data/example/pulse.rb +10 -10
- data/example/read-in-trans.rb +15 -15
- data/example/subspace.rb +34 -0
- data/example/take-nowait.rb +1 -0
- data/example/tcp.rb +3 -3
- data/example/timeout-trans.rb +5 -5
- data/example/timeout.rb +10 -9
- data/example/tiny-client.rb +5 -5
- data/example/tiny-server.rb +2 -2
- data/example/transaction-logic.rb +16 -16
- data/example/wait-interrupt.rb +38 -0
- data/example/write-wait.rb +11 -11
- data/lib/tupelo/archiver/tuplespace.rb +5 -1
- data/lib/tupelo/archiver/worker.rb +25 -18
- data/lib/tupelo/client/reader.rb +2 -2
- data/lib/tupelo/client/transaction.rb +79 -36
- data/lib/tupelo/client/tuplespace.rb +1 -0
- data/lib/tupelo/client/worker.rb +107 -13
- data/lib/tupelo/client.rb +36 -2
- data/lib/tupelo/version.rb +1 -1
- data/test/lib/mock-client.rb +4 -0
- data/test/lib/testable-worker.rb +1 -1
- data/test/stress/concurrent-transactions.rb +15 -15
- data/test/system/test-archiver.rb +8 -8
- data/test/unit/test-ops.rb +56 -0
- metadata +72 -68
- data/bugs/write-read.rb +0 -15
- data/example/broker-queue.rb +0 -35
- data/example/child-of-child.rb +0 -34
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 892628e201d400fa9592a01b1fd62c4bb8e09ab5
|
4
|
+
data.tar.gz: 2ca45f4f124ad6db32e2880557004f5c0553cf9f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0b8bfcdbb7143f543372cd0bdad95feb49305a47e57e0368146e3c224f33e1fddb261f9f019ec4c4a503a4250d91ecc653a93e5792c27e4f47cfd7ca33727a76
|
7
|
+
data.tar.gz: 931b57069c7e06452429eb71e083c568be953a1e37de54cd1b9541107c6a79884e7d64645ed8765777c9ede5f18589e3c35d5057e946e82b8daa70c18180b49d
|
data/README.md
CHANGED
@@ -21,6 +21,7 @@ Documentation
|
|
21
21
|
============
|
22
22
|
|
23
23
|
* [FAQ](doc/faq.md)
|
24
|
+
* [Subspaces](doc/subspace.md)
|
24
25
|
|
25
26
|
Getting started
|
26
27
|
==========
|
@@ -110,7 +111,7 @@ Getting started
|
|
110
111
|
...
|
111
112
|
end
|
112
113
|
|
113
|
-
Note that a local match is still not a guarantee of `x_final == x_optimistic`. Another process may take `x_optimistic` first, and the take will be re-executed. (Think of #take_nowait as a way of saying "take a match, but don't bother trying if there is no match known at this time.")
|
114
|
+
Note that a local match is still not a guarantee of `x_final == x_optimistic`. Another process may take `x_optimistic` first, and the take will be re-executed. (Think of #take_nowait as a way of saying "take a match, but don't bother trying if there is no match known at this time.") Similarly, #take_nowait returning nil is not a guarantee that a match does not exist: another process could have written a match later than the time of the local search.
|
114
115
|
|
115
116
|
Perform a general transaction:
|
116
117
|
|
@@ -124,7 +125,17 @@ Getting started
|
|
124
125
|
end
|
125
126
|
|
126
127
|
Note that the block may execute more than once, if there is competition for the tuples that you are trying to #take or #read. When the block exits, however, the transaction is final and universally accepted by all clients.
|
128
|
+
|
129
|
+
Tuples written or taken during a transaction affect subsequent operations in the transaction without modifying the tuplespace or affecting other concurrent transactions (until the transaction completes):
|
127
130
|
|
131
|
+
transaction do |t|
|
132
|
+
t.write [3]
|
133
|
+
p t.read [3] # => 3
|
134
|
+
p read_all # => [] # note read_all called on client, not trans.
|
135
|
+
t.take [3]
|
136
|
+
p t.read_nowait [3] # => nil
|
137
|
+
end
|
138
|
+
|
128
139
|
You can timeout a transaction:
|
129
140
|
|
130
141
|
transaction timeout: 1 do
|
@@ -180,6 +191,8 @@ A tuplespace is a service for coordination, configuration, and control of concur
|
|
180
191
|
|
181
192
|
See https://en.wikipedia.org/wiki/Tuple_space for general information and history. This project is strongly influenced by Masatoshi Seki's Rinda implementation, part of the Ruby standard library. See http://pragprog.com/book/sidruby/the-druby-book for a good introduction to rinda and druby.
|
182
193
|
|
194
|
+
See http://dbmsmusings.blogspot.com/2010/08/problems-with-acid-and-how-to-fix-them.html for an explanation of the imporance of determinism in distributed transaction systems.
|
195
|
+
|
183
196
|
What is a tuple?
|
184
197
|
----------------
|
185
198
|
|
@@ -210,6 +223,8 @@ In other words, a tuple is a fairly general object, though this depends on the s
|
|
210
223
|
|
211
224
|
It's kind of like a "JSON object", except that, when using the json serializer, the hash keys can only be strings. In the msgpack case, keys have no special limitations. In the case of the marshal and yaml modes, tuples can contain many other kinds of objects.
|
212
225
|
|
226
|
+
The empty tuples `[]` and `{}` are allowed, but bare values such as `3.14` or `false` are not tuples by themselves.
|
227
|
+
|
213
228
|
One other thing to keep in mind: in the array case, the order of the elements is significant. In the hash case, the order is not significant. So these are both true:
|
214
229
|
|
215
230
|
[1,2] != [2,1]
|
@@ -292,6 +307,17 @@ Transactions have a significant disadvantage compared to using take/write to loc
|
|
292
307
|
|
293
308
|
Transactions do have an advantage over using take/write to lock/unlock tuples: there is no possibility of deadlock. See [example/deadlock.rb](example/deadlock.rb) and [example/parallel.rb](example/parallel.rb).
|
294
309
|
|
310
|
+
Another advantage of tranactions is that it is possible to guarantee continuous existence of a time-series of tuples. For example, suppose that tuples matching `{step: Numeric}` indicate the progress of some activity. With transactions, you can guarantee that there is exactly one matching tuple at any time. So any client which reads this template will find a match without blocking.
|
311
|
+
|
312
|
+
Another use of transactions: forcing a retry when something changes:
|
313
|
+
|
314
|
+
transaction do
|
315
|
+
step = read(step: nil)["step"]
|
316
|
+
take value: nil, step: step
|
317
|
+
end
|
318
|
+
|
319
|
+
This code waits on the existence of a value, but retries if the step changes while waiting. See example/pregel/distributed.rb for a use of this techinique.
|
320
|
+
|
295
321
|
Tupelo transactions are ACID in the following sense. They are Atomic and Isolated -- this is enforced by the transaction processing in each client. Consistency is enforced by the underlying message sequencer: each client's copy of the space is the deterministic result of the same sequence of operations. Durability is optional, but can be provided by the persistent archiver or other clients.
|
296
322
|
|
297
323
|
On the CAP spectrum, tupelo tends towards consistency: for all clients, write and take operations are applied in the same order, so the state of the entire system up through a given tick of discrete time is universally agreed upon. Of course, because of the difficulties of distributed systems, one client may not yet have seen the same range of ticks as another.
|
@@ -404,6 +430,8 @@ To compare
|
|
404
430
|
|
405
431
|
* doozer, etcd
|
406
432
|
|
433
|
+
* serf -- tupelo has lower latency and is transactional, but at a cost compared to serf; tupelo semantics is closer to databases
|
434
|
+
|
407
435
|
* arakoon
|
408
436
|
|
409
437
|
* hazelcast
|
@@ -471,6 +499,8 @@ Each inner serialization method ("blobber") has its own advantages and drawbacks
|
|
471
499
|
|
472
500
|
For most purposes, msgpack is a good choice, so it is the default.
|
473
501
|
|
502
|
+
The sending client's tupelo library must make sure that there is no aliasing within the list of tuples (this is only an issue for Marshal and YAML, since msgpack and json do not support references).
|
503
|
+
|
474
504
|
|
475
505
|
Development
|
476
506
|
===========
|
data/bugs/read-take.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'tupelo/app'
|
2
|
+
|
3
|
+
Tupelo.application do
|
4
|
+
local do
|
5
|
+
write_wait [1]
|
6
|
+
|
7
|
+
note = notifier
|
8
|
+
|
9
|
+
transaction do
|
10
|
+
read [1]
|
11
|
+
take [1]
|
12
|
+
end
|
13
|
+
|
14
|
+
note.wait
|
15
|
+
status, tick, cid, op = note.wait
|
16
|
+
p op # should "take [1]", not "take [1]; read [1]"
|
17
|
+
# this is just an optimization, not really a bug
|
18
|
+
end
|
19
|
+
end
|
data/bugs/take-write.rb
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
require 'tupelo/app'
|
2
2
|
|
3
|
-
Tupelo.application do
|
4
|
-
|
5
|
-
|
3
|
+
Tupelo.application do
|
4
|
+
local do
|
5
|
+
write_wait [1]
|
6
6
|
|
7
|
-
note =
|
7
|
+
note = notifier
|
8
8
|
|
9
|
-
|
10
|
-
x =
|
11
|
-
|
9
|
+
transaction do
|
10
|
+
x = take [1]
|
11
|
+
write x
|
12
12
|
end
|
13
13
|
|
14
14
|
note.wait
|
data/example/app-and-tup.rb
CHANGED
@@ -11,19 +11,22 @@
|
|
11
11
|
#
|
12
12
|
# w [2]
|
13
13
|
# w [3]
|
14
|
-
# ra
|
14
|
+
# ra # => [[5]]
|
15
15
|
# w [7.4]
|
16
|
-
# ra
|
16
|
+
# ra # => [[12.4]]
|
17
17
|
|
18
18
|
require 'tupelo/app'
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
filename = "servers-#$$.yaml"
|
21
|
+
puts "run this in another shell: tup #{filename}"
|
22
|
+
|
23
|
+
Tupelo.application servers_file: filename do
|
24
|
+
child do
|
22
25
|
loop do
|
23
|
-
|
24
|
-
x, =
|
25
|
-
y, =
|
26
|
-
|
26
|
+
transaction do
|
27
|
+
x, = take [Numeric]
|
28
|
+
y, = take [Numeric]
|
29
|
+
write [x + y]
|
27
30
|
end
|
28
31
|
end
|
29
32
|
end
|
@@ -2,15 +2,15 @@ require 'tupelo/app'
|
|
2
2
|
|
3
3
|
# see also cancel.rb
|
4
4
|
|
5
|
-
Tupelo.application do
|
6
|
-
|
7
|
-
t =
|
8
|
-
|
9
|
-
|
5
|
+
Tupelo.application do
|
6
|
+
child do
|
7
|
+
t = transaction.async do
|
8
|
+
write ["pong"]
|
9
|
+
take ["ping"]
|
10
10
|
end
|
11
11
|
|
12
|
-
|
13
|
-
puts
|
12
|
+
write ["ping"]
|
13
|
+
puts take ["pong"]
|
14
14
|
puts t.value
|
15
15
|
end
|
16
16
|
end
|
@@ -1,28 +1,28 @@
|
|
1
1
|
require 'tupelo/app'
|
2
2
|
|
3
|
-
Tupelo.application do
|
4
|
-
|
5
|
-
|
3
|
+
Tupelo.application do
|
4
|
+
child do
|
5
|
+
write(
|
6
6
|
{name: "alice", balance: 1000},
|
7
7
|
{name: "bob", balance: 200}
|
8
8
|
)
|
9
9
|
10.times do |i|
|
10
|
-
alice =
|
11
|
-
|
10
|
+
alice = take(name: "alice", balance: Numeric)
|
11
|
+
log alice
|
12
12
|
alice = alice.dup
|
13
13
|
alice["balance"] -= 10
|
14
|
-
|
14
|
+
write_wait alice
|
15
15
|
sleep 0.1
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
log read_all(name: /^(?:alice|bob)$/, balance: nil)
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
child do
|
22
22
|
sleep 0.3
|
23
23
|
|
24
|
-
src =
|
25
|
-
dst =
|
24
|
+
src = take(name: "alice", balance: Numeric)
|
25
|
+
dst = take(name: "bob", balance: Numeric)
|
26
26
|
|
27
27
|
if src["balance"] < 500
|
28
28
|
abort "insufficient funds -- not attempting transfer"
|
@@ -41,10 +41,10 @@ Tupelo.application do |app|
|
|
41
41
|
src["balance"] -= 500
|
42
42
|
dst["balance"] += 500
|
43
43
|
|
44
|
-
w =
|
45
|
-
|
44
|
+
w = write src, dst
|
45
|
+
log "attempting to set #{[src, dst]}"
|
46
46
|
|
47
47
|
w.wait
|
48
|
-
|
48
|
+
log read_all(name: /^(?:alice|bob)$/, balance: nil)
|
49
49
|
end
|
50
50
|
end
|
@@ -1,27 +1,27 @@
|
|
1
1
|
require 'tupelo/app'
|
2
2
|
|
3
|
-
Tupelo.application do
|
4
|
-
|
5
|
-
|
3
|
+
Tupelo.application do
|
4
|
+
child do
|
5
|
+
write(
|
6
6
|
{name: "alice", balance: 1000},
|
7
7
|
{name: "bob", balance: 200}
|
8
8
|
)
|
9
9
|
10.times do |i|
|
10
|
-
alice =
|
11
|
-
|
10
|
+
alice = take(name: "alice", balance: Numeric)
|
11
|
+
log alice
|
12
12
|
alice = alice.dup
|
13
13
|
alice["balance"] -= 10
|
14
|
-
|
14
|
+
write_wait alice
|
15
15
|
sleep 0.1
|
16
16
|
end
|
17
17
|
|
18
|
-
|
18
|
+
log read_all(name: /^(?:alice|bob)$/, balance: nil)
|
19
19
|
end
|
20
20
|
|
21
|
-
|
22
|
-
|
23
|
-
src =
|
24
|
-
dst =
|
21
|
+
child do
|
22
|
+
transaction do
|
23
|
+
src = take(name: "alice", balance: Numeric)
|
24
|
+
dst = take(name: "bob", balance: Numeric)
|
25
25
|
|
26
26
|
if src["balance"] < 500
|
27
27
|
abort "insufficient funds -- not attempting transfer"
|
@@ -37,19 +37,19 @@ Tupelo.application do |app|
|
|
37
37
|
# force fail -- the tuples this client is trying to take
|
38
38
|
# will be gone when it wakes up
|
39
39
|
|
40
|
-
|
41
|
-
|
40
|
+
log "attempting to set #{[src, dst]}"
|
41
|
+
write src, dst
|
42
42
|
|
43
43
|
if false # enable this to see how failures are retried
|
44
44
|
begin
|
45
|
-
|
45
|
+
commit.wait
|
46
46
|
rescue => ex
|
47
|
-
|
47
|
+
log "retrying after #{ex}"
|
48
48
|
raise
|
49
49
|
end
|
50
50
|
end
|
51
51
|
end
|
52
52
|
|
53
|
-
|
53
|
+
log read_all(name: /^(?:alice|bob)$/, balance: nil)
|
54
54
|
end
|
55
55
|
end
|
data/example/balance-xfer.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'tupelo/app'
|
2
2
|
|
3
|
-
Tupelo.application do
|
4
|
-
|
5
|
-
|
3
|
+
Tupelo.application do
|
4
|
+
child do
|
5
|
+
write(
|
6
6
|
{name: "alice", balance: 1000},
|
7
7
|
{name: "bob", balance: 200}
|
8
8
|
)
|
9
9
|
end
|
10
10
|
|
11
|
-
|
12
|
-
|
13
|
-
src =
|
14
|
-
dst =
|
11
|
+
child do
|
12
|
+
transaction do
|
13
|
+
src = take(name: "alice", balance: Numeric)
|
14
|
+
dst = take(name: "bob", balance: Numeric)
|
15
15
|
|
16
16
|
if src["balance"] < 500
|
17
17
|
abort "insufficient funds -- not attempting transfer"
|
@@ -23,11 +23,11 @@ Tupelo.application do |app|
|
|
23
23
|
src["balance"] -= 500
|
24
24
|
dst["balance"] += 500
|
25
25
|
|
26
|
-
|
26
|
+
write src, dst
|
27
27
|
end
|
28
28
|
# transaction will block if balances have changed since the read.
|
29
29
|
# see balance-xfer-retry.rb
|
30
30
|
|
31
|
-
|
31
|
+
log read_all(name: /^(?:alice|bob)$/, balance: nil)
|
32
32
|
end
|
33
33
|
end
|
data/example/boolean-match.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
require 'tupelo/app'
|
2
2
|
require 'tupelo/util/boolean'
|
3
3
|
|
4
|
-
Tupelo.application do
|
5
|
-
|
6
|
-
tm =
|
4
|
+
Tupelo.application do
|
5
|
+
local do
|
6
|
+
tm = match_any [0..2, String], [3..5, Hash]
|
7
7
|
|
8
|
-
|
8
|
+
write(
|
9
9
|
[0, "a"], [1, {b: 0}], [2, "c"],
|
10
10
|
[3, "a"], [4, {b: 0}], [5, "c"]
|
11
11
|
).wait
|
12
12
|
|
13
|
-
|
13
|
+
log read_all tm
|
14
14
|
end
|
15
15
|
end
|
data/example/bounded-retry.rb
CHANGED
@@ -3,32 +3,32 @@ require 'tupelo/app'
|
|
3
3
|
N = 2
|
4
4
|
K = 1
|
5
5
|
|
6
|
-
Tupelo.application do
|
6
|
+
Tupelo.application do
|
7
7
|
(N+K).times do
|
8
|
-
|
8
|
+
child do
|
9
9
|
catch :gave_up do
|
10
10
|
tries = 0
|
11
11
|
|
12
|
-
r =
|
12
|
+
r = take [Integer] do |val|
|
13
13
|
tries += 1
|
14
14
|
if tries >= N
|
15
|
-
|
15
|
+
log "giving up on #{val}"
|
16
16
|
throw :gave_up
|
17
17
|
end
|
18
|
-
|
18
|
+
log "trying to take #{val}"
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
log "took #{r.inspect}"
|
22
22
|
end
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
26
|
sleep 0.01
|
27
27
|
|
28
|
-
|
28
|
+
child do
|
29
29
|
N.times do |i|
|
30
|
-
|
31
|
-
|
30
|
+
write [i]
|
31
|
+
log "wrote #{[i]}"
|
32
32
|
sleep 0.1
|
33
33
|
end
|
34
34
|
end
|
data/example/broker-locking.rb
CHANGED
@@ -7,37 +7,37 @@ N_PLAYERS = 10
|
|
7
7
|
|
8
8
|
token = ["token"] # only the holder of the token can arrange games
|
9
9
|
|
10
|
-
Tupelo.application do
|
11
|
-
|
12
|
-
|
10
|
+
Tupelo.application do
|
11
|
+
local do
|
12
|
+
write token
|
13
13
|
end
|
14
14
|
|
15
15
|
N_PLAYERS.times do
|
16
|
-
|
17
|
-
me =
|
16
|
+
child do
|
17
|
+
me = client_id
|
18
18
|
|
19
|
-
|
20
|
-
other_player =
|
19
|
+
take token # bottleneck and fragile until 'write token'
|
20
|
+
other_player = read_nowait(name: nil)
|
21
21
|
# sleep 1 # program takes ~N_PLAYERS sec to finish
|
22
22
|
|
23
23
|
if other_player
|
24
|
-
|
25
|
-
|
24
|
+
take other_player
|
25
|
+
write(
|
26
26
|
player1: me,
|
27
27
|
player2: other_player["name"])
|
28
|
-
|
28
|
+
write token
|
29
29
|
you = other_player["name"]
|
30
30
|
|
31
31
|
else
|
32
|
-
|
33
|
-
|
34
|
-
game =
|
32
|
+
write(name: me)
|
33
|
+
write token
|
34
|
+
game = read(
|
35
35
|
player1: nil,
|
36
36
|
player2: me)
|
37
37
|
you = game["player1"]
|
38
38
|
end
|
39
39
|
|
40
|
-
|
40
|
+
log "now playing with #{you}"
|
41
41
|
end
|
42
42
|
end
|
43
43
|
end
|
@@ -9,15 +9,15 @@ require 'tupelo/app'
|
|
9
9
|
|
10
10
|
N_PLAYERS = 10
|
11
11
|
|
12
|
-
Tupelo.application do
|
12
|
+
Tupelo.application do
|
13
13
|
N_PLAYERS.times do
|
14
14
|
# sleep rand / 10 # reduce contention -- could also randomize inserts
|
15
|
-
|
16
|
-
me =
|
17
|
-
|
15
|
+
child do
|
16
|
+
me = client_id
|
17
|
+
write name: me
|
18
18
|
|
19
19
|
begin
|
20
|
-
t =
|
20
|
+
t = transaction
|
21
21
|
if t.take_nowait name: me
|
22
22
|
you = t.take(name: nil)["name"]
|
23
23
|
t.write(
|
@@ -28,14 +28,14 @@ Tupelo.application do |app|
|
|
28
28
|
t.fail!
|
29
29
|
end
|
30
30
|
rescue Tupelo::Client::TransactionFailure => ex
|
31
|
-
game =
|
31
|
+
game = read_nowait(
|
32
32
|
player1: nil,
|
33
33
|
player2: me)
|
34
34
|
retry unless game
|
35
35
|
you = game["player1"]
|
36
36
|
end
|
37
37
|
|
38
|
-
|
38
|
+
log "now playing with #{you}"
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
data/example/cancel.rb
CHANGED
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'tupelo/app'
|
2
2
|
|
3
|
-
Tupelo.application do
|
4
|
-
|
3
|
+
Tupelo.application do
|
4
|
+
child do
|
5
5
|
ats = (0..4).map do |i|
|
6
|
-
|
7
|
-
|
8
|
-
|
6
|
+
transaction.async do
|
7
|
+
take ["start"]
|
8
|
+
write [i]
|
9
9
|
end
|
10
10
|
end
|
11
11
|
|
12
12
|
[0,1,2,4].each {|i| ats[i].cancel}
|
13
13
|
|
14
|
-
|
15
|
-
p
|
14
|
+
write ["start"]
|
15
|
+
p take [Integer]
|
16
16
|
end
|
17
17
|
end
|
@@ -2,37 +2,37 @@ require 'tupelo/app'
|
|
2
2
|
|
3
3
|
N = 100
|
4
4
|
|
5
|
-
Tupelo.application do
|
6
|
-
|
7
|
-
|
5
|
+
Tupelo.application do
|
6
|
+
child do
|
7
|
+
write [0, 0]
|
8
8
|
|
9
9
|
t1 = Thread.new do
|
10
10
|
N.times do
|
11
|
-
|
12
|
-
|
13
|
-
x, y =
|
14
|
-
|
15
|
-
|
11
|
+
transaction do
|
12
|
+
take ["reader ready"]
|
13
|
+
x, y = take [nil, nil]
|
14
|
+
write [x+1, y]
|
15
|
+
write ["data ready"]
|
16
16
|
end
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
20
|
t2 = Thread.new do
|
21
21
|
N.times do
|
22
|
-
|
23
|
-
|
24
|
-
x, y =
|
25
|
-
|
26
|
-
|
22
|
+
transaction do
|
23
|
+
take ["reader ready"]
|
24
|
+
x, y = take [nil, nil]
|
25
|
+
write [x, y+1]
|
26
|
+
write ["data ready"]
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
31
|
loop do
|
32
|
-
|
33
|
-
|
34
|
-
x, y =
|
35
|
-
|
32
|
+
write ["reader ready"]
|
33
|
+
take ["data ready"]
|
34
|
+
x, y = read [nil, nil]
|
35
|
+
log "%3d %3d" % [x, y]
|
36
36
|
break if x == N and y == N
|
37
37
|
end
|
38
38
|
end
|
data/example/custom-class.rb
CHANGED
@@ -10,20 +10,20 @@ end
|
|
10
10
|
require 'tupelo/app'
|
11
11
|
|
12
12
|
# Must use marshal or yaml -- msgpack and json do not support custom classes.
|
13
|
-
Tupelo.application blob_type: 'marshal' do
|
14
|
-
|
13
|
+
Tupelo.application blob_type: 'marshal' do
|
14
|
+
child do
|
15
15
|
f = Foo.new; f.x = 3
|
16
16
|
p f
|
17
17
|
|
18
|
-
|
18
|
+
write [f]
|
19
19
|
|
20
|
-
p
|
21
|
-
p
|
22
|
-
p
|
20
|
+
p read [nil]
|
21
|
+
p read [Foo]
|
22
|
+
p read [f]
|
23
23
|
|
24
|
-
p
|
24
|
+
p take [Foo]
|
25
25
|
|
26
|
-
|
27
|
-
p
|
26
|
+
write [f]
|
27
|
+
p take [f]
|
28
28
|
end
|
29
29
|
end
|
data/example/custom-search.rb
CHANGED
@@ -14,14 +14,14 @@ class MyClient < Tupelo::Client
|
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
Tupelo.application do
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
17
|
+
Tupelo.application do
|
18
|
+
local MyClient do
|
19
|
+
write [41, 42, 43]
|
20
|
+
write [42, 42, 42]
|
21
|
+
write [42, 42]
|
22
|
+
write_wait [42] # make sure all writes up to this one have completed
|
23
23
|
|
24
|
-
|
25
|
-
|
24
|
+
log read_all
|
25
|
+
log read_all_diagonal 42
|
26
26
|
end
|
27
27
|
end
|