riser 0.1.0

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