hakuban 0.6.1 → 0.6.4

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