more_rinda 0.0.1

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/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ .DS_Store
2
+ *~
3
+ *.dat
4
+ pkg/*
5
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,20 @@
1
+ # MoreRinda
2
+
3
+ ## Summary
4
+
5
+ Extension to rinda tuplespace
6
+
7
+ - rinda_eval = simple parallel programming utility. runs a computation inside child process and returns the result via tuple.
8
+
9
+ - ptuplespace = persistency later
10
+
11
+ - njet = rinda's pattern matcher to match any changes.
12
+
13
+ ## Install
14
+
15
+ gem install more_rinda
16
+
17
+
18
+ ## Example Usage.
19
+
20
+ Please see "test" and "sample" directories for the usage.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/example/eval0.rb ADDED
@@ -0,0 +1,15 @@
1
+ require 'rinda/tuplespace'
2
+ require 'rinda/eval'
3
+
4
+ place = Rinda::TupleSpace.new
5
+ DRb.start_service
6
+
7
+ 10.times do |n|
8
+ Rinda::rinda_eval(place) do |ts|
9
+ [:sqrt, n, Math.sqrt(n)]
10
+ end
11
+ end
12
+
13
+ 10.times do |n|
14
+ p place.read([:sqrt, n, nil])
15
+ end
data/example/nqueen.rb ADDED
@@ -0,0 +1,78 @@
1
+ require 'rinda/tuplespace'
2
+ require 'rinda/eval'
3
+
4
+ module NQueen
5
+ module_function
6
+ def concat(board, row)
7
+ board.each_with_index do |v, col|
8
+ check = (v - row).abs
9
+ return nil if check == 0
10
+ return nil if check == board.size - col
11
+ end
12
+ board + [row]
13
+ end
14
+
15
+ def nq(size, board=[])
16
+ found = 0
17
+ size.times do |row|
18
+ fwd = concat(board, row)
19
+ next unless fwd
20
+ return 1 if fwd.size == size
21
+ found += nq(size, fwd)
22
+ end
23
+ found
24
+ end
25
+
26
+ def nq2(size, r1, r2)
27
+ board = concat([r1], r2)
28
+ return 0 unless board
29
+ nq(size, board)
30
+ end
31
+ end
32
+
33
+ def invoke_engine(rinda, num)
34
+ num.times do
35
+ Rinda::rinda_eval(rinda) do |ts|
36
+ begin
37
+ while true
38
+ sym, size, r1, r2 = ts.take([:nq, Integer, Integer, Integer])
39
+ ts.write([:nq_ans, size, r1, r2, NQueen.nq2(size, r1, r2)])
40
+ end
41
+ rescue
42
+ end
43
+ [:nq_engine]
44
+ end
45
+ end
46
+ end
47
+
48
+ def write_q(rinda, size)
49
+ size.times do |r1|
50
+ size.times do |r2|
51
+ rinda.write([:nq, size, r1, r2])
52
+ end
53
+ end
54
+ end
55
+
56
+ def take_a(rinda, size)
57
+ found = 0
58
+ size.times do |r1|
59
+ size.times do |r2|
60
+ tuple = rinda.take([:nq_ans, size, r1, r2, nil])
61
+ found += tuple[4]
62
+ end
63
+ end
64
+ found
65
+ end
66
+
67
+ def resolve(rinda, size)
68
+ write_q(rinda, size)
69
+ take_a(rinda, size)
70
+ end
71
+
72
+
73
+ DRb.start_service
74
+ rinda = Rinda::TupleSpace.new
75
+ size = (ARGV.shift || '5').to_i
76
+
77
+ invoke_engine(rinda, 4)
78
+ puts resolve(rinda, size)
data/example/phil.rb ADDED
@@ -0,0 +1,62 @@
1
+ require 'rinda/tuplespace'
2
+ require 'rinda/eval'
3
+
4
+ class Phil
5
+ def initialize(ts, n, num)
6
+ @ts = ts
7
+ @right = n
8
+ @left = (n + 1) % num
9
+ end
10
+
11
+ def run
12
+ while running?
13
+ do_it(:think)
14
+ @ts.take([:room_ticket])
15
+ @ts.take([:chopstick, @right])
16
+ @ts.take([:chopstick, @left])
17
+ do_it(:eat)
18
+ @ts.write([:chopstick, @right])
19
+ @ts.write([:chopstick, @left])
20
+ @ts.write([:room_ticket])
21
+ end
22
+ end
23
+
24
+ def running?
25
+ @ts.read_all([:done]).size == 0
26
+ end
27
+
28
+ def do_it(symbol)
29
+ sleep(rand)
30
+ @ts.write(p [symbol, @right])
31
+ end
32
+ end
33
+
34
+ def main
35
+ place = Rinda::TupleSpace.new
36
+ DRb.start_service
37
+
38
+ num = 10
39
+ num.times do |n|
40
+ place.write([:chopstick, n])
41
+ Rinda::rinda_eval(place) do |ts|
42
+ phil = Phil.new(ts, n, num)
43
+ phil.run
44
+ [:phil, n]
45
+ end
46
+ end
47
+
48
+ (num - 1).times do |n|
49
+ place.write([:room_ticket])
50
+ end
51
+
52
+ sleep(10)
53
+
54
+ place.write([:done])
55
+
56
+ num.times do |n|
57
+ place.take([:phil, n])
58
+ p [n, place.read_all([:think, n]).size]
59
+ end
60
+ end
61
+
62
+ main
@@ -0,0 +1,53 @@
1
+ require 'thread'
2
+ require 'drb/drb'
3
+ require 'rinda/rinda'
4
+
5
+ class Agent
6
+ def initialize(name, url, desc)
7
+ @tuple = [name, url, desc]
8
+ @inbox = Queue.new
9
+ @renewer = Rinda::SimpleRenewer.new(15)
10
+ end
11
+
12
+ def pop
13
+ @inbox.pop
14
+ end
15
+
16
+ def hello(name, url, desc)
17
+ @inbox.push([name, url, desc])
18
+ return @tuple
19
+ end
20
+
21
+ def meet(who)
22
+ tuple = who.hello(*@tuple)
23
+ @inbox.push(tuple)
24
+ end
25
+
26
+ def enter(place)
27
+ place.write([:agent, @tuple[0], self], @renewer)
28
+ end
29
+
30
+ def broadcast(place)
31
+ place.read_all([:agent, String, DRbObject]).each do |k, name, ro|
32
+ next if name == @tuple[0]
33
+ ro.meet(self) rescue nil
34
+ end
35
+ end
36
+ end
37
+
38
+ DRb.start_service
39
+ place = DRbObject.new_with_uri(ARGV.shift)
40
+
41
+ name = ARGV.shift || 'your_nick'
42
+ url = ARGV.shift || 'http://www.druby.org'
43
+ desc = ARGV.shift || 'no comment is good comment'
44
+
45
+ agent = Agent.new(name, url, desc)
46
+ agent.enter(place)
47
+ agent.broadcast(place)
48
+
49
+ while true
50
+ p agent.pop
51
+ end
52
+
53
+
@@ -0,0 +1,10 @@
1
+ require 'drb/drb'
2
+
3
+ ro = DRbObject.new_with_uri(ARGV.shift)
4
+
5
+ name = ARGV.shift || 'your_nick'
6
+ url = ARGV.shift || 'http://www.druby.org'
7
+ desc = ARGV.shift || 'no comment is good comment'
8
+
9
+ p ro.exchange(name, url, desc)
10
+
@@ -0,0 +1,18 @@
1
+ require 'rinda/tuplespace'
2
+ require 'rinda/njet'
3
+
4
+ class CardHolder
5
+ def initialize
6
+ @ts = Rinda::TupleSpace.new
7
+ @ts.write(['m_seki', 'http://d.hatena.ne.jp/m_seki', 'Hello, Again'])
8
+ end
9
+
10
+ def exchange(name, url, desc)
11
+ @ts.write([name, url, desc])
12
+ @ts.take([Rinda::Njet.new(name), nil, nil])
13
+ end
14
+ end
15
+
16
+ DRb.start_service('druby://:56789', CardHolder.new)
17
+ puts DRb.uri
18
+ DRb.thread.join
@@ -0,0 +1,17 @@
1
+ require 'rinda/tuplespace'
2
+ require 'rinda/inspect'
3
+ require 'rinda/njet'
4
+
5
+ ts = Rinda::TupleSpace.new(15)
6
+ DRb.start_service('druby://:56788', ts)
7
+
8
+ while true
9
+ puts
10
+ puts DRb.uri
11
+ ts.report.tuple.each do |t|
12
+ p [t[0], t[1], t[2].__drburi]
13
+ end
14
+ sleep 10
15
+ end
16
+
17
+
@@ -0,0 +1,85 @@
1
+ require 'thread'
2
+
3
+ class ActorsOffice
4
+ def initialize(actor)
5
+ @queue = Queue.new
6
+ @thread = Thread.new(actor) do
7
+ catch(actor) do
8
+ while true
9
+ msg, arg, blk = @queue.pop
10
+ actor.__send__(msg, *arg, &blk)
11
+ end
12
+ end
13
+ end
14
+ end
15
+ def __thread__; @thread; end
16
+
17
+ def method_missing(m, *a, &b)
18
+ @queue.push([m, a, b])
19
+ end
20
+ end
21
+
22
+ class BarberShop
23
+ def initialize
24
+ @chairs = []
25
+ end
26
+
27
+ def barber=(barber)
28
+ @barber = barber
29
+ end
30
+
31
+ def new_customer(name)
32
+ puts "<< arriving #{name}"
33
+ if (@chairs.size >= 3)
34
+ puts "** sorry, no room for #{name} **"
35
+ else
36
+ @chairs << name
37
+ @barber.wake_up
38
+ end
39
+ end
40
+
41
+ def customer_leaves
42
+ barber_check
43
+ end
44
+
45
+ def barber_check
46
+ @barber.customer(@chairs.shift) unless @chairs.empty?
47
+ end
48
+
49
+ def quit
50
+ puts 'quit'
51
+ throw(self)
52
+ end
53
+ end
54
+
55
+ class Barber
56
+ def initialize(shop)
57
+ @shop = shop
58
+ end
59
+
60
+ def cut(name)
61
+ puts "cutting #{name}."
62
+ sleep(rand(5))
63
+ puts "finishing cutting #{name}.>>"
64
+ @shop.customer_leaves
65
+ end
66
+
67
+ def wake_up
68
+ @shop.barber_check
69
+ end
70
+
71
+ def customer(name)
72
+ cut(name)
73
+ @shop.barber_check
74
+ end
75
+ end
76
+
77
+ shop = ActorsOffice.new(BarberShop.new)
78
+ shop.barber = ActorsOffice.new(Barber.new(shop))
79
+
80
+ %w(jack john henry tom bob m_seki makoto).each do |name|
81
+ shop.new_customer(name)
82
+ sleep(1)
83
+ end
84
+
85
+ gets
@@ -0,0 +1,102 @@
1
+ require 'rinda/tuplespace'
2
+ require 'rinda/eval'
3
+
4
+ class BarberShop
5
+ def initialize(ts)
6
+ @ts = ts
7
+ @waiting = []
8
+ end
9
+
10
+ def start
11
+ @ts.write([:shop, @waiting])
12
+ while true
13
+ addr, msg, arg = @ts.take([:shop, nil, nil])
14
+ case msg
15
+ when :new_customer
16
+ name = arg
17
+ puts "<< arriving #{name}"
18
+ if (@waiting.size >= 3)
19
+ puts "** sorry, no room for #{name} **"
20
+ else
21
+ @waiting << name
22
+ @ts.write([:barber, :wake_up, nil])
23
+ end
24
+ when :customer_leaves
25
+ puts "leaving #{@waiting.shift}>>"
26
+ @ts.write([:barber, :customer, @waiting[0]])
27
+ when :barber_check
28
+ @ts.write([:barber, :customer, @waiting[0]])
29
+ when :quit
30
+ return
31
+ end
32
+ @ts.take([:shop, nil])
33
+ @ts.write([:shop, @waiting])
34
+ end
35
+ end
36
+ end
37
+
38
+ class Barber
39
+ def initialize(ts)
40
+ @ts = ts
41
+ end
42
+
43
+ def start
44
+ @ts.write([:barber, 'sleep'])
45
+ while true
46
+ addr, msg, arg = @ts.take([:barber, nil, nil])
47
+ case msg
48
+ when :wake_up
49
+ @ts.take([:barber, nil])
50
+ @ts.write([:barber, 'wake up'])
51
+ @ts.write([:shop, :barber_check, nil])
52
+ when :customer
53
+ if arg
54
+ cut(arg)
55
+ @ts.write([:shop, :customer_leaves, nil])
56
+ else
57
+ puts " sleeping"
58
+ @ts.take([:barber, nil])
59
+ @ts.write([:barber, 'sleep'])
60
+ end
61
+ when :quit
62
+ return
63
+ end
64
+ end
65
+ end
66
+
67
+ def cut(name)
68
+ puts " cutting #{name}."
69
+ rand(5).times do
70
+ puts " cutting..."
71
+ sleep(1)
72
+ end
73
+ puts " finishing cutting #{name}."
74
+ end
75
+ end
76
+
77
+ place = Rinda::TupleSpace.new
78
+ DRb.start_service('druby://localhost:0')
79
+
80
+ Rinda::rinda_eval(place) do |ts|
81
+ BarberShop.new(ts).start
82
+ [:shop]
83
+ end
84
+
85
+ Rinda::rinda_eval(place) do |ts|
86
+ Barber.new(ts).start
87
+ [:barber]
88
+ end
89
+
90
+ %w(jack john henry tom bob m_seki makoto).each do |name|
91
+ place.write([:shop, :new_customer, name])
92
+ sleep(2)
93
+ end
94
+
95
+ p place.read([:shop, []])
96
+ p place.read([:barber, 'sleep'])
97
+
98
+ place.write([:shop, :quit, nil])
99
+ place.write([:barber, :quit, nil])
100
+
101
+ p place.take([:shop])
102
+ p place.take([:barber])
data/install.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'rbconfig'
2
+ require 'fileutils'
3
+
4
+ dest = RbConfig::CONFIG['sitelibdir'] + '/rinda'
5
+ src = ['lib/rinda/eval.rb']
6
+
7
+ FileUtils.mkdir_p(dest, {:verbose => true})
8
+ src.each do |s|
9
+ FileUtils.install(s, dest, {:verbose => true, :mode => 0644})
10
+ end
11
+
data/lib/rinda/eval.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'drb/drb'
2
+ require 'rinda/rinda'
3
+
4
+ module Rinda
5
+ module_function
6
+ def rinda_eval(ts)
7
+ Thread.pass # FIXME
8
+ ts = DRbObject.new(ts) unless DRbObject === ts
9
+ pid = fork do
10
+ Thread.current['DRb'] = nil
11
+ DRb.stop_service
12
+ DRb.start_service
13
+ place = TupleSpaceProxy.new(ts)
14
+ tuple = yield(place)
15
+ place.write(tuple) rescue nil
16
+ end
17
+ Process.detach(pid)
18
+ end
19
+ end
@@ -0,0 +1,63 @@
1
+ require 'drb/drb'
2
+ require 'rinda/tuplespace'
3
+
4
+ module Rinda
5
+ class TupleSpace
6
+ attr_reader :bag, :read_waiter, :take_waiter, :notify_waiter
7
+
8
+ def report
9
+ synchronize do
10
+ TupleSpaceReport.new(self)
11
+ end
12
+ end
13
+ end
14
+
15
+ class TupleBag
16
+ include Enumerable
17
+ def each(&blk)
18
+ each_entry(&blk)
19
+ end
20
+ end
21
+
22
+ class WaitTemplateEntry
23
+ attr_reader :cond
24
+ end
25
+
26
+ class TupleSpaceReport
27
+ def initialize(ts)
28
+ @tuple = ts.bag.collect {|t| t.value}
29
+ @reader = ts.read_waiter.collect {|t| [t.value, waiters(t)]}
30
+ @taker = ts.take_waiter.collect {|t| [t.value, waiters(t)]}
31
+ end
32
+ attr_reader :tuple, :reader, :taker
33
+
34
+ def waiters(tuple)
35
+ tuple.cond.instance_variable_get(:@waiters).collect do |th|
36
+ [th, (th[:DRb]['client'].stream.peeraddr rescue nil)]
37
+ end[0]
38
+ end
39
+ end
40
+ end
41
+
42
+ if __FILE__ == $0
43
+ require 'pp'
44
+
45
+ def report(ts)
46
+ pp ts.report
47
+ end
48
+
49
+ ts = Rinda::TupleSpace.new
50
+ DRb.start_service(nil, ts)
51
+ p DRb.uri
52
+
53
+ t1 = Thread.new { p ts.take([:hello, :world]) }
54
+ t2 = Thread.new { p ts.read([:hello, :nil]) }
55
+ sleep 1
56
+ report(ts)
57
+ ts.write([:hello, :world])
58
+ report(ts)
59
+ sleep 1
60
+ report(ts)
61
+ gets
62
+ report(ts)
63
+ end
data/lib/rinda/njet.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Rinda
2
+ class Njet
3
+ def initialize(value)
4
+ @value = value
5
+ end
6
+
7
+ def ===(other)
8
+ @value != other
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,178 @@
1
+ require 'rinda/tuplespace'
2
+ require 'rinda/tuplestore'
3
+ require 'singleton'
4
+ require 'weakref'
5
+
6
+ module Rinda
7
+ class PTupleEntryCache
8
+ include Singleton
9
+
10
+ def initialize
11
+ @cache = {}
12
+ end
13
+
14
+ def [](serial)
15
+ ref = @cache[serial]
16
+ return nil unless ref
17
+ it = ref.__getobj__
18
+ @cache.delete(serial) unless it
19
+ return it
20
+ end
21
+
22
+ def register(tuple)
23
+ @cache[tuple.serial] = WeakRef.new(tuple)
24
+ end
25
+ end
26
+
27
+ class PTupleEntry < TupleEntry
28
+ def self.new_with_desc(key, desc)
29
+ store = Rinda.tuple_store
30
+ it = allocate
31
+ it.instance_variable_set('@store', store)
32
+ it.instance_variable_set('@serial', key)
33
+ it.instance_variable_set('@tuple', desc[:tuple])
34
+ it.instance_variable_set('@expires', desc[:expires])
35
+ it.instance_variable_set('@cancel', desc[:cancel])
36
+ it.instance_variable_set('@renewer', desc[:renewer])
37
+ PTupleEntryCache.instance.register(it)
38
+ it
39
+ end
40
+
41
+ def to_store
42
+ { :tuple => @tuple,
43
+ :expires => @expires,
44
+ :cancel => @cancel,
45
+ :renewer => @renewer }
46
+ end
47
+
48
+ def initialize(*arg)
49
+ super(*arg)
50
+ @store = Rinda.tuple_store
51
+ @serial = @store.add(to_store)
52
+ PTupleEntryCache.instance.register(self)
53
+ end
54
+ attr_reader :serial
55
+
56
+ def cancel
57
+ super
58
+ @store.set_cancel(@serial)
59
+ end
60
+
61
+ def delete
62
+ @store.delete(@serial)
63
+ end
64
+
65
+ def expires=(it)
66
+ super(it)
67
+ @store.set_expires(@serial, it)
68
+ end
69
+
70
+ def renew(it)
71
+ old = @expires
72
+ super(it)
73
+ return unless @store
74
+ @store.set_expires(@serail, @expires) unless old == @expires
75
+ end
76
+ end
77
+
78
+ class PTupleSpace < TupleSpace
79
+ def initialize(*arg)
80
+ super(*arg)
81
+ @bag = PTupleBag.new
82
+ end
83
+
84
+ def create_entry(tuple, sec)
85
+ PTupleEntry.new(tuple, sec)
86
+ end
87
+
88
+ def restore
89
+ @bag.restore
90
+ end
91
+ end
92
+
93
+ class PTupleBag < TupleBag
94
+ def initialize(*var)
95
+ super(*var)
96
+ end
97
+ attr_reader :store
98
+
99
+ def delete(tuple)
100
+ it = super(tuple)
101
+ return nil unless it
102
+ tuple.delete
103
+ it
104
+ end
105
+
106
+ def restore
107
+ Rinda.tuple_store.each do |k, v|
108
+ tuple = PTupleEntry.new_with_desc(k, v)
109
+ push(tuple)
110
+ nil
111
+ end
112
+ end
113
+ end
114
+
115
+ class TupleStoreIdConv < DRb::DRbIdConv
116
+ def tuple_store?(ref)
117
+ return false unless ref.kind_of?(Array)
118
+ return false unless ref[0] == :TupleStoreId
119
+ return true
120
+ end
121
+
122
+ def to_obj(ref)
123
+ if tuple_store?(ref)
124
+ PTupleEntryCache.instance[ref[1]]
125
+ else
126
+ super(ref)
127
+ end
128
+ end
129
+
130
+ def to_id(obj)
131
+ if obj.class == PTupleEntry
132
+ [:TupleStoreId, obj.serial]
133
+ else
134
+ super(obj)
135
+ end
136
+ end
137
+ end
138
+
139
+ def setup_tuple_store(store)
140
+ raise "tuple_store was assinged" if @tuple_store
141
+ @tuple_store = store
142
+ end
143
+ module_function :setup_tuple_store
144
+
145
+ def tuple_store
146
+ @tuple_store
147
+ end
148
+ module_function :tuple_store
149
+ end
150
+
151
+ if __FILE__ == $0
152
+ # store = DRbObject.new_with_uri('druby://localhost:12345')
153
+ store = Rinda::TupleStoreLog.new('ts_log')
154
+ Rinda::setup_tuple_store(store)
155
+
156
+ DRb.install_id_conv(Rinda::TupleStoreIdConv.new)
157
+ ts = Rinda::PTupleSpace.new
158
+ DRb.start_service('druby://localhost:23456', ts)
159
+ ts.restore
160
+ # sleep
161
+
162
+ ts.write(['Hello', 'World'])
163
+ p ts.read_all(['Hello', nil])
164
+ p ts.take(['Hello', nil])
165
+
166
+ x = ts.write(['Hello', 'cancel'], 2)
167
+ p ts.read_all(['Hello', nil])
168
+ ref = DRbObject.new(x)
169
+ ref.cancel
170
+ p ts.read_all(['Hello', nil])
171
+ x = ts.write(['Hello', 'World'])
172
+
173
+ p DRbObject.new(x)
174
+
175
+ File.open('test.dat', 'wb') do |x|
176
+ Marshal.dump(store, x)
177
+ end
178
+ end
@@ -0,0 +1,122 @@
1
+ require 'rinda/tuplestore'
2
+ require 'tokyocabinet'
3
+
4
+ module Rinda
5
+ class TokyoStore < TupleStore
6
+ class BDBError < RuntimeError
7
+ def initialize(bdb)
8
+ super(bdb.errmsg(bdb.ecode))
9
+ end
10
+ end
11
+
12
+ class BDB < TokyoCabinet::BDB
13
+ def exception
14
+ BDBError.new(self)
15
+ end
16
+
17
+ def cursor
18
+ TokyoCabinet::BDBCUR.new(self)
19
+ end
20
+
21
+ def self.call_or_die(*ary)
22
+ file, lineno = __FILE__, __LINE__
23
+ if /^(.+?):(\d+)(?::in `(.*)')?/ =~ caller(1)[0]
24
+ file = $1
25
+ lineno = $2.to_i
26
+ end
27
+ ary.each do |sym|
28
+ module_eval("def #{sym}(*arg); super || raise(self); end",
29
+ file, lineno)
30
+ end
31
+ end
32
+
33
+ call_or_die :open, :close
34
+ call_or_die :tranbegin, :tranabort, :trancommit
35
+ call_or_die :vanish
36
+ end
37
+
38
+ include MonitorMixin
39
+ def initialize(name)
40
+ super()
41
+ @name = name
42
+ @bdb = BDB.new
43
+ writer {}
44
+ end
45
+
46
+ def transaction(mode)
47
+ synchronize do
48
+ begin
49
+ @bdb.open(@name, mode)
50
+ return yield
51
+ ensure
52
+ @bdb.close
53
+ end
54
+ end
55
+ end
56
+
57
+ def reader(&block)
58
+ transaction(BDB::OREADER, &block)
59
+ end
60
+
61
+ def writer(&block)
62
+ transaction(BDB::OWRITER | BDB::OCREAT, &block)
63
+ end
64
+
65
+ def load(val)
66
+ val ? Marshal.load(val) : nil
67
+ end
68
+
69
+ def each
70
+ reader do
71
+ cursor = @bdb.cursor
72
+ cursor.jump('t.')
73
+ while cursor.key
74
+ break unless /^t\.(\w+)/ =~ cursor.key
75
+ key = $1
76
+ desc = Hash.new
77
+ desc[:tuple] = load(cursor.val)
78
+ cursor.next
79
+ next unless desc[:tuple]
80
+ next if @bdb["c.#{key}"]
81
+ desc[:renewer] = load(@bdb["r.#{key}"])
82
+ desc[:expires] = load(@bdb["e.#{key}"])
83
+ yield(key, desc)
84
+ end
85
+ end
86
+ end
87
+
88
+ def add(desc)
89
+ writer do
90
+ ser = (@bdb['ser'] || 0).to_i + 1
91
+ @bdb['ser'] = ser
92
+ key = ser.to_s(36)
93
+ @bdb["t.#{key}"] = Marshal.dump(desc[:tuple])
94
+ @bdb["r.#{key}"] = Marshal.dump(desc[:renewer])
95
+ @bdb["e.#{key}"] = Marshal.dump(desc[:expires])
96
+ @bdb["c.#{key}"] = 't' if desc[:cancel]
97
+ return key
98
+ end
99
+ end
100
+
101
+ def delete(key)
102
+ writer do
103
+ @bdb.out("t.#{key}")
104
+ @bdb.out("r.#{key}")
105
+ @bdb.out("e.#{key}")
106
+ @bdb.out("c.#{key}")
107
+ end
108
+ end
109
+
110
+ def set_cancel(key)
111
+ writer do
112
+ @bdb["c.#{key}"] = 't'
113
+ end
114
+ end
115
+
116
+ def set_expires(key, value)
117
+ writer do
118
+ @bdb["e.#{key}"] = Marshal.dump(value)
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,139 @@
1
+ require 'rinda/tuplespace'
2
+ require 'monitor'
3
+
4
+ module Rinda
5
+ class TupleStore
6
+ def each; end
7
+ def add(desc); end
8
+ def delete(key); end
9
+ def set_cancel(key); end
10
+ def set_expires(key, value); end
11
+ end
12
+
13
+ class TupleStoreSimple < TupleStore
14
+ include MonitorMixin
15
+
16
+ def initialize
17
+ super()
18
+ @ser = 0
19
+ @hash = {}
20
+ end
21
+
22
+ def each
23
+ @hash.each do |k, v|
24
+ next unless v
25
+ next if v[:cancel]
26
+ yield(k, v) if v
27
+ end
28
+ end
29
+
30
+ def add(desc)
31
+ synchronize do
32
+ @ser += 1
33
+ @hash[@ser] = desc
34
+ return @ser
35
+ end
36
+ end
37
+
38
+ def delete(key)
39
+ @hash.delete(key)
40
+ end
41
+
42
+ def set_cancel(key)
43
+ return unless @hash[key]
44
+ @hash[key][:cancel] = true
45
+ end
46
+
47
+ def set_expires(key, value)
48
+ return unless @hash[key]
49
+ @hash[key][:expires] = value
50
+ end
51
+ end
52
+
53
+ class TupleStoreLog < TupleStore
54
+ include MonitorMixin
55
+
56
+ def initialize(log_dir)
57
+ super()
58
+ @log_dir = log_dir
59
+ Dir::mkdir(log_dir) rescue nil
60
+ @ser = 0
61
+ @hash = {}
62
+ restore
63
+ end
64
+
65
+ def each
66
+ @hash.each do |k, v|
67
+ next unless v
68
+ next if v[:cancel]
69
+ yield(k, v) if v
70
+ end
71
+ end
72
+
73
+ def add(desc)
74
+ synchronize do
75
+ @ser += 1
76
+ @hash[@ser] = desc
77
+ File.open(File.join(@log_dir, @ser.to_s), 'wb') do |f|
78
+ Marshal.dump(desc, f)
79
+ end
80
+ return @ser
81
+ end
82
+ end
83
+
84
+ def delete(key)
85
+ synchronize do
86
+ @hash.delete(key)
87
+ fname = File.join(@log_dir, key.to_s)
88
+ File.rename(fname, fname + '_del')
89
+ nil
90
+ end
91
+ end
92
+
93
+ def set_attr(key, name, value)
94
+ synchronize do
95
+ return unless @hash[key]
96
+ @hash[key][name] = value
97
+ fname = File.join(@log_dir, key.to_s)
98
+ File.open(fname + 'tmp', 'wb') do |f|
99
+ Marshal.dump(@hash[key], f)
100
+ end
101
+ File.rename(fname + 'tmp', fname)
102
+ end
103
+ end
104
+
105
+ def set_cancel(key)
106
+ set_attr(key, :cancel, true)
107
+ end
108
+
109
+ def set_expires(key, value)
110
+ set_attr(key, :expires, value)
111
+ end
112
+
113
+ def restore
114
+ Dir.foreach(@log_dir) do |file|
115
+ num = desc = nil
116
+ if /([0-9]+)(_del)?/ =~ file
117
+ num = $1.to_i
118
+ @ser = num if @ser < num
119
+ next if $2
120
+ begin
121
+ File.open(File.join(@log_dir, file), 'rb') do |f|
122
+ desc = Marshal.load(f)
123
+ @hash[num] = desc
124
+ end
125
+ rescue
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end
131
+ end
132
+
133
+ if __FILE__ == $0
134
+ uri = ARGV.shift || 'druby://localhost:12345'
135
+ store = Rinda::TupleStoreLog.new('ts_log')
136
+ DRb.start_service(uri, store)
137
+ DRb.thread.join
138
+ end
139
+
@@ -0,0 +1,3 @@
1
+ module MoreRinda
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "rinda/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "more_rinda"
7
+ s.version = MoreRinda::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Masatoshi Seki"]
10
+ s.homepage = "https://github.com/seki/MoreRinda"
11
+ s.summary = %q{Various extensions for Rinda::TupleSpace lovers.}
12
+ s.description = ""
13
+
14
+ s.rubyforge_project = "drip"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,25 @@
1
+ require 'rinda/ptuplespace'
2
+
3
+ # store = DRbObject.new_with_uri('druby://localhost:12345')
4
+ store = Rinda::TupleStoreLog.new('ts_log')
5
+ Rinda::setup_tuple_store(store)
6
+
7
+ DRb.install_id_conv(Rinda::TupleStoreIdConv.new)
8
+ ts = Rinda::PTupleSpace.new
9
+ DRb.start_service('druby://localhost:23456', ts)
10
+ ts.restore
11
+ # sleep
12
+
13
+ ts.write(['Hello', 'World'])
14
+ p ts.read_all(['Hello', nil])
15
+ p ts.take(['Hello', nil])
16
+
17
+ x = ts.write(['Hello', 'cancel'], 2)
18
+ p ts.read_all(['Hello', nil])
19
+ ref = DRbObject.new(x)
20
+ ref.cancel
21
+ p ts.read_all(['Hello', nil])
22
+ x = ts.write(['Hello', 'World'])
23
+
24
+ p DRbObject.new(x)
25
+
data/test/test_njet.rb ADDED
@@ -0,0 +1,34 @@
1
+ require 'rinda/tuplespace'
2
+ require 'rinda/njet'
3
+ require 'test/unit'
4
+
5
+ class NjetTest < Test::Unit::TestCase
6
+ include Rinda
7
+ def test_match
8
+ ts = Rinda::TupleSpace.new
9
+ assert_equal([], ts.read_all([Njet.new(10)]))
10
+ ts.write([10])
11
+ assert_equal([], ts.read_all([Njet.new(10)]))
12
+ ts.write([11])
13
+ assert_equal([[11]], ts.read_all([Njet.new(10)]))
14
+ ts.write([12])
15
+ assert_equal([[11], [12]], ts.read_all([Njet.new(10)]).sort)
16
+ end
17
+
18
+ def test_wait_for_change
19
+ ts = Rinda::TupleSpace.new
20
+
21
+ ts.write([:state, 1])
22
+ _, last_state = ts.read([:state, nil])
23
+
24
+ assert_raise(Rinda::RequestExpiredError) do
25
+ ts.read([:state, Njet.new(last_state)], 0)
26
+ end
27
+
28
+ tuple = ts.take([:state, nil])
29
+ tuple[1] += 1
30
+ ts.write(tuple)
31
+
32
+ assert_equal([:state, 2], ts.read([:state, Njet.new(last_state)], 0))
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: more_rinda
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Masatoshi Seki
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-08-09 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description: ""
17
+ email:
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files: []
23
+
24
+ files:
25
+ - .gitignore
26
+ - Gemfile
27
+ - README.md
28
+ - Rakefile
29
+ - example/eval0.rb
30
+ - example/nqueen.rb
31
+ - example/phil.rb
32
+ - example/rk09/agent.rb
33
+ - example/rk09/card.rb
34
+ - example/rk09/card_holder.rb
35
+ - example/rk09/place.rb
36
+ - example/sleeping_barber_actor.rb
37
+ - example/sleeping_barber_rinda.rb
38
+ - install.rb
39
+ - lib/rinda/eval.rb
40
+ - lib/rinda/inspect.rb
41
+ - lib/rinda/njet.rb
42
+ - lib/rinda/ptuplespace.rb
43
+ - lib/rinda/tokyotuplestore.rb
44
+ - lib/rinda/tuplestore.rb
45
+ - lib/rinda/version.rb
46
+ - more_rinda.gemspec
47
+ - test/ptuplespace_sample.rb
48
+ - test/test_njet.rb
49
+ homepage: https://github.com/seki/MoreRinda
50
+ licenses: []
51
+
52
+ post_install_message:
53
+ rdoc_options: []
54
+
55
+ require_paths:
56
+ - lib
57
+ required_ruby_version: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: "0"
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ none: false
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: "0"
69
+ requirements: []
70
+
71
+ rubyforge_project: drip
72
+ rubygems_version: 1.8.6.1
73
+ signing_key:
74
+ specification_version: 3
75
+ summary: Various extensions for Rinda::TupleSpace lovers.
76
+ test_files:
77
+ - test/ptuplespace_sample.rb
78
+ - test/test_njet.rb