hakuban 0.6.2 → 0.6.3

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: 6d0b0d54e3b6dce8ecd11b991e94768247f2e978cca397d430fcab6bfd987484
4
- data.tar.gz: 81466178ba54513cb665cd49800599125bda1ba108f7eee71efff3e5b496cbb5
3
+ metadata.gz: e6822f983334fd24d18385b13f77352026609b85e2f386760b00bae8e60e53cb
4
+ data.tar.gz: e90301ced5b302e23fcc4c3da3d59c9ac32bf9e22d472272ff43bdb8ad78eadc
5
5
  SHA512:
6
- metadata.gz: dc7157372663dc871a85b36ee042e0fc12b66ad45fb322398b593086b47896167ab4cb913ab8b1f636071a9a006d2a3ab3af34b6d32e4024d7f3121dc4d9e19c
7
- data.tar.gz: 6b0b7c59f66be811b16322574f8f0670c5802760b8cf8e690b3d4fa4107ea17dffa1e085c0c9607a72fa0a025eef0665087d5b18b0d671e43bab1cf7b5eea95f
6
+ metadata.gz: 22eb5c5b3223bd25d7b3dea31f52134f9a71a07a1fcdf06e4d6077dc27df689e57a9e92cb059919f283f8708bfbe26e9ed679cdaeee7631d1203eedc4254aa85
7
+ data.tar.gz: e39db8ea279d53883c61333768928aca255891e105dfb27e82e6ddc88f73a1128fe0b9972da59921074479272ed7baf32b0317dc933bebef1664568414469e8c
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
@@ -2,8 +2,6 @@
2
2
 
3
3
  require 'ostruct'
4
4
 
5
- require_relative './ffi.rb'
6
-
7
5
 
8
6
  module Hakuban
9
7
 
@@ -1,7 +1,6 @@
1
1
  require 'set'
2
2
  require 'ostruct'
3
3
 
4
- require_relative './ffi.rb'
5
4
  require_relative './event-queue.rb'
6
5
 
7
6
  #TODO: explicit drops?
@@ -33,9 +32,14 @@ module Hakuban
33
32
  end
34
33
 
35
34
 
35
+ def self.hakuban_initialize
36
+ require_relative './ffi.rb'
37
+ end
38
+
36
39
  @@logger_initialized = false
37
40
 
38
41
  def self.logger_initialize(default_level, skip_if_already_initialized = false)
42
+ ::Hakuban::hakuban_initialize
39
43
  if @@logger_initialized and !skip_if_already_initialized
40
44
  raise "Logger already initialized. This can't be done more than once. Make sure logger_initialize is called before any LocalNode gets constructed."
41
45
  end
@@ -53,6 +57,7 @@ module Hakuban
53
57
  attr_reader :local_node_pointer #todo: hide
54
58
 
55
59
  def initialize(name: nil)
60
+ ::Hakuban::hakuban_initialize
56
61
  @default_serializer = lambda { |data_type, data|
57
62
  [["JSON"]+data_type, JSON.dump(data)]
58
63
  }
@@ -6,17 +6,55 @@ 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
14
51
 
15
- def initialize(contract, object_class, block)
52
+ def initialize(contract, block, retry_backoff, terminate_on_deactivation, on_exception_policy)
16
53
  @contract = contract
54
+ object_class = @contract.class.const_get(:ManagedObject)
17
55
  @objects_mutex = Mutex.new
18
- @objects = {}
19
- @running_handlers = {}
56
+ @existing_objects = {} #TODO: turn this into a WeakMap, to keep object if external code still holds reference to it
57
+ @active_objects = {}
20
58
  @event_queue = Queue.new
21
59
 
22
60
  # This callback gets called from a separate thread, with no async reactor running.
@@ -31,38 +69,82 @@ module Hakuban
31
69
  while @event_queue and event = @event_queue.shift
