hakuban 0.6.4 → 0.7.0

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