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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f12beca62a4975af1ee3f85d5ec83e85753a6f6bb95fb36529c284d88eba41c5
4
- data.tar.gz: ca7a3c890c4ba1b7dc337ac1e3120b8d5d260c8170f641493067b5f75060c3ab
3
+ metadata.gz: 8c827f2e6b27d41fbc349a9928b2039fc04b9cd139ce90641491b3dee250482c
4
+ data.tar.gz: 6081be62f7bc8abf6ea90543ee79543bc6b7f20678fbe5cce38bd72fc09de897
5
5
  SHA512:
6
- metadata.gz: fb3e86f190c140edb9658f42f33cb9bfdad718cb6b04a9f1bced7240ed2b64716d43ab651f3551b4ee084d103fdfc3bf038dc7e3f91332f1bac692e5b9b7001f
7
- data.tar.gz: fd40ec84a8e7f713f857768e84dd0f5554beb00adf586adf7e739e12e1920cb568387df5cc8c4085c7bec83fbffc36a5d16bbe7211481774e849b5dda402fab6
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/async'
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
- puts "-"*120
32
- puts "Event: " + event
33
- puts "Descriptor: "
34
- descriptor.tags.each { |tag|
35
- puts "\tTags: " + JSON.dump(tag.json)
36
- }
37
- puts "\tJSON: " + JSON.dump(descriptor.json)
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
- Async {
41
-
42
- observe_contract.async { |object|
43
- print_event(object.descriptor, "create")
44
- while object.next_change
45
- print_event(object.descriptor, "change")
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
- puts "Version: " + state.version.inspect
48
- puts "Last synchronized: " + state.synchronized.to_s + "ms ago"
49
- puts "Data:\n"
50
- pp state.data
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
- end
53
- print_event(object.descriptor, "drop")
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(async)
13
- async.wait
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
- class TagObserve
33
- def async(&block)
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 TagExpose
40
- def async(&block)
41
- AsyncObjectManager.new(self, ExposedObject, block)
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
@@ -1,7 +1,7 @@
1
1
  require 'set'
2
2
  require 'ostruct'
3
3
 
4
- require_relative './ffi.rb'
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 events
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
- ObjectDescriptorEvents.new(FFI::hakuban_object_observe_events_get(@object_observe_pointer))
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 events
309
+ def new_callback_event_queue
323
310
  raise "Attempt to use after 'drop'" if not @object_expose_pointer
324
- ObjectDescriptorEvents.new(FFI::hakuban_object_expose_events_get(@object_expose_pointer))
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
- end
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 events
412
+ def new_callback_event_queue
417
413
  raise "Attempt to use after 'drop'" if not @tag_observe_pointer
418
- ObjectDescriptorEvents.new(FFI::hakuban_tag_observe_events_get(@tag_observe_pointer))
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 events
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
- ObjectDescriptorEvents.new(FFI::hakuban_tag_expose_events_get(@tag_expose_pointer))
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?
@@ -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, object_class, block)
55
+ def initialize(contract, block, retry_backoff, terminate_on_deactivation, on_exception_policy)
16
56
  @contract = contract
17
- @objects = {}
18
- @running_handlers = {}
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.events
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
- case event.action
32
- when :insert
33
- raise if @objects[event.descriptor]
34
- @objects[event.descriptor] = object_class.new(@contract, event.descriptor)
35
- @event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if block
36
- when :change
37
- raise if not @objects[event.descriptor]
38
- @objects[event.descriptor].do_change(:change)
39
- when :remove
40
- raise if not @objects[event.descriptor]
41
- @objects[event.descriptor].do_change(:remove)
42
- @objects.delete(event.descriptor)
43
- when :handler_start
44
- if (object = @objects[event.descriptor]) and !object.handler_already_run and !@running_handlers[event.descriptor]
45
- descriptor_for_lambda = event.descriptor
46
- @running_handlers[event.descriptor] = async_run {
47
- object.run(block)
48
- @event_queue << Event.new(action: :handler_finished, descriptor: descriptor_for_lambda) if @event_queue
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
- when :handler_finished
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
- def objects; @objects; end
73
- def object; @objects.values.first; end
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 ManagedObject
190
+ class ManagedObjectBase
90
191
 
91
- attr_reader :descriptor, :async, :handler_already_run
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
- @handler_already_run = false
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 :remove then return false
213
+ when :stop then return false
113
214
  end
114
215
  }
115
216
  end
116
217
 
117
218
 
118
- def do_change(change)
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 ObservedObject < ManagedObject
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 ExposedObject < ManagedObject
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 ObservedObject < ManagedObject
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 ExposedObject < ManagedObject
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
@@ -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.join
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
- class ObjectExpose
25
- def thread(&block)
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 TagObserve
32
- def thread(&block)
33
- ThreadObjectManager.new(self, ObservedObject, block)
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Hakuban
4
- VERSION = "0.6.1"
4
+ VERSION = "0.6.4"
5
5
  end
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.1
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-02-06 00:00:00.000000000 Z
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