32
70
  @objects_mutex.synchronize {
33
71
  case event.action
34
- when :insert
35
- raise if @objects[event.descriptor]
36
- @objects[event.descriptor] = object_class.new(@contract, event.descriptor)
37
- @event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if block
38
- when :change
39
- raise if not @objects[event.descriptor]
40
- @objects[event.descriptor].do_change(:change)
41
- when :remove
42
- raise if not @objects[event.descriptor]
43
- @objects[event.descriptor].do_change(:remove)
44
- @objects.delete(event.descriptor)
45
- when :handler_start
46
- if (object = @objects[event.descriptor]) and !object.handler_already_run and !@running_handlers[event.descriptor]
47
- descriptor_for_lambda = event.descriptor
48
- @running_handlers[event.descriptor] = async_run {
49
- object.run(block)
50
- @event_queue << Event.new(action: :handler_finished, descriptor: descriptor_for_lambda) if @event_queue
51
- }
72
+ when :insert, :change, :remove, :stopped, :timer
73
+ object = @existing_objects[event.descriptor] ||= object_class.new(@contract, event.descriptor)
74
+ if !object.running and object.async
75
+ error = async_join(object.async)
76
+ object.async = nil
77
+ if object.last_exception_at
78
+ interval_since_last_exception = Time.new - object.last_exception_at
79
+ heal_multiplier = 10.0
80
+ object.current_delay_index -= (interval_since_last_exception / (retry_backoff[-1]*heal_multiplier)).floor
81
+ object.current_delay_index = 0 if object.current_delay_index < 0
82
+ end
83
+ if error.kind_of? HandlerException
84
+ case on_exception_policy
85
+ when :raise
86
+ raise error.exception
87
+ when :retry
88
+ object.last_exception_at = Time.new
89
+ object.earliest_next_run = Time.new + retry_backoff[object.current_delay_index]
90
+ lambda_sleep_time = object.earliest_next_run - Time.new
91
+ lambda_descriptor = event.descriptor
92
+ async_run {
93
+ sleep lambda_sleep_time
94
+ @event_queue << Event.new(action: :timer, descriptor: lambda_descriptor) if @event_queue
95
+ } #TODO: do we have to join this?
96
+ object.current_delay_index += 1 if object.current_delay_index < retry_backoff.size - 1
97
+ #when :ignore_failing_descriptor
98
+ #when :drop_contract
99
+ end
100
+ end
101
+ end
102
+ if object.check_active
103
+ if block and !object.running and (!object.earliest_next_run or Time.new >= object.earliest_next_run)
104
+ descriptor_for_lambda = event.descriptor
105
+ object.running = true
106
+ object.async = async_run do
107
+ object.run(block)
108
+ rescue Async::Stop
109
+ nil
110
+ rescue Exception => e
111
+ $stderr.puts "Exception in hakuban manager lambda: #{e}"
112
+ $stderr.puts "Contract: "+@contract.inspect
113
+ $stderr.puts "Descriptor: "+descriptor_for_lambda.inspect
114
+ $stderr.puts "Backtrace:\n\t#{e.backtrace.join("\n\t")}"
115
+ HandlerException.new(exception: e) # exception has to wrapped here to stop async from throwing on join
116
+ ensure
117
+ object.running = false
118
+ @event_queue << Event.new(action: :stopped, descriptor: descriptor_for_lambda) if @event_queue
119
+ end
120
+ end
121
+ @active_objects[event.descriptor] ||= object
122
+ object.change
123
+ else
124
+ @active_objects.delete(event.descriptor)
125
+ if object.running
126
+ object.stop
127
+ async_stop(object.async) if terminate_on_deactivation
128
+ else
129
+ @existing_objects.delete(event.descriptor)
130
+ end
52
131
  end
53
- when :handler_finished
54
- raise if not @running_handlers[event.descriptor]
55
- @running_handlers.delete(event.descriptor)
56
- @event_queue << Event.new(action: :handler_start, descriptor: event.descriptor) if @objects[event.descriptor]
57
132
  when :drop
58
133
  @ffi_events.callback_unregister
59
- @objects.values.each { |object| object.do_change(:remove) }
60
- while @running_handlers.size > 0
134
+ @active_objects.clear
135
+ @existing_objects.dup.each { |descriptor, object|
136
+ if object.running
137
+ object.stop
138
+ async_stop(object.async) if terminate_on_deactivation
139
+ else
140
+ @existing_objects.delete(descriptor)
141
+ end
142
+ }
143
+ while @existing_objects.size > 0
61
144
  event = @event_queue.shift
62
- @running_handlers.delete(event.descriptor) if event.action == :handler_finished
145
+ @existing_objects.delete(event.descriptor) if event.action == :stopped
63
146
  end
64
147
  @event_queue.clear
65
- @objects.clear
66
148
  @contract.drop
67
149
  @contract, @event_queue = nil, nil
68
150
  break
@@ -75,14 +157,14 @@ module Hakuban
75
157
 
76
158
  def objects
77
159
  @objects_mutex.synchronize {
78
- @objects.dup
160
+ @active_objects.dup
79
161
  }
80
162
  end
81
163
 
82
164
 
83
165
  def object
84
166
  @objects_mutex.synchronize {
85
- @objects.values.first
167
+ @active_objects.values.first
86
168
  }
87
169
  end
88
170
 
@@ -102,20 +184,20 @@ module Hakuban
102
184
 
103
185
 
104
186
 
105
- class ManagedObject
187
+ class ManagedObjectBase
106
188
 
107
- attr_reader :descriptor, :async, :handler_already_run
189
+ attr_reader :descriptor
190
+ attr_accessor :running, :async, :last_exception_at, :current_delay_index, :earliest_next_run
108
191
 
109
192
 
110
193
  def initialize(contract, descriptor)
111
194
  @contract,@descriptor = contract, descriptor
112
195
  @changes = Queue.new
113
- @handler_already_run = false
196
+ @current_delay_index = 0
114
197
  end
115
198
 
116
199
 
117
200
  def run(handler)
118
- @handler_already_run = true
119
201
  handler.call(self)
120
202
  end
121
203
 
@@ -125,23 +207,27 @@ module Hakuban
125
207
  loop {
126
208
  case @changes.shift
127
209
  when :change then return true
128
- when :remove then return false
210
+ when :stop then return false
129
211
  end
130
212
  }
131
213
  end
132
214
 
133
215
 
134
- def do_change(change)
135
- @changes.push(change)
216
+ def change
217
+ @changes.push(:change)
136
218
  end
137
219
 
220
+ def stop
221
+ @changes.push(:stop)
222
+ end
223
+
138
224
  end
139
225
 
140
226
 
141
227
 
142
228
  class ObjectObserve
143
229
 
144
- class ObservedObject < ManagedObject
230
+ class ManagedObject < ManagedObjectBase
145
231
 
146
232
  def state
147
233
  @contract.object_state
@@ -151,6 +237,15 @@ module Hakuban
151
237
  @contract.object_state&.data
152
238
  end
153
239
 
240
+ def check_active
241
+ !!state
242
+ end
243
+
244
+ end
245
+
246
+
247
+ def manage
248
+ ObjectManagerBuilder.new(self)
154
249
  end
155
250
 
156
251
  end
@@ -158,7 +253,7 @@ module Hakuban
158
253
 
159
254
  class ObjectExpose
160
255
 
161
- class ExposedObject < ManagedObject
256
+ class ManagedObject < ManagedObjectBase
162
257
 
163
258
  def initialize(contract, descriptor)
164
259
  super(contract, descriptor)
@@ -201,6 +296,15 @@ module Hakuban
201
296
  set_data(value)
202
297
  end
203
298
 
299
+ def check_active
300
+ assigned?
301
+ end
302
+
303
+ end
304
+
305
+
306
+ def manage
307
+ ObjectManagerBuilder.new(self)
204
308
  end
205
309
 
206
310
  end
@@ -208,7 +312,7 @@ module Hakuban
208
312
 
209
313
  class TagObserve
210
314
 
211
- class ObservedObject < ManagedObject
315
+ class ManagedObject < ManagedObjectBase
212
316
 
213
317
  def state
214
318
  @contract.object_state(@descriptor)
@@ -218,6 +322,15 @@ module Hakuban
218
322
  @contract.object_state(@descriptor)&.data
219
323
  end
220
324
 
325
+ def check_active
326
+ !!state
327
+ end
328
+
329
+ end
330
+
331
+
332
+ def manage
333
+ ObjectManagerBuilder.new(self)
221
334
  end
222
335
 
223
336
  end
@@ -225,7 +338,7 @@ module Hakuban
225
338
 
226
339
  class TagExpose
227
340
 
228
- class ExposedObject < ManagedObject
341
+ class ManagedObject < ManagedObjectBase
229
342
 
230
343
  def initialize(contract, descriptor)
231
344
  super(contract, descriptor)
@@ -268,6 +381,15 @@ module Hakuban
268
381
  set_data(value)
269
382
  end
270
383
 
384
+ def check_active
385
+ assigned?
386
+ end
387
+
388
+ end
389
+
390
+
391
+ def manage
392
+ ObjectManagerBuilder.new(self)
271
393
  end
272
394
 
273
395
  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.2"
4
+ VERSION = "0.6.3"
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.2
4
+ version: 0.6.3
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-13 00:00:00.000000000 Z
11
+ date: 2022-03-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec