more_rinda 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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