hakuban 0.5.0 → 0.6.0
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/MIT-LICENSE +20 -0
- data/bin/hakuban-observer +56 -0
- data/bin/hakuban-thread-engine +45 -0
- data/lib/hakuban/async.rb +45 -0
- data/lib/hakuban/engine.rb +44 -0
- data/lib/hakuban/ffi.rb +47 -25
- data/lib/hakuban/hakuban.rb +208 -233
- data/lib/hakuban/manager.rb +259 -0
- data/lib/hakuban/thread.rb +44 -0
- data/lib/hakuban/version.rb +1 -1
- data/lib/hakuban.rb +3 -0
- metadata +21 -22
- data/.gitignore +0 -12
- data/.rspec +0 -3
- data/Gemfile +0 -6
- data/LICENSE.txt +0 -21
- data/bin/console +0 -15
- data/bin/setup +0 -8
- data/examples/all-top-managed.rb +0 -46
- data/examples/all-top-simple.rb +0 -26
- data/examples/cpu-usage.rb +0 -16
- data/hakuban.gemspec +0 -36
data/lib/hakuban/hakuban.rb
CHANGED
@@ -1,9 +1,13 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'ostruct'
|
2
3
|
|
3
4
|
require_relative './ffi.rb'
|
4
5
|
|
5
6
|
#TODO: explicit drops?
|
6
7
|
#TODO: privative methods
|
8
|
+
#TODO: error classes
|
9
|
+
|
10
|
+
#AutoPointers use proc-wrapped calls because they segfault sometimes when used with FFI::method(:xxx) (why?)
|
7
11
|
|
8
12
|
module Hakuban
|
9
13
|
|
@@ -28,26 +32,73 @@ module Hakuban
|
|
28
32
|
end
|
29
33
|
|
30
34
|
|
35
|
+
@@logger_initialized = false
|
36
|
+
|
37
|
+
def self.logger_initialize(default_level, skip_if_already_initialized = false)
|
38
|
+
if @@logger_initialized and !skip_if_already_initialized
|
39
|
+
raise "Logger already initialized. This can't be done more than once. Make sure logger_initialize is called before any LocalNode gets constructed."
|
40
|
+
end
|
41
|
+
if not @@logger_initialized
|
42
|
+
raise "Invalid default log level string" if FFI::hakuban_logger_initialize(default_level) != 0
|
43
|
+
@@logger_initialized = true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
|
31
48
|
class LocalNode
|
32
49
|
|
33
50
|
#TODO: explicit drop
|
34
51
|
|
35
52
|
attr_reader :local_node_pointer #todo: hide
|
36
53
|
|
37
|
-
def initialize(name
|
38
|
-
|
54
|
+
def initialize(name: nil)
|
55
|
+
@default_serializer = lambda { |data_type, data|
|
56
|
+
[["JSON"]+data_type, JSON.dump(data)]
|
57
|
+
}
|
58
|
+
@default_deserializer = lambda { |data_type, data|
|
59
|
+
raise "Expected JSON serialized data, got: #{dat_type}" if data_type[0] != "JSON"
|
60
|
+
[data_type[1..-1], JSON.load(data)]
|
61
|
+
}
|
62
|
+
Hakuban::logger_initialize("hakuban=warn", true)
|
63
|
+
result = FFI::hakuban_local_node_new(name || File.basename(caller_locations(0..1)[1].path))
|
39
64
|
Hakuban::raise_if_error(result)
|
40
65
|
@local_node_pointer = ::FFI::AutoPointer.new(result[:local_node_pointer], FFI::method(:hakuban_local_node_drop))
|
41
66
|
end
|
42
67
|
|
68
|
+
def with_default_serializer(&block)
|
69
|
+
@default_serializer = block
|
70
|
+
self
|
71
|
+
end
|
72
|
+
|
73
|
+
def with_default_deserializer(&block)
|
74
|
+
@default_deserializer = block
|
75
|
+
self
|
76
|
+
end
|
77
|
+
|
78
|
+
def default_serializer(&block)
|
79
|
+
if block_given?
|
80
|
+
@default_serializer = block
|
81
|
+
else
|
82
|
+
@default_serializer
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def default_deserializer(&block)
|
87
|
+
if block_given?
|
88
|
+
@default_deserializer = block
|
89
|
+
else
|
90
|
+
@default_deserializer
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
43
94
|
def object(tags, descriptor)
|
44
95
|
#TODO: accept real descriptor too
|
45
|
-
ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor))
|
96
|
+
ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor), @default_serializer, @default_deserializer)
|
46
97
|
end
|
47
98
|
|
48
99
|
def tag(descriptor)
|
49
100
|
#TODO: accept real descriptor too
|
50
|
-
TagBuilder.new(self, TagDescriptor.new(descriptor))
|
101
|
+
TagBuilder.new(self, TagDescriptor.new(descriptor), @default_serializer, @default_deserializer)
|
51
102
|
end
|
52
103
|
|
53
104
|
end
|
@@ -55,16 +106,24 @@ module Hakuban
|
|
55
106
|
|
56
107
|
class ObjectBuilder
|
57
108
|
|
58
|
-
def initialize(store, descriptor)
|
59
|
-
@store, @descriptor = store, descriptor
|
109
|
+
def initialize(store, descriptor, serializer, deserializer)
|
110
|
+
@store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
|
60
111
|
end
|
61
112
|
|
62
113
|
def observe
|
63
|
-
ObjectObserve.new(@store, @descriptor)
|
114
|
+
ObjectObserve.new(@store, @descriptor, @deserializer)
|
64
115
|
end
|
65
116
|
|
66
117
|
def expose
|
67
|
-
ObjectExpose.new(@store, @descriptor)
|
118
|
+
ObjectExpose.new(@store, @descriptor, @serializer)
|
119
|
+
end
|
120
|
+
|
121
|
+
def with_serializer(&block)
|
122
|
+
@serializer = block
|
123
|
+
end
|
124
|
+
|
125
|
+
def with_deserializer(&block)
|
126
|
+
@deserializer = block
|
68
127
|
end
|
69
128
|
|
70
129
|
end
|
@@ -72,16 +131,24 @@ module Hakuban
|
|
72
131
|
|
73
132
|
class TagBuilder
|
74
133
|
|
75
|
-
def initialize(store, descriptor)
|
76
|
-
@store, @descriptor = store, descriptor
|
134
|
+
def initialize(store, descriptor, serializer, deserializer)
|
135
|
+
@store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
|
77
136
|
end
|
78
137
|
|
79
138
|
def observe
|
80
|
-
TagObserve.new(@store, @descriptor)
|
139
|
+
TagObserve.new(@store, @descriptor, @deserializer)
|
81
140
|
end
|
82
141
|
|
83
142
|
def expose
|
84
|
-
TagExpose.new(@store, @descriptor)
|
143
|
+
TagExpose.new(@store, @descriptor, @serializer)
|
144
|
+
end
|
145
|
+
|
146
|
+
def with_serializer(&block)
|
147
|
+
@serializer = block
|
148
|
+
end
|
149
|
+
|
150
|
+
def with_deserializer(&block)
|
151
|
+
@deserializer = block
|
85
152
|
end
|
86
153
|
|
87
154
|
end
|
@@ -91,15 +158,16 @@ module Hakuban
|
|
91
158
|
attr_reader :tags, :json
|
92
159
|
|
93
160
|
def initialize(tags,json)
|
94
|
-
@tags
|
161
|
+
@tags = Set.new(tags.map { |tag| tag.kind_of?(TagDescriptor) ? tag : TagDescriptor.new(tag) })
|
162
|
+
@json = json
|
95
163
|
end
|
96
164
|
|
97
165
|
def to_ffi
|
98
|
-
FFI::FFIObjectDescriptor.construct(@tags, @json)
|
166
|
+
FFI::FFIObjectDescriptor.construct(@tags.map(&:json), @json)
|
99
167
|
end
|
100
168
|
|
101
169
|
def self.from_ffi(ffi)
|
102
|
-
ObjectDescriptor.new(ffi.tags, ffi.json)
|
170
|
+
ObjectDescriptor.new(ffi.tags.map{ |tag| TagDescriptor.new(tag) }, ffi.json)
|
103
171
|
end
|
104
172
|
|
105
173
|
def ==(other)
|
@@ -111,6 +179,11 @@ module Hakuban
|
|
111
179
|
def hash
|
112
180
|
[@tags.hash, @json.hash].hash
|
113
181
|
end
|
182
|
+
|
183
|
+
def inspect
|
184
|
+
"#<ObjectDescriptor @tags={%s}, @json=%p>"%[self.tags.map(&:inspect).join(","), self.json]
|
185
|
+
end
|
186
|
+
|
114
187
|
end
|
115
188
|
|
116
189
|
|
@@ -142,68 +215,59 @@ module Hakuban
|
|
142
215
|
end
|
143
216
|
|
144
217
|
|
145
|
-
class
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
@callback = EventQueue.generate_callback(@queue, prefix)
|
150
|
-
@registered_callback_pointer = ::FFI::AutoPointer.new(
|
151
|
-
register_method.call(pointer, @callback, ::FFI::Pointer::NULL),
|
152
|
-
EventQueue.generate_unregister(unregister_method, @callback)
|
153
|
-
)
|
154
|
-
#puts "Allocated callback pointer: #{@registered_callback_pointer}"
|
155
|
-
#puts "Allocated callback queue: #{@events}"
|
156
|
-
end
|
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
|
157
222
|
|
158
|
-
|
159
|
-
|
160
|
-
|
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|
|
161
226
|
action = Hakuban::action_int_to_symbol(ffi_action)
|
162
227
|
descriptor = ObjectDescriptor.from_ffi(ffi_descriptor)
|
163
|
-
|
228
|
+
callback.call(descriptor, action)
|
164
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
|
+
)
|
165
234
|
end
|
166
235
|
|
167
|
-
def
|
168
|
-
|
169
|
-
|
170
|
-
callback # probably not needed, but I want to be sure this is captured
|
171
|
-
unregister_method.call(pointer)
|
172
|
-
}
|
236
|
+
def callback_unregister
|
237
|
+
@callback_pointer.free
|
238
|
+
@callback_pointer = nil
|
173
239
|
end
|
174
240
|
|
175
|
-
|
241
|
+
def drop
|
242
|
+
@events_pointer.free
|
243
|
+
@events_pointer = nil
|
244
|
+
end
|
176
245
|
|
246
|
+
end
|
177
247
|
|
178
248
|
class ObjectObserve
|
179
249
|
|
180
250
|
attr_reader :descriptor
|
181
251
|
|
182
|
-
def initialize(local_node, descriptor)
|
183
|
-
@local_node, @descriptor = local_node, descriptor
|
252
|
+
def initialize(local_node, descriptor, deserializer)
|
253
|
+
@local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
|
184
254
|
result = FFI::hakuban_object_observe_new(@local_node.local_node_pointer, descriptor.to_ffi)
|
185
255
|
Hakuban::raise_if_error(result)
|
186
|
-
@object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], FFI::
|
187
|
-
@queues = {}
|
256
|
+
@object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], proc { |ptr| FFI::hakuban_object_observe_drop(ptr) })
|
188
257
|
end
|
189
258
|
|
190
259
|
def object_state
|
191
260
|
raise "Attempt to use after 'drop'" if not @object_observe_pointer
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
queue,
|
198
|
-
prefix,
|
199
|
-
FFI::method(:hakuban_object_observe_changes_callback_register),
|
200
|
-
@object_observe_pointer,
|
201
|
-
FFI::method(:hakuban_object_callback_unregister)
|
202
|
-
)
|
261
|
+
if (state_ptr = FFI::hakuban_object_observe_state_borrow(@object_observe_pointer)).null?
|
262
|
+
nil
|
263
|
+
else
|
264
|
+
ObjectObserveState.new(state_ptr, @deserializer)
|
265
|
+
end
|
203
266
|
end
|
204
267
|
|
205
|
-
def
|
206
|
-
@
|
268
|
+
def events
|
269
|
+
raise "Attempt to use after 'drop'" if not @object_observe_pointer
|
270
|
+
ObjectDescriptorEvents.new(FFI::hakuban_object_observe_events_get(@object_observe_pointer))
|
207
271
|
end
|
208
272
|
|
209
273
|
def inspect
|
@@ -213,12 +277,10 @@ module Hakuban
|
|
213
277
|
def drop
|
214
278
|
@object_observe_pointer.free
|
215
279
|
@object_observe_pointer = nil
|
216
|
-
@queues.values.each { |pointer| pointer.free }
|
217
|
-
@queues.clear
|
218
280
|
end
|
219
281
|
|
220
|
-
def
|
221
|
-
|
282
|
+
def dropped?
|
283
|
+
@object_observe_pointer.nil?
|
222
284
|
end
|
223
285
|
|
224
286
|
end
|
@@ -228,51 +290,51 @@ module Hakuban
|
|
228
290
|
|
229
291
|
attr_reader :descriptor
|
230
292
|
|
231
|
-
def initialize(local_node, descriptor)
|
232
|
-
@local_node, @descriptor = local_node, descriptor
|
293
|
+
def initialize(local_node, descriptor, serializer)
|
294
|
+
@local_node, @descriptor, @serializer = local_node, descriptor, serializer
|
233
295
|
result = FFI::hakuban_object_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
|
234
296
|
Hakuban::raise_if_error(result)
|
235
297
|
@object_expose_pointer = ::FFI::AutoPointer.new(result[:object_expose_pointer], FFI::method(:hakuban_object_expose_drop))
|
236
|
-
@queues = {}
|
237
298
|
end
|
238
299
|
|
239
|
-
def set_object_state(version, data_type,
|
300
|
+
def set_object_state(version, data, data_type: [], assignment: 0)
|
240
301
|
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
241
|
-
|
242
|
-
|
243
|
-
result = FFI::hakuban_object_expose_state(@object_expose_pointer, FFI::FFIObjectExposeState.construct(version, data_type, serialized))
|
302
|
+
serialized_data_type, serialized_data = @serializer.call(data_type, data)
|
303
|
+
result = FFI::hakuban_object_expose_state(@object_expose_pointer, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_data), assignment)
|
244
304
|
Hakuban::raise_if_error(result)
|
245
305
|
result[:changed] == 1
|
246
306
|
end
|
247
307
|
|
248
|
-
def
|
308
|
+
def assignment
|
249
309
|
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
250
|
-
FFI::
|
310
|
+
FFI::hakuban_object_expose_assignment(@object_expose_pointer)
|
251
311
|
end
|
252
312
|
|
253
|
-
def
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
)
|
313
|
+
def assigned?
|
314
|
+
assignment > 0
|
315
|
+
end
|
316
|
+
|
317
|
+
def desynchronize(assignment)
|
318
|
+
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
319
|
+
FFI::hakuban_object_expose_desynchronize(@object_expose_pointer, assignment)
|
261
320
|
end
|
262
321
|
|
263
|
-
def
|
264
|
-
@
|
322
|
+
def events
|
323
|
+
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
324
|
+
ObjectDescriptorEvents.new(FFI::hakuban_object_expose_events_get(@object_expose_pointer))
|
325
|
+
end
|
326
|
+
|
327
|
+
def inspect
|
328
|
+
"#<ObjectExpose #{@descriptor}>"
|
265
329
|
end
|
266
330
|
|
267
331
|
def drop
|
268
332
|
@object_expose_pointer.free
|
269
333
|
@object_expose_pointer = nil
|
270
|
-
@queues.values.each { |pointer| pointer.free }
|
271
|
-
@queues.clear
|
272
334
|
end
|
273
335
|
|
274
|
-
def
|
275
|
-
|
336
|
+
def dropped?
|
337
|
+
@object_expose_pointer.nil?
|
276
338
|
end
|
277
339
|
|
278
340
|
end
|
@@ -280,29 +342,45 @@ module Hakuban
|
|
280
342
|
|
281
343
|
class ObjectObserveState
|
282
344
|
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
@raw_state = raw_state
|
287
|
-
@synchronized = (raw_state[:synchronized] == 1)
|
288
|
-
@data_type = raw_state[:data_type].read_array_of_pointer(raw_state[:data_type_length]).map { |string| string.read_string() }
|
289
|
-
@data_version = raw_state[:version].read_array_of_type(::FFI::TYPE_INT64, :read_int64, raw_state[:version_length]) if not raw_state[:version].null?
|
290
|
-
@data = MessagePack.unpack(raw_state[:raw].read_string_length(raw_state[:raw_length])) if not raw_state[:raw].null?
|
291
|
-
ObjectSpace.define_finalizer(self, ObjectObserveState.finalize(@raw_state))
|
345
|
+
def initialize(state, deserializer)
|
346
|
+
@state, @deserializer = state, deserializer
|
347
|
+
ObjectSpace.define_finalizer(self, ObjectObserveState.finalize(@state))
|
292
348
|
end
|
293
349
|
|
294
|
-
def ObjectObserveState.finalize(
|
350
|
+
def ObjectObserveState.finalize(state)
|
295
351
|
proc { |_|
|
296
|
-
FFI::hakuban_object_observe_state_return(
|
352
|
+
FFI::hakuban_object_observe_state_return(state)
|
297
353
|
}
|
298
354
|
end
|
299
355
|
|
300
356
|
def version
|
301
|
-
@data_version
|
357
|
+
return @data_version if @data_version
|
358
|
+
ffi_version = FFI::hakuban_object_observe_state_get_data_version(@state)
|
359
|
+
@data_version = ffi_version[:version_elements].read_array_of_type(::FFI::TYPE_INT64, :read_int64, ffi_version[:version_length])
|
360
|
+
end
|
361
|
+
|
362
|
+
def data
|
363
|
+
return @data if @data
|
364
|
+
ffi_data = FFI::hakuban_object_observe_state_get_data(@state)
|
365
|
+
ffi_data_type = FFI::hakuban_object_observe_state_get_data_type(@state)
|
366
|
+
serialized_data_type = ffi_data_type[:type_elements].read_array_of_pointer(ffi_data_type[:type_length]).map { |string| string.read_string() }
|
367
|
+
@data_type, @data = @deserializer.call(serialized_data_type, ffi_data[:data_bytes].read_string_length(ffi_data[:data_length]))
|
368
|
+
@data
|
369
|
+
end
|
370
|
+
|
371
|
+
def data_type
|
372
|
+
return @data_type if @data_type
|
373
|
+
data
|
374
|
+
@data_type
|
375
|
+
end
|
376
|
+
|
377
|
+
def synchronized
|
378
|
+
return @synchronized if @synchronized
|
379
|
+
@synchronized = FFI::hakuban_object_observe_state_get_synchronized(@state)
|
302
380
|
end
|
303
381
|
|
304
382
|
def inspect
|
305
|
-
"#<ObjectObserveState version
|
383
|
+
"#<ObjectObserveState @synchronized=%p @version=%p>"%[@synchronized, @data_version]
|
306
384
|
end
|
307
385
|
|
308
386
|
end
|
@@ -312,12 +390,11 @@ module Hakuban
|
|
312
390
|
|
313
391
|
attr_reader :descriptor
|
314
392
|
|
315
|
-
def initialize(local_node, descriptor)
|
316
|
-
@local_node, @descriptor = local_node, descriptor
|
393
|
+
def initialize(local_node, descriptor, deserializer)
|
394
|
+
@local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
|
317
395
|
result = FFI::hakuban_tag_observe_new(@local_node.local_node_pointer, @descriptor.to_ffi)
|
318
396
|
Hakuban::raise_if_error(result)
|
319
397
|
@tag_observe_pointer = ::FFI::AutoPointer.new(result[:tag_observe_pointer], FFI::method(:hakuban_tag_observe_drop))
|
320
|
-
@queues = {}
|
321
398
|
end
|
322
399
|
|
323
400
|
def object_descriptors
|
@@ -333,36 +410,21 @@ module Hakuban
|
|
333
410
|
result = FFI::hakuban_tag_observe_object_state_borrow(@tag_observe_pointer, object_descriptor.to_ffi)
|
334
411
|
return nil if result[:error] == 4
|
335
412
|
Hakuban::raise_if_error(result)
|
336
|
-
ObjectObserveState.new(result[:state])
|
413
|
+
ObjectObserveState.new(result[:state], @deserializer)
|
337
414
|
end
|
338
415
|
|
339
|
-
def
|
340
|
-
|
341
|
-
|
342
|
-
|
343
|
-
def send_objects_events_to(queue, *prefix)
|
344
|
-
@queues[queue] = EventQueue.new(
|
345
|
-
queue,
|
346
|
-
prefix,
|
347
|
-
FFI::method(:hakuban_tag_observe_objects_changes_callback_register),
|
348
|
-
@tag_observe_pointer,
|
349
|
-
FFI::method(:hakuban_object_callback_unregister)
|
350
|
-
)
|
351
|
-
end
|
352
|
-
|
353
|
-
def stop_sending_objects_events_to(queue)
|
354
|
-
@queues.delete(queue)
|
416
|
+
def events
|
417
|
+
raise "Attempt to use after 'drop'" if not @tag_observe_pointer
|
418
|
+
ObjectDescriptorEvents.new(FFI::hakuban_tag_observe_events_get(@tag_observe_pointer))
|
355
419
|
end
|
356
420
|
|
357
421
|
def drop
|
358
422
|
@tag_observe_pointer.free
|
359
423
|
@tag_observe_pointer = nil
|
360
|
-
@queues.values.each { |pointer| pointer.free }
|
361
|
-
@queues.clear
|
362
424
|
end
|
363
425
|
|
364
|
-
def
|
365
|
-
|
426
|
+
def dropped?
|
427
|
+
@tag_observe_pointer.nil?
|
366
428
|
end
|
367
429
|
|
368
430
|
end
|
@@ -372,12 +434,11 @@ module Hakuban
|
|
372
434
|
|
373
435
|
attr_reader :descriptor
|
374
436
|
|
375
|
-
def initialize(local_node, descriptor)
|
376
|
-
@local_node, @descriptor = local_node, descriptor
|
437
|
+
def initialize(local_node, descriptor, serializer)
|
438
|
+
@local_node, @descriptor, @serializer = local_node, descriptor, serializer
|
377
439
|
result = FFI::hakuban_tag_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
|
378
440
|
Hakuban::raise_if_error(result)
|
379
441
|
@tag_expose_pointer = ::FFI::AutoPointer.new(result[:tag_expose_pointer], FFI::method(:hakuban_tag_expose_drop))
|
380
|
-
@queues = {}
|
381
442
|
end
|
382
443
|
|
383
444
|
def object_descriptors
|
@@ -388,42 +449,41 @@ module Hakuban
|
|
388
449
|
ret
|
389
450
|
end
|
390
451
|
|
391
|
-
def set_object_state(object_descriptor, version, data_type,
|
452
|
+
def set_object_state(object_descriptor, version, data, data_type: [], assignment: 0)
|
392
453
|
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
393
|
-
|
394
|
-
|
395
|
-
result = FFI::hakuban_tag_expose_object_state(@tag_expose_pointer, object_descriptor.to_ffi, FFI::FFIObjectExposeState.construct(version,
|
454
|
+
raise if not version #TODO: replace this by type check
|
455
|
+
serialized_data_type, serialized_data = @serializer.call(data_type, data)
|
456
|
+
result = FFI::hakuban_tag_expose_object_state(@tag_expose_pointer, object_descriptor.to_ffi, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_data), assignment)
|
396
457
|
Hakuban::raise_if_error(result)
|
397
458
|
result[:changed] == 1
|
398
459
|
end
|
399
460
|
|
400
|
-
def
|
401
|
-
|
461
|
+
def assignment(object_descriptor)
|
462
|
+
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
463
|
+
FFI::hakuban_tag_expose_object_assignment(@tag_expose_pointer, object_descriptor.to_ffi)
|
402
464
|
end
|
403
465
|
|
404
|
-
def
|
405
|
-
|
406
|
-
|
407
|
-
|
408
|
-
|
409
|
-
|
410
|
-
|
411
|
-
)
|
466
|
+
def assigned?(object_descriptor)
|
467
|
+
assignment(object_descriptor) > 0
|
468
|
+
end
|
469
|
+
|
470
|
+
def desynchronize(object_descriptor, assignment)
|
471
|
+
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
472
|
+
FFI::hakuban_tag_expose_object_desynchronize(@tag_expose_pointer, object_descriptor.to_ffi, assignment)
|
412
473
|
end
|
413
474
|
|
414
|
-
def
|
415
|
-
@
|
475
|
+
def events
|
476
|
+
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
477
|
+
ObjectDescriptorEvents.new(FFI::hakuban_tag_expose_events_get(@tag_expose_pointer))
|
416
478
|
end
|
417
479
|
|
418
480
|
def drop
|
419
481
|
@tag_expose_pointer.free
|
420
482
|
@tag_expose_pointer = nil
|
421
|
-
@queues.values.each { |pointer| pointer.free }
|
422
|
-
@queues.clear
|
423
483
|
end
|
424
484
|
|
425
|
-
def
|
426
|
-
|
485
|
+
def dropped?
|
486
|
+
@tag_expose_pointer.nil?
|
427
487
|
end
|
428
488
|
|
429
489
|
end
|
@@ -452,104 +512,19 @@ module Hakuban
|
|
452
512
|
|
453
513
|
#TODO: drop
|
454
514
|
|
455
|
-
def initialize(url)
|
456
|
-
result = FFI::hakuban_tokio_websocket_connector_new(Tokio.pointer, url)
|
515
|
+
def initialize(local_node, url)
|
516
|
+
result = FFI::hakuban_tokio_websocket_connector_new(Tokio.pointer, local_node.local_node_pointer, url)
|
457
517
|
Hakuban::raise_if_error(result)
|
458
|
-
@websocket_connector_pointer = result[:websocket_connector_pointer]
|
518
|
+
@websocket_connector_pointer = ::FFI::AutoPointer.new(result[:websocket_connector_pointer], WebsocketConnector.generate_drop)
|
459
519
|
end
|
460
520
|
|
461
|
-
def
|
462
|
-
|
463
|
-
|
464
|
-
self
|
465
|
-
end
|
466
|
-
|
467
|
-
end
|
468
|
-
|
469
|
-
|
470
|
-
# convenience utils
|
471
|
-
|
472
|
-
class ObjectManager
|
473
|
-
|
474
|
-
attr_reader :objects, :contract
|
475
|
-
|
476
|
-
def initialize(contract, object_class, *object_constructor_params)
|
477
|
-
@contract = contract
|
478
|
-
@objects = {}
|
479
|
-
@queue = Queue.new
|
480
|
-
contract.send_events_to(@queue, :object)
|
481
|
-
@thread = Thread.new {
|
482
|
-
loop {
|
483
|
-
event = @queue.shift
|
484
|
-
case event[0..1]
|
485
|
-
when [:control, :quit]
|
486
|
-
break
|
487
|
-
when [:object, :insert]
|
488
|
-
@objects[event[2]] = object_class.new(@contract, event[2], *object_constructor_params)
|
489
|
-
when [:object, :change]
|
490
|
-
@objects[event[2]].change if @objects[event[2]].respond_to?(:change)
|
491
|
-
when [:object, :remove]
|
492
|
-
raise if not deleted = @objects.delete(event[2])
|
493
|
-
deleted.drop if deleted.respond_to?(:drop)
|
494
|
-
end
|
495
|
-
}
|
521
|
+
def self.generate_drop
|
522
|
+
proc { |pointer|
|
523
|
+
FFI::hakuban_tokio_websocket_connector_drop(Tokio.pointer, pointer)
|
496
524
|
}
|
497
525
|
end
|
498
526
|
|
499
|
-
def drop
|
500
|
-
@contract.stop_sending_objects_events_to(@queue)
|
501
|
-
@queue.push [:control, :quit]
|
502
|
-
@thread.join
|
503
|
-
@queue, @contract, @thread, @objects = nil, nil, nil, {}
|
504
|
-
end
|
505
|
-
|
506
|
-
|
507
|
-
class ManagedObject
|
508
|
-
attr_reader :contract, :descriptor
|
509
|
-
def initialize(contract, descriptor)
|
510
|
-
@contract, @descriptor = contract, descriptor
|
511
|
-
end
|
512
|
-
end
|
513
|
-
|
514
|
-
|
515
|
-
#TODO: fix the arity thing...
|
516
|
-
module ObservedObject
|
517
|
-
def state
|
518
|
-
if @contract.method(:object_state).arity == 1
|
519
|
-
@contract.object_state(@descriptor)
|
520
|
-
else
|
521
|
-
@contract.object_state
|
522
|
-
end
|
523
|
-
end
|
524
|
-
def data
|
525
|
-
if @contract.method(:object_state).arity == 1
|
526
|
-
@contract.object_state(@descriptor)&.data
|
527
|
-
else
|
528
|
-
@contract.object_state&.data
|
529
|
-
end
|
530
|
-
end
|
531
|
-
end
|
532
|
-
|
533
|
-
module ExposedObject
|
534
|
-
def state=(state)
|
535
|
-
if @contract.method(:set_object_state).arity == 4
|
536
|
-
@contract.set_object_state(@descriptor, *state)
|
537
|
-
else
|
538
|
-
@contract.set_object_state(*state)
|
539
|
-
end
|
540
|
-
end
|
541
|
-
def data=(value)
|
542
|
-
timestamp = Time.new.to_f.floor
|
543
|
-
if @contract.method(:set_object_state).arity == 4
|
544
|
-
@contract.set_object_state(@descriptor,[1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],[],value)
|
545
|
-
else
|
546
|
-
@contract.set_object_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],[],value)
|
547
|
-
end
|
548
|
-
end
|
549
|
-
end
|
550
527
|
end
|
551
528
|
|
552
529
|
|
553
|
-
|
554
|
-
|
555
530
|
end
|