riser 0.1.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.
@@ -0,0 +1,386 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'delegate'
4
+ require 'drb'
5
+ require 'forwardable'
6
+ require 'riser'
7
+ require 'set'
8
+
9
+ module Riser
10
+ class Resource
11
+ class Manager
12
+ def initialize(create, destroy)
13
+ @mutex = Thread::Mutex.new
14
+ @create = create
15
+ @destroy = destroy
16
+ @ref_count = 0
17
+ @ref_object = nil
18
+ @ref_proxy = {} # to keep proxy objects living in dRuby process
19
+ end
20
+
21
+ def ref_count
22
+ @mutex.synchronize{ @ref_count }
23
+ end
24
+
25
+ def proxy_count
26
+ @mutex.synchronize{ @ref_proxy.length }
27
+ end
28
+
29
+ def ref_object?
30
+ @mutex.synchronize{ ! @ref_object.nil? }
31
+ end
32
+
33
+ def ref_object
34
+ @mutex.synchronize{
35
+ if (@ref_count < 0) then
36
+ raise "internal error: negative reference count <#{@ref_count}>"
37
+ end
38
+
39
+ if (@ref_count == 0) then
40
+ # if an exception occurs at `@create.call', the object should not be referenced.
41
+ @ref_object = @create.call
42
+ end
43
+ @ref_count += 1
44
+ @ref_object
45
+ }
46
+ end
47
+
48
+ def unref_object
49
+ @mutex.synchronize{
50
+ unless (@ref_count > 0) then
51
+ raise "internal error: unreferenced resource object <#{@ref_count}>"
52
+ end
53
+
54
+ @ref_count -= 1
55
+ if (@ref_count == 0) then
56
+ tmp_object = @ref_object
57
+ @ref_object = nil
58
+ # even if an exception occurs at `@destroy.call', the object should be unreferenced.
59
+ @destroy.call(tmp_object)
60
+ end
61
+ }
62
+
63
+ nil
64
+ end
65
+
66
+ def ref_proxy(proxy)
67
+ @mutex.synchronize{
68
+ if (@ref_proxy.key? proxy.__id__) then
69
+ raise "internal error: duplicated proxy object <#{proxy.__id__}>"
70
+ end
71
+ @ref_proxy[proxy.__id__] = proxy
72
+ }
73
+ end
74
+
75
+ def unref_proxy(proxy)
76
+ @mutex.synchronize{
77
+ @ref_proxy.delete(proxy.__id__) or raise "internal error: unreferenced proxy object <#{proxy.__id__}>"
78
+ }
79
+ end
80
+ end
81
+
82
+ module Delegatable
83
+ def __getobj__
84
+ if (@delegate_proxy_obj.nil?) then
85
+ return yield if block_given?
86
+ __raise__ ::ArgumentError, "not delegated"
87
+ end
88
+ @delegate_proxy_obj
89
+ end
90
+
91
+ def __setobj__(obj)
92
+ __raise__ ::ArgumentError, "cannot delegate to self" if self.equal?(obj)
93
+ @delegate_proxy_obj = obj
94
+ end
95
+ protected :__setobj__
96
+ end
97
+
98
+ module DelegateUnrefAlias
99
+ def method_missing(name, *args, &block)
100
+ if (@unref_alias_set.include? name.to_sym) then
101
+ __unref__
102
+ else
103
+ super
104
+ end
105
+ end
106
+
107
+ def respond_to_missing?(name, include_private)
108
+ if (@unref_alias_set.include? name.to_sym) then
109
+ true
110
+ else
111
+ super
112
+ end
113
+ end
114
+ end
115
+
116
+ class Proxy < Delegator
117
+ include DRb::DRbUndumped
118
+ include Delegatable
119
+ include DelegateUnrefAlias
120
+
121
+ def initialize(manager, unref_alias_set)
122
+ @manager = manager
123
+ @unref_alias_set = unref_alias_set
124
+ # if an exception occurs at `@create.call', the proxy should not be referenced.
125
+ super(@manager.ref_object)
126
+ @manager.ref_proxy(self)
127
+ end
128
+
129
+ def __unref__
130
+ delegated = true
131
+ __getobj__{ delegated = false }
132
+
133
+ if (delegated) then
134
+ __setobj__(nil)
135
+ @manager.unref_proxy(self)
136
+ # even if an exception occurs at `@destroy.call', the proxy should be unreferenced.
137
+ @manager.unref_object
138
+ end
139
+
140
+ nil
141
+ end
142
+ end
143
+
144
+ class Builder
145
+ def initialize
146
+ @create = nil
147
+ @destroy = nil
148
+ @unref_alias_set = Set.new
149
+ end
150
+
151
+ def at_create(&block) # :yields:
152
+ @create = block
153
+ nil
154
+ end
155
+
156
+ def at_destroy(&block) # :yields: resource_object
157
+ @destroy = block
158
+ nil
159
+ end
160
+
161
+ def alias_unref(name)
162
+ @unref_alias_set << name.to_sym
163
+ nil
164
+ end
165
+
166
+ def call
167
+ @create or raise 'not defined create block'
168
+ @destroy or raise 'not defined destroy block'
169
+ Resource.new(Manager.new(@create, @destroy), @unref_alias_set)
170
+ end
171
+ end
172
+
173
+ def self.build
174
+ builder = Builder.new
175
+ yield(builder)
176
+ builder.call
177
+ end
178
+
179
+ extend Forwardable
180
+ include DRb::DRbUndumped
181
+
182
+ def initialize(manager, unref_alias_set)
183
+ @manager = manager
184
+ @unref_alias_set = unref_alias_set
185
+ end
186
+
187
+ def_delegators :@manager, :ref_count, :proxy_count, :ref_object?
188
+
189
+ def call
190
+ proxy = Proxy.new(@manager, @unref_alias_set)
191
+ if (block_given?) then
192
+ begin
193
+ yield(proxy)
194
+ ensure
195
+ proxy.__unref__
196
+ end
197
+ else
198
+ proxy
199
+ end
200
+ end
201
+ end
202
+
203
+ class ResourceSet
204
+ class Manager
205
+ Reference = Struct.new(:count, :object) # :nodoc:
206
+
207
+ def initialize(create, destroy)
208
+ @mutex = Thread::Mutex.new
209
+ @create = create
210
+ @destroy = destroy
211
+ @ref_table = {}
212
+ @ref_proxy = {} # to keep proxy objects living in dRuby process
213
+ end
214
+
215
+ def key_count
216
+ @mutex.synchronize{ @ref_table.size }
217
+ end
218
+
219
+ def ref_count(access_key)
220
+ @mutex.synchronize{
221
+ if (ref = @ref_table[access_key]) then
222
+ ref.count
223
+ else
224
+ 0
225
+ end
226
+ }
227
+ end
228
+
229
+ def proxy_count
230
+ @mutex.synchronize{ @ref_proxy.length }
231
+ end
232
+
233
+ def ref_object?(access_key)
234
+ @mutex.synchronize{
235
+ if (ref = @ref_table[access_key]) then
236
+ ! ref.object.nil?
237
+ else
238
+ false
239
+ end
240
+ }
241
+ end
242
+
243
+ def ref_object(access_key)
244
+ @mutex.synchronize{
245
+ if (ref = @ref_table[access_key]) then
246
+ unless (ref.count > 0) then
247
+ raise "internal error: unreferenced resource object <#{ref.count}>"
248
+ end
249
+ ref.count += 1
250
+ ref.object
251
+ else
252
+ # if an exception occurs at `@create.call', the object should not be referenced.
253
+ tmp_object = @create.call(access_key)
254
+ @ref_table[access_key] = Reference.new(1, tmp_object)
255
+ tmp_object
256
+ end
257
+ }
258
+ end
259
+
260
+ def unref_object(access_key)
261
+ @mutex.synchronize{
262
+ ref = @ref_table[access_key] or raise "internal error: not defined resource object <#{access_key}>"
263
+ unless (ref.count > 0) then
264
+ raise "internal error: unreferenced resource object <#{ref.count}>"
265
+ end
266
+
267
+ ref.count -= 1
268
+ if (ref.count == 0) then
269
+ @ref_table.delete(access_key)
270
+ # even if an exception occurs at `@destroy.call', the object should be unreferenced.
271
+ @destroy.call(ref.object)
272
+ end
273
+ }
274
+
275
+ nil
276
+ end
277
+
278
+ def ref_proxy(proxy)
279
+ @mutex.synchronize{
280
+ if (@ref_proxy.key? proxy.__id__) then
281
+ raise "internal error: duplicated proxy object <#{proxy.__id__}>"
282
+ end
283
+ @ref_proxy[proxy.__id__] = proxy
284
+ }
285
+ end
286
+
287
+ def unref_proxy(proxy)
288
+ @mutex.synchronize{
289
+ @ref_proxy.delete(proxy.__id__) or raise "internal error: unreferenced proxy object <#{proxy.__id__}>"
290
+ }
291
+ end
292
+ end
293
+
294
+ class Proxy < Delegator
295
+ include DRb::DRbUndumped
296
+ include Resource::Delegatable
297
+ include Resource::DelegateUnrefAlias
298
+
299
+ def initialize(manager, unref_alias_set, access_key)
300
+ @manager = manager
301
+ @unref_alias_set = unref_alias_set
302
+ @access_key = access_key
303
+ # if an exception occurs at `@create.call', the proxy should not be referenced.
304
+ __setobj__(@manager.ref_object(@access_key))
305
+ @manager.ref_proxy(self)
306
+ end
307
+
308
+ def __unref__
309
+ delegated = true
310
+ __getobj__{ delegated = false }
311
+
312
+ if (delegated) then
313
+ __setobj__(nil)
314
+ @manager.unref_proxy(self)
315
+ # even if an exception occurs at `@destroy.call', the proxy should be unreferenced.
316
+ @manager.unref_object(@access_key)
317
+ end
318
+
319
+ nil
320
+ end
321
+ end
322
+
323
+ class Builder
324
+ def initialize
325
+ @create = nil
326
+ @destroy = nil
327
+ @unref_alias_set = Set.new
328
+ end
329
+
330
+ def at_create(&block) # :yields: access_key
331
+ @create = block
332
+ nil
333
+ end
334
+
335
+ def at_destroy(&block) # :yields: resource_object
336
+ @destroy = block
337
+ nil
338
+ end
339
+
340
+ def alias_unref(name)
341
+ @unref_alias_set << name.to_sym
342
+ nil
343
+ end
344
+
345
+ def call
346
+ @create or raise 'not defined create block'
347
+ @destroy or raise 'not defined destroy block'
348
+ ResourceSet.new(Manager.new(@create, @destroy), @unref_alias_set)
349
+ end
350
+ end
351
+
352
+ def self.build
353
+ builder = Builder.new
354
+ yield(builder)
355
+ builder.call
356
+ end
357
+
358
+ extend Forwardable
359
+ include DRb::DRbUndumped
360
+
361
+ def initialize(manager, unref_alias_set)
362
+ @manager = manager
363
+ @unref_alias_set = unref_alias_set
364
+ end
365
+
366
+ def_delegators :@manager, :key_count, :ref_count, :proxy_count, :ref_object?
367
+
368
+ def call(access_key)
369
+ proxy = Proxy.new(@manager, @unref_alias_set, access_key)
370
+ if (block_given?) then
371
+ begin
372
+ yield(proxy)
373
+ ensure
374
+ proxy.__unref__
375
+ end
376
+ else
377
+ proxy
378
+ end
379
+ end
380
+ end
381
+ end
382
+
383
+ # Local Variables:
384
+ # mode: Ruby
385
+ # indent-tabs-mode: nil
386
+ # End: