hakuban 0.6.5 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,398 +0,0 @@
1
- require 'hakuban/hakuban'
2
- require 'ostruct'
3
-
4
- #TODO: prevent usage of old object?
5
- #TODO: destructors
6
-
7
- module Hakuban
8
-
9
- class ObjectManagerBuilder
10
-
11
- def initialize(contract)
12
- @contract = contract
13
- @on_exception_policy = :retry
14
- @retry_backoff = [0.01, 0.1, 1.0, 10.0]
15
- @terminate_on_deactivation = false
16
- end
17
-
18
-
19
- def on_exception(policy)
20
- @on_exception_policy = policy
21
- self
22
- end
23
-
24
-
25
- def terminate_on_deactivation
26
- @terminate_on_deactivation = true
27
- self
28
- end
29
-
30
-
31
- def retry_backoff(*times)
32
- @retry_backoff = times.flatten
33
- self
34
- end
35
-
36
-
37
- def build(manager_class, block)
38
- raise "don't use build() directly, use .with_thread {} or .with_async {} instead" if not manager_class
39
- manager_class.new(@contract, block, @retry_backoff, @terminate_on_deactivation, @on_exception_policy)
40
- end
41
-
42
- end
43
-
44
-
45
- class ObjectManager
46
-
47
- attr_reader :contract
48
-
49
- class Event < OpenStruct; end
50
- class HandlerException < OpenStruct; end
51
-
52
-
53
- def initialize(contract, block, retry_backoff, terminate_on_deactivation, on_exception_policy)
54
- @contract = contract
55
- object_class = @contract.class.const_get(:ManagedObject)
56
- @objects_mutex = Mutex.new
57
- @existing_objects = {} #TODO: turn this into a WeakMap, to keep object if external code still holds reference to it
58
- @active_objects = {}
59
- @event_queue = Queue.new
60
-
61
- # This callback gets called from a separate thread, with no async reactor running.
62
- # So, we only use it to forward actions to the main thread.
63
- @ffi_callback = proc { |descriptor, action|
64
- @event_queue << Event.new(action: action, descriptor: descriptor)
65
- }
66
- @ffi_events = @contract.new_callback_event_queue
67
- @ffi_events.callback_register(&@ffi_callback)
68
-
69
- @async = async_run {
70
- while @event_queue and event = @event_queue.shift
71
- @objects_mutex.synchronize {
72
- case event.action
73
- when :insert, :change, :remove, :stopped, :timer
74
- object = @existing_objects[event.descriptor] ||= object_class.new(@contract, event.descriptor)
75
- if !object.running and object.async
76
- error = async_join(object.async)
77
- object.async = nil
78
- if object.last_exception_at
79
- interval_since_last_exception = Time.new - object.last_exception_at
80
- heal_multiplier = 10.0
81
- object.current_delay_index -= (interval_since_last_exception / (retry_backoff[-1]*heal_multiplier)).floor
82
- object.current_delay_index = 0 if object.current_delay_index < 0
83
- end
84
- if error.kind_of? HandlerException
85
- case on_exception_policy
86
- when :raise
87
- raise error.exception
88
- when :retry
89
- object.last_exception_at = Time.new
90
- object.earliest_next_run = Time.new + retry_backoff[object.current_delay_index]
91
- lambda_sleep_time = object.earliest_next_run - Time.new
92
- lambda_descriptor = event.descriptor
93
- async_run {
94
- sleep lambda_sleep_time
95
- @event_queue << Event.new(action: :timer, descriptor: lambda_descriptor) if @event_queue
96
- } #TODO: do we have to join this?
97
- object.current_delay_index += 1 if object.current_delay_index < retry_backoff.size - 1
98
- #when :ignore_failing_descriptor
99
- #when :drop_contract
100
- end
101
- end
102
- end
103
- if object.check_active
104
- if block and !object.running and (!object.earliest_next_run or Time.new >= object.earliest_next_run)
105
- descriptor_for_lambda = event.descriptor
106
- object.running = true
107
- object.async = async_run do
108
- async_filter_out_stop_exception {
109
- object.run(block)
110
- }
111
- rescue Exception => e
112
- $stderr.puts "Exception in hakuban manager lambda: #{e}"
113
- $stderr.puts "Contract: "+@contract.inspect
114
- $stderr.puts "Descriptor: "+descriptor_for_lambda.inspect
115
- $stderr.puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
116
- HandlerException.new(exception: e) # exception has to wrapped here to stop async from throwing on join
117
- ensure
118
- object.running = false
119
- @event_queue << Event.new(action: :stopped, descriptor: descriptor_for_lambda) if @event_queue
120
- end
121
- end
122
- @active_objects[event.descriptor] ||= object
123
- object.change
124
- else
125
- @active_objects.delete(event.descriptor)
126
- if object.running
127
- object.stop
128
- async_stop(object.async) if terminate_on_deactivation
129
- else
130
- @existing_objects.delete(event.descriptor)
131
- end
132
- end
133
- when :drop
134
- @ffi_events.callback_unregister
135
- @active_objects.clear
136
- @existing_objects.dup.each { |descriptor, object|
137
- if object.running
138
- object.stop
139
- async_stop(object.async) if terminate_on_deactivation
140
- else
141
- @existing_objects.delete(descriptor)
142
- end
143
- }
144
- while @existing_objects.size > 0
145
- event = @event_queue.shift
146
- @existing_objects.delete(event.descriptor) if event.action == :stopped
147
- end
148
- @event_queue.clear
149
- @contract.drop
150
- @contract, @event_queue = nil, nil
151
- break
152
- end
153
- }
154
- end
155
- }
156
- end
157
-
158
-
159
- def objects
160
- @objects_mutex.synchronize {
161
- @active_objects.dup
162
- }
163
- end
164
-
165
-
166
- def object
167
- @objects_mutex.synchronize {
168
- @active_objects.values.first
169
- }
170
- end
171
-
172
-
173
- def drop
174
- drop_nonblock
175
- async_join(@async)
176
- end
177
-
178
-
179
- def drop_nonblock
180
- if @contract
181
- @event_queue << Event.new(action: :drop)
182
- end
183
- end
184
- end
185
-
186
-
187
-
188
- class ManagedObjectBase
189
-
190
- attr_reader :descriptor
191
- attr_accessor :running, :async, :last_exception_at, :current_delay_index, :earliest_next_run
192
-
193
-
194
- def initialize(contract, descriptor)
195
- @contract,@descriptor = contract, descriptor
196
- @changes = Queue.new
197
- @current_delay_index = 0
198
- end
199
-
200
-
201
- def run(handler)
202
- handler.call(self)
203
- end
204
-
205
-
206
- def next_event; next_change; end
207
- def next_change
208
- loop {
209
- case @changes.shift
210
- when :change then return true
211
- when :stop then return false
212
- end
213
- }
214
- end
215
-
216
-
217
- def change
218
- @changes.push(:change)
219
- end
220
-
221
- def stop
222
- @changes.push(:stop)
223
- end
224
-
225
- end
226
-
227
-
228
-
229
- class ObjectObserve
230
-
231
- class ManagedObject < ManagedObjectBase
232
-
233
- def state
234
- @contract.object_state
235
- end
236
-
237
- def data
238
- @contract.object_state&.data
239
- end
240
-
241
- def check_active
242
- !!state
243
- end
244
-
245
- end
246
-
247
-
248
- def manage
249
- ObjectManagerBuilder.new(self)
250
- end
251
-
252
- end
253
-
254
-
255
- class ObjectExpose
256
-
257
- class ManagedObject < ManagedObjectBase
258
-
259
- def initialize(contract, descriptor)
260
- super(contract, descriptor)
261
- @assignment = contract.assignment
262
- end
263
-
264
- def run(handler)
265
- super
266
- @contract.desynchronize(@assignment)
267
- end
268
-
269
- def do_change(change)
270
- @assignment = @contract.assignment
271
- super(change)
272
- end
273
-
274
- def assignement
275
- @assignment = @contract.assignment()
276
- end
277
-
278
- def assigned?
279
- @contract.assigned?()
280
- end
281
-
282
- def set_state(*state, **kwargs)
283
- kwargs[:assignment] ||= @assignment
284
- @contract.set_object_state(*state, **kwargs)
285
- end
286
-
287
- def state=(state)
288
- set_state(*state)
289
- end
290
-
291
- def set_data(value)
292
- timestamp = Time.new.to_f
293
- set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
294
- end
295
-
296
- def data=(value)
297
- set_data(value)
298
- end
299
-
300
- def check_active
301
- assigned?
302
- end
303
-
304
- end
305
-
306
-
307
- def manage
308
- ObjectManagerBuilder.new(self)
309
- end
310
-
311
- end
312
-
313
-
314
- class TagObserve
315
-
316
- class ManagedObject < ManagedObjectBase
317
-
318
- def state
319
- @contract.object_state(@descriptor)
320
- end
321
-
322
- def data
323
- @contract.object_state(@descriptor)&.data
324
- end
325
-
326
- def check_active
327
- !!state
328
- end
329
-
330
- end
331
-
332
-
333
- def manage
334
- ObjectManagerBuilder.new(self)
335
- end
336
-
337
- end
338
-
339
-
340
- class TagExpose
341
-
342
- class ManagedObject < ManagedObjectBase
343
-
344
- def initialize(contract, descriptor)
345
- super(contract, descriptor)
346
- @assignment = contract.assignment(@descriptor)
347
- end
348
-
349
- def run(handler)
350
- super
351
- @contract.desynchronize(@descriptor, @assignment)
352
- end
353
-
354
- def do_change(change)
355
- @assignment = @contract.assignment(@descriptor)
356
- super(change)
357
- end
358
-
359
- def assignement
360
- @assignment = @contract.assignment(@descriptor)
361
- end
362
-
363
- def assigned?
364
- @contract.assigned?(@descriptor)
365
- end
366
-
367
- def set_state(*args, **kwargs)
368
- kwargs[:assignment] ||= @assignment
369
- @contract.set_object_state(@descriptor, *args, **kwargs)
370
- end
371
-
372
- def state=(state)
373
- set_state(*state)
374
- end
375
-
376
- def set_data(value)
377
- timestamp = Time.new.to_f
378
- set_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
379
- end
380
-
381
- def data=(value)
382
- set_data(value)
383
- end
384
-
385
- def check_active
386
- assigned?
387
- end
388
-
389
- end
390
-
391
-
392
- def manage
393
- ObjectManagerBuilder.new(self)
394
- end
395
-
396
- end
397
-
398
- end
@@ -1,35 +0,0 @@
1
- require 'hakuban'
2
- require 'hakuban/manager'
3
-
4
- module Hakuban
5
-
6
- class ThreadObjectManager < ObjectManager
7
-
8
- def async_run
9
- Thread.new { yield }
10
- end
11
-
12
- def async_join(thread)
13
- thread.value
14
- end
15
-
16
- def async_stop(thread)
17
- thread.kill
18
- end
19
-
20
- def async_filter_out_stop_exception
21
- yield
22
- end
23
-
24
- end
25
-
26
-
27
- class ObjectManagerBuilder
28
-
29
- def with_thread(&block)
30
- build(ThreadObjectManager, block)
31
- end
32
-
33
- end
34
-
35
- end