hakuban 0.6.1 → 0.6.4
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.
- checksums.yaml +4 -4
- data/bin/hakuban-observer +31 -23
- data/lib/hakuban/async.rb +11 -24
- data/lib/hakuban/event-queue.rb +75 -0
- data/lib/hakuban/hakuban.rb +55 -40
- data/lib/hakuban/manager.rb +195 -54
- data/lib/hakuban/thread.rb +9 -22
- data/lib/hakuban/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c827f2e6b27d41fbc349a9928b2039fc04b9cd139ce90641491b3dee250482c
|
4
|
+
data.tar.gz: 6081be62f7bc8abf6ea90543ee79543bc6b7f20678fbe5cce38bd72fc09de897
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 36ed621045173a200cdcf65832f6c126496aae26584c74fc07ceebc303b22f387e22852b03034b0b97164b0b06e739c5b192d06babc275c49b75a8ca5b872f4a
|
7
|
+
data.tar.gz: 35bb3c5a6937b3fbecf3088d90f2da945e7b6643d736df272896eb5a8a2c624bf62629e2c598a2206a4a98bab444f0a6b3736aed803d04c085a2746d54d43653
|
data/bin/hakuban-observer
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
|
3
3
|
require 'slop'
|
4
4
|
require 'pp'
|
5
|
-
require 'hakuban/
|
5
|
+
require 'hakuban/thread'
|
6
6
|
|
7
7
|
|
8
8
|
OPTIONS = Slop.parse { |o|
|
@@ -27,30 +27,38 @@ else
|
|
27
27
|
end
|
28
28
|
|
29
29
|
|
30
|
-
def print_event(descriptor, event)
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
puts "
|
30
|
+
def print_event(descriptor, event, more)
|
31
|
+
if $subsequent
|
32
|
+
puts ','
|
33
|
+
else
|
34
|
+
$subsequent = true
|
35
|
+
end
|
36
|
+
puts '{'
|
37
|
+
puts '"event": "%s",'%[event]
|
38
|
+
puts '"descriptor_json": %s,'%[JSON.dump(descriptor.json)]
|
39
|
+
puts '"descriptor_tags": [%s],'%[descriptor.tags.map { |tag| JSON.dump(tag.json) }.join(",")]
|
40
|
+
puts more if more
|
41
|
+
puts '}'
|
38
42
|
end
|
39
43
|
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
|
45
|
+
puts "["
|
46
|
+
|
47
|
+
contract = observe_contract.manage.with_thread { |object|
|
48
|
+
print_event(object.descriptor, "create", nil)
|
49
|
+
while object.next_change
|
50
|
+
print_event(object.descriptor, "change",
|
46
51
|
if state = object.state
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
52
|
+
[
|
53
|
+
'"version": %s,'%[JSON.dump(state.version)],
|
54
|
+
'"last_sync_ms_ago": %s,'%[JSON.dump(state.synchronized)],
|
55
|
+
'"data": %s,'%[JSON.dump(state.data)],
|
56
|
+
].join("\n")
|
51
57
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
58
|
+
)
|
59
|
+
end
|
60
|
+
print_event(object.descriptor, "drop", nil)
|
56
61
|
}
|
62
|
+
|
63
|
+
$stdin.read(1)
|
64
|
+
puts "]"
|
data/lib/hakuban/async.rb
CHANGED
@@ -5,41 +5,28 @@ require 'async'
|
|
5
5
|
module Hakuban
|
6
6
|
|
7
7
|
class AsyncObjectManager < ObjectManager
|
8
|
+
|
8
9
|
def async_run
|
9
10
|
Async { yield }
|
10
11
|
end
|
11
12
|
|
12
|
-
def async_join(
|
13
|
-
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
|
18
|
-
class ObjectObserve
|
19
|
-
def async(&block)
|
20
|
-
AsyncObjectManager.new(self, ObservedObject, block)
|
13
|
+
def async_join(task)
|
14
|
+
task.wait
|
21
15
|
end
|
22
|
-
end
|
23
|
-
|
24
|
-
|
25
|
-
class ObjectExpose
|
26
|
-
def async(&block)
|
27
|
-
AsyncObjectManager.new(self, ExposedObject, block)
|
28
|
-
end
|
29
|
-
end
|
30
|
-
|
31
16
|
|
32
|
-
|
33
|
-
|
34
|
-
AsyncObjectManager.new(self, ObservedObject, block)
|
17
|
+
def async_stop(task)
|
18
|
+
task.stop
|
35
19
|
end
|
20
|
+
|
36
21
|
end
|
37
22
|
|
38
23
|
|
39
|
-
class
|
40
|
-
|
41
|
-
|
24
|
+
class ObjectManagerBuilder
|
25
|
+
|
26
|
+
def with_async(&block)
|
27
|
+
build(AsyncObjectManager, block)
|
42
28
|
end
|
29
|
+
|
43
30
|
end
|
44
31
|
|
45
32
|
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
#TODO: rebuild to handle event squashing on rust side
|
2
|
+
|
3
|
+
require 'ostruct'
|
4
|
+
|
5
|
+
|
6
|
+
module Hakuban
|
7
|
+
|
8
|
+
class Event < OpenStruct; end
|
9
|
+
|
10
|
+
class ObjectDescriptorCallbackEventQueue
|
11
|
+
|
12
|
+
def initialize(pointer)
|
13
|
+
@events_pointer = ::FFI::AutoPointer.new(pointer, proc { |ptr| FFI.hakuban_object_descriptor_events_return(ptr) })
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
# WARNING: this callback may be run from a separate, non-ruby, thread
|
18
|
+
def callback_register(&callback)
|
19
|
+
ffi_callback = proc { |_userdata, ffi_descriptor, ffi_action|
|
20
|
+
action = Hakuban::action_int_to_symbol(ffi_action)
|
21
|
+
descriptor = ObjectDescriptor.from_ffi(ffi_descriptor)
|
22
|
+
callback.call(descriptor, action)
|
23
|
+
}
|
24
|
+
@callback_pointer = ::FFI::AutoPointer.new(
|
25
|
+
FFI::hakuban_object_descriptor_events_callback_register(@events_pointer, ffi_callback, ::FFI::Pointer::NULL),
|
26
|
+
proc { |ptr| FFI::hakuban_object_descriptor_events_callback_unregister(ptr) }
|
27
|
+
)
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
def callback_unregister
|
32
|
+
@callback_pointer.free
|
33
|
+
@callback_pointer = nil
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
def drop
|
38
|
+
@events_pointer.free
|
39
|
+
@events_pointer = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
class ObjectDescriptorEventQueue
|
46
|
+
|
47
|
+
def initialize(contract)
|
48
|
+
@contract = contract
|
49
|
+
@queue = Queue.new
|
50
|
+
@ffi_callback = proc { |descriptor, action|
|
51
|
+
@queue << Hakuban::Event.new(action: action, descriptor: descriptor)
|
52
|
+
}
|
53
|
+
@ffi_events = @contract.new_callback_event_queue
|
54
|
+
@ffi_events.callback_register(&@ffi_callback)
|
55
|
+
end
|
56
|
+
|
57
|
+
|
58
|
+
#def push(event)
|
59
|
+
# @queue << event
|
60
|
+
#end
|
61
|
+
|
62
|
+
|
63
|
+
def next_event; next_change; end
|
64
|
+
def next_change
|
65
|
+
@queue.pop
|
66
|
+
end
|
67
|
+
|
68
|
+
|
69
|
+
def close
|
70
|
+
@queue.close
|
71
|
+
end
|
72
|
+
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
data/lib/hakuban/hakuban.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'set'
|
2
2
|
require 'ostruct'
|
3
3
|
|
4
|
-
require_relative './
|
4
|
+
require_relative './event-queue.rb'
|
5
5
|
|
6
6
|
#TODO: explicit drops?
|
7
7
|
#TODO: privative methods
|
@@ -32,9 +32,14 @@ module Hakuban
|
|
32
32
|
end
|
33
33
|
|
34
34
|
|
35
|
+
def self.hakuban_initialize
|
36
|
+
require_relative './ffi.rb'
|
37
|
+
end
|
38
|
+
|
35
39
|
@@logger_initialized = false
|
36
40
|
|
37
41
|
def self.logger_initialize(default_level, skip_if_already_initialized = false)
|
42
|
+
::Hakuban::hakuban_initialize
|
38
43
|
if @@logger_initialized and !skip_if_already_initialized
|
39
44
|
raise "Logger already initialized. This can't be done more than once. Make sure logger_initialize is called before any LocalNode gets constructed."
|
40
45
|
end
|
@@ -52,6 +57,7 @@ module Hakuban
|
|
52
57
|
attr_reader :local_node_pointer #todo: hide
|
53
58
|
|
54
59
|
def initialize(name: nil)
|
60
|
+
::Hakuban::hakuban_initialize
|
55
61
|
@default_serializer = lambda { |data_type, data|
|
56
62
|
[["JSON"]+data_type, JSON.dump(data)]
|
57
63
|
}
|
@@ -215,36 +221,6 @@ module Hakuban
|
|
215
221
|
end
|
216
222
|
|
217
223
|
|
218
|
-
class ObjectDescriptorEvents
|
219
|
-
def initialize(pointer)
|
220
|
-
@events_pointer = ::FFI::AutoPointer.new(pointer, proc { |ptr| FFI.hakuban_object_descriptor_events_return(ptr) })
|
221
|
-
end
|
222
|
-
|
223
|
-
# WARNING: this callback may be run from a separate, non-ruby, thread
|
224
|
-
def callback_register(&callback)
|
225
|
-
ffi_callback = proc { |_userdata, ffi_descriptor, ffi_action|
|
226
|
-
action = Hakuban::action_int_to_symbol(ffi_action)
|
227
|
-
descriptor = ObjectDescriptor.from_ffi(ffi_descriptor)
|
228
|
-
callback.call(descriptor, action)
|
229
|
-
}
|
230
|
-
@callback_pointer = ::FFI::AutoPointer.new(
|
231
|
-
FFI::hakuban_object_descriptor_events_callback_register(@events_pointer, ffi_callback, ::FFI::Pointer::NULL),
|
232
|
-
proc { |ptr| FFI::hakuban_object_descriptor_events_callback_unregister(ptr) }
|
233
|
-
)
|
234
|
-
end
|
235
|
-
|
236
|
-
def callback_unregister
|
237
|
-
@callback_pointer.free
|
238
|
-
@callback_pointer = nil
|
239
|
-
end
|
240
|
-
|
241
|
-
def drop
|
242
|
-
@events_pointer.free
|
243
|
-
@events_pointer = nil
|
244
|
-
end
|
245
|
-
|
246
|
-
end
|
247
|
-
|
248
224
|
class ObjectObserve
|
249
225
|
|
250
226
|
attr_reader :descriptor
|
@@ -253,6 +229,7 @@ module Hakuban
|
|
253
229
|
@local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
|
254
230
|
result = FFI::hakuban_object_observe_new(@local_node.local_node_pointer, descriptor.to_ffi)
|
255
231
|
Hakuban::raise_if_error(result)
|
232
|
+
@queues = []
|
256
233
|
@object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], proc { |ptr| FFI::hakuban_object_observe_drop(ptr) })
|
257
234
|
end
|
258
235
|
|
@@ -265,9 +242,16 @@ module Hakuban
|
|
265
242
|
end
|
266
243
|
end
|
267
244
|
|
268
|
-
def
|
245
|
+
def new_callback_event_queue
|
246
|
+
raise "Attempt to use after 'drop'" if not @object_observe_pointer
|
247
|
+
ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_object_observe_events_get(@object_observe_pointer))
|
248
|
+
end
|
249
|
+
|
250
|
+
def new_event_queue
|
269
251
|
raise "Attempt to use after 'drop'" if not @object_observe_pointer
|
270
|
-
|
252
|
+
queue = ObjectDescriptorEventQueue.new(self)
|
253
|
+
@queues << queue
|
254
|
+
queue
|
271
255
|
end
|
272
256
|
|
273
257
|
def inspect
|
@@ -277,6 +261,8 @@ module Hakuban
|
|
277
261
|
def drop
|
278
262
|
@object_observe_pointer.free
|
279
263
|
@object_observe_pointer = nil
|
264
|
+
@queues.each(&:close) #is this atomic?
|
265
|
+
@queues.clear
|
280
266
|
end
|
281
267
|
|
282
268
|
def dropped?
|
@@ -294,6 +280,7 @@ module Hakuban
|
|
294
280
|
@local_node, @descriptor, @serializer = local_node, descriptor, serializer
|
295
281
|
result = FFI::hakuban_object_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
|
296
282
|
Hakuban::raise_if_error(result)
|
283
|
+
@queues = []
|
297
284
|
@object_expose_pointer = ::FFI::AutoPointer.new(result[:object_expose_pointer], FFI::method(:hakuban_object_expose_drop))
|
298
285
|
end
|
299
286
|
|
@@ -319,9 +306,16 @@ module Hakuban
|
|
319
306
|
FFI::hakuban_object_expose_desynchronize(@object_expose_pointer, assignment)
|
320
307
|
end
|
321
308
|
|
322
|
-
def
|
309
|
+
def new_callback_event_queue
|
323
310
|
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
324
|
-
|
311
|
+
ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_object_expose_events_get(@object_expose_pointer))
|
312
|
+
end
|
313
|
+
|
314
|
+
def new_event_queue
|
315
|
+
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
316
|
+
queue = ObjectDescriptorEventQueue.new(self)
|
317
|
+
@queues << queue
|
318
|
+
queue
|
325
319
|
end
|
326
320
|
|
327
321
|
def inspect
|
@@ -331,7 +325,8 @@ module Hakuban
|
|
331
325
|
def drop
|
332
326
|
@object_expose_pointer.free
|
333
327
|
@object_expose_pointer = nil
|
334
|
-
|
328
|
+
@queues.each(&:close) #is this atomic?
|
329
|
+
@queues.clear end
|
335
330
|
|
336
331
|
def dropped?
|
337
332
|
@object_expose_pointer.nil?
|
@@ -394,6 +389,7 @@ module Hakuban
|
|
394
389
|
@local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
|
395
390
|
result = FFI::hakuban_tag_observe_new(@local_node.local_node_pointer, @descriptor.to_ffi)
|
396
391
|
Hakuban::raise_if_error(result)
|
392
|
+
@queues = []
|
397
393
|
@tag_observe_pointer = ::FFI::AutoPointer.new(result[:tag_observe_pointer], FFI::method(:hakuban_tag_observe_drop))
|
398
394
|
end
|
399
395
|
|
@@ -413,14 +409,23 @@ module Hakuban
|
|
413
409
|
ObjectObserveState.new(result[:state], @deserializer)
|
414
410
|
end
|
415
411
|
|
416
|
-
def
|
412
|
+
def new_callback_event_queue
|
417
413
|
raise "Attempt to use after 'drop'" if not @tag_observe_pointer
|
418
|
-
|
414
|
+
ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_tag_observe_events_get(@tag_observe_pointer))
|
415
|
+
end
|
416
|
+
|
417
|
+
def new_event_queue
|
418
|
+
raise "Attempt to use after 'drop'" if not @tag_observe_pointer
|
419
|
+
queue = ObjectDescriptorEventQueue.new(self)
|
420
|
+
@queues << queue
|
421
|
+
queue
|
419
422
|
end
|
420
423
|
|
421
424
|
def drop
|
422
425
|
@tag_observe_pointer.free
|
423
426
|
@tag_observe_pointer = nil
|
427
|
+
@queues.each(&:close) #is this atomic?
|
428
|
+
@queues.clear
|
424
429
|
end
|
425
430
|
|
426
431
|
def dropped?
|
@@ -438,6 +443,7 @@ module Hakuban
|
|
438
443
|
@local_node, @descriptor, @serializer = local_node, descriptor, serializer
|
439
444
|
result = FFI::hakuban_tag_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
|
440
445
|
Hakuban::raise_if_error(result)
|
446
|
+
@queues = []
|
441
447
|
@tag_expose_pointer = ::FFI::AutoPointer.new(result[:tag_expose_pointer], FFI::method(:hakuban_tag_expose_drop))
|
442
448
|
end
|
443
449
|
|
@@ -472,14 +478,23 @@ module Hakuban
|
|
472
478
|
FFI::hakuban_tag_expose_object_desynchronize(@tag_expose_pointer, object_descriptor.to_ffi, assignment)
|
473
479
|
end
|
474
480
|
|
475
|
-
def
|
481
|
+
def new_callback_event_queue
|
482
|
+
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
483
|
+
ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_tag_expose_events_get(@tag_expose_pointer))
|
484
|
+
end
|
485
|
+
|
486
|
+
def new_event_queue
|
476
487
|
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
477
|
-
|
488
|
+
queue = ObjectDescriptorEventQueue.new(self)
|
489
|
+
@queues << queue
|
490
|
+
queue
|
478
491
|
end
|
479
492
|
|
480
493
|
def drop
|
481
494
|
@tag_expose_pointer.free
|
482
495
|
@tag_expose_pointer = nil
|
496
|
+
@queues.each(&:close) #is this atomic?
|
497
|
+
@queues.clear
|
483
498
|
end
|
484
499
|
|
485
500
|
def dropped?
|
data/lib/hakuban/manager.rb
CHANGED
@@ -6,16 +6,58 @@ require 'ostruct'
|
|
6
6
|
|
7
7
|
module Hakuban
|
8
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
|
+
|
9
45
|
class ObjectManager
|
10
46
|
|
11
47
|
attr_reader :contract
|
12
48
|
|
13
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
|
14
54
|
|
15
|
-
def initialize(contract,
|
55
|
+
def initialize(contract, block, retry_backoff, terminate_on_deactivation, on_exception_policy)
|
16
56
|
@contract = contract
|
17
|
-
|
18
|
-
@
|
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 = {}
|
19
61
|
@event_queue = Queue.new
|
20
62
|
|
21
63
|
# This callback gets called from a separate thread, with no async reactor running.
|
@@ -23,60 +65,119 @@ module Hakuban
|
|
23
65
|
@ffi_callback = proc { |descriptor, action|
|
24
66
|
@event_queue << Event.new(action: action, descriptor: descriptor)
|
25
67
|
}
|
26
|
-
@ffi_events = @contract.
|
68
|
+
@ffi_events = @contract.new_callback_event_queue
|
27
69
|
@ffi_events.callback_register(&@ffi_callback)
|
28
70
|
|
29
71
|
@async = async_run {
|
30
|
-
while event = @event_queue.shift
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
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
|
49
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
|
50
154
|
end
|
51
|
-
|
52
|
-
raise if not @running_handlers[event.descriptor]
|
53
|
-
@running_handlers.delete(event.descriptor)
|
54
|
-
@event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if @objects[event.descriptor]
|
55
|
-
when :drop
|
56
|
-
@ffi_events.callback_unregister
|
57
|
-
@objects.values.each { |object| object.do_change(:remove) }
|
58
|
-
while @running_handlers.size > 0
|
59
|
-
event = @event_queue.shift
|
60
|
-
@running_handlers.delete(event.descriptor) if event.action == :handler_finished
|
61
|
-
end
|
62
|
-
@event_queue.clear
|
63
|
-
@objects.clear
|
64
|
-
@contract.drop
|
65
|
-
@contract, @event_queue = nil, nil
|
66
|
-
break
|
67
|
-
end
|
155
|
+
}
|
68
156
|
end
|
69
157
|
}
|
70
158
|
end
|
71
159
|
|
72
|
-
|
73
|
-
def
|
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
|
+
|
74
174
|
|
75
175
|
def drop
|
76
176
|
drop_nonblock
|
77
177
|
async_join(@async)
|
78
178
|
end
|
79
179
|
|
180
|
+
|
80
181
|
def drop_nonblock
|
81
182
|
if @contract
|
82
183
|
@event_queue << Event.new(action: :drop)
|
@@ -86,20 +187,20 @@ module Hakuban
|
|
86
187
|
|
87
188
|
|
88
189
|
|
89
|
-
class
|
190
|
+
class ManagedObjectBase
|
90
191
|
|
91
|
-
attr_reader :descriptor
|
192
|
+
attr_reader :descriptor
|
193
|
+
attr_accessor :running, :async, :last_exception_at, :current_delay_index, :earliest_next_run
|
92
194
|
|
93
195
|
|
94
196
|
def initialize(contract, descriptor)
|
95
197
|
@contract,@descriptor = contract, descriptor
|
96
198
|
@changes = Queue.new
|
97
|
-
@
|
199
|
+
@current_delay_index = 0
|
98
200
|
end
|
99
201
|
|
100
202
|
|
101
203
|
def run(handler)
|
102
|
-
@handler_already_run = true
|
103
204
|
handler.call(self)
|
104
205
|
end
|
105
206
|
|
@@ -109,23 +210,27 @@ module Hakuban
|
|
109
210
|
loop {
|
110
211
|
case @changes.shift
|
111
212
|
when :change then return true
|
112
|
-
when :
|
213
|
+
when :stop then return false
|
113
214
|
end
|
114
215
|
}
|
115
216
|
end
|
116
217
|
|
117
218
|
|
118
|
-
def
|
119
|
-
@changes.push(change)
|
219
|
+
def change
|
220
|
+
@changes.push(:change)
|
120
221
|
end
|
121
222
|
|
223
|
+
def stop
|
224
|
+
@changes.push(:stop)
|
225
|
+
end
|
226
|
+
|
122
227
|
end
|
123
228
|
|
124
229
|
|
125
230
|
|
126
231
|
class ObjectObserve
|
127
232
|
|
128
|
-
class
|
233
|
+
class ManagedObject < ManagedObjectBase
|
129
234
|
|
130
235
|
def state
|
131
236
|
@contract.object_state
|
@@ -135,6 +240,15 @@ module Hakuban
|
|
135
240
|
@contract.object_state&.data
|
136
241
|
end
|
137
242
|
|
243
|
+
def check_active
|
244
|
+
!!state
|
245
|
+
end
|
246
|
+
|
247
|
+
end
|
248
|
+
|
249
|
+
|
250
|
+
def manage
|
251
|
+
ObjectManagerBuilder.new(self)
|
138
252
|
end
|
139
253
|
|
140
254
|
end
|
@@ -142,7 +256,7 @@ module Hakuban
|
|
142
256
|
|
143
257
|
class ObjectExpose
|
144
258
|
|
145
|
-
class
|
259
|
+
class ManagedObject < ManagedObjectBase
|
146
260
|
|
147
261
|
def initialize(contract, descriptor)
|
148
262
|
super(contract, descriptor)
|
@@ -185,6 +299,15 @@ module Hakuban
|
|
185
299
|
set_data(value)
|
186
300
|
end
|
187
301
|
|
302
|
+
def check_active
|
303
|
+
assigned?
|
304
|
+
end
|
305
|
+
|
306
|
+
end
|
307
|
+
|
308
|
+
|
309
|
+
def manage
|
310
|
+
ObjectManagerBuilder.new(self)
|
188
311
|
end
|
189
312
|
|
190
313
|
end
|
@@ -192,7 +315,7 @@ module Hakuban
|
|
192
315
|
|
193
316
|
class TagObserve
|
194
317
|
|
195
|
-
class
|
318
|
+
class ManagedObject < ManagedObjectBase
|
196
319
|
|
197
320
|
def state
|
198
321
|
@contract.object_state(@descriptor)
|
@@ -202,6 +325,15 @@ module Hakuban
|
|
202
325
|
@contract.object_state(@descriptor)&.data
|
203
326
|
end
|
204
327
|
|
328
|
+
def check_active
|
329
|
+
!!state
|
330
|
+
end
|
331
|
+
|
332
|
+
end
|
333
|
+
|
334
|
+
|
335
|
+
def manage
|
336
|
+
ObjectManagerBuilder.new(self)
|
205
337
|
end
|
206
338
|
|
207
339
|
end
|
@@ -209,7 +341,7 @@ module Hakuban
|
|
209
341
|
|
210
342
|
class TagExpose
|
211
343
|
|
212
|
-
class
|
344
|
+
class ManagedObject < ManagedObjectBase
|
213
345
|
|
214
346
|
def initialize(contract, descriptor)
|
215
347
|
super(contract, descriptor)
|
@@ -252,8 +384,17 @@ module Hakuban
|
|
252
384
|
set_data(value)
|
253
385
|
end
|
254
386
|
|
387
|
+
def check_active
|
388
|
+
assigned?
|
389
|
+
end
|
390
|
+
|
391
|
+
end
|
392
|
+
|
393
|
+
|
394
|
+
def manage
|
395
|
+
ObjectManagerBuilder.new(self)
|
255
396
|
end
|
256
397
|
|
257
398
|
end
|
258
399
|
|
259
|
-
end
|
400
|
+
end
|
data/lib/hakuban/thread.rb
CHANGED
@@ -4,41 +4,28 @@ require 'hakuban/manager'
|
|
4
4
|
module Hakuban
|
5
5
|
|
6
6
|
class ThreadObjectManager < ObjectManager
|
7
|
+
|
7
8
|
def async_run
|
8
9
|
Thread.new { yield }
|
9
10
|
end
|
10
11
|
|
11
12
|
def async_join(thread)
|
12
|
-
thread.
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
|
17
|
-
class ObjectObserve
|
18
|
-
def thread(&block)
|
19
|
-
ThreadObjectManager.new(self, ObservedObject, block)
|
13
|
+
thread.value
|
20
14
|
end
|
21
|
-
end
|
22
|
-
|
23
15
|
|
24
|
-
|
25
|
-
|
26
|
-
ThreadObjectManager.new(self, ExposedObject, block)
|
16
|
+
def async_stop(thread)
|
17
|
+
thread.kill
|
27
18
|
end
|
19
|
+
|
28
20
|
end
|
29
21
|
|
30
22
|
|
31
|
-
class
|
32
|
-
|
33
|
-
|
23
|
+
class ObjectManagerBuilder
|
24
|
+
|
25
|
+
def with_thread(&block)
|
26
|
+
build(ThreadObjectManager, block)
|
34
27
|
end
|
35
|
-
end
|
36
28
|
|
37
|
-
|
38
|
-
class TagExpose
|
39
|
-
def thread(&block)
|
40
|
-
ThreadObjectManager.new(self, ExposedObject, block)
|
41
|
-
end
|
42
29
|
end
|
43
30
|
|
44
31
|
end
|
data/lib/hakuban/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hakuban
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- yunta
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-03-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rspec
|
@@ -83,6 +83,7 @@ files:
|
|
83
83
|
- lib/hakuban.rb
|
84
84
|
- lib/hakuban/async.rb
|
85
85
|
- lib/hakuban/engine.rb
|
86
|
+
- lib/hakuban/event-queue.rb
|
86
87
|
- lib/hakuban/ffi.rb
|
87
88
|
- lib/hakuban/hakuban.rb
|
88
89
|
- lib/hakuban/manager.rb
|