hakuban 0.6.2 → 0.6.3

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: 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