hakuban 0.5.2 → 0.6.2
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/event-queue.rb +77 -0
- data/lib/hakuban/ffi.rb +34 -16
- data/lib/hakuban/hakuban.rb +190 -300
- data/lib/hakuban/manager.rb +275 -0
- data/lib/hakuban/thread.rb +44 -0
- data/lib/hakuban/version.rb +1 -1
- data/lib/hakuban.rb +3 -0
- metadata +22 -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,11 +1,14 @@
|
|
1
1
|
require 'set'
|
2
|
+
require 'ostruct'
|
2
3
|
|
3
4
|
require_relative './ffi.rb'
|
5
|
+
require_relative './event-queue.rb'
|
4
6
|
|
5
7
|
#TODO: explicit drops?
|
6
8
|
#TODO: privative methods
|
7
9
|
#TODO: error classes
|
8
10
|
|
11
|
+
#AutoPointers use proc-wrapped calls because they segfault sometimes when used with FFI::method(:xxx) (why?)
|
9
12
|
|
10
13
|
module Hakuban
|
11
14
|
|
@@ -49,21 +52,54 @@ module Hakuban
|
|
49
52
|
|
50
53
|
attr_reader :local_node_pointer #todo: hide
|
51
54
|
|
52
|
-
def initialize(name
|
55
|
+
def initialize(name: nil)
|
56
|
+
@default_serializer = lambda { |data_type, data|
|
57
|
+
[["JSON"]+data_type, JSON.dump(data)]
|
58
|
+
}
|
59
|
+
@default_deserializer = lambda { |data_type, data|
|
60
|
+
raise "Expected JSON serialized data, got: #{data_type}" if data_type[0] != "JSON"
|
61
|
+
[data_type[1..-1], JSON.load(data)]
|
62
|
+
}
|
53
63
|
Hakuban::logger_initialize("hakuban=warn", true)
|
54
64
|
result = FFI::hakuban_local_node_new(name || File.basename(caller_locations(0..1)[1].path))
|
55
65
|
Hakuban::raise_if_error(result)
|
56
66
|
@local_node_pointer = ::FFI::AutoPointer.new(result[:local_node_pointer], FFI::method(:hakuban_local_node_drop))
|
57
67
|
end
|
58
68
|
|
69
|
+
def with_default_serializer(&block)
|
70
|
+
@default_serializer = block
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
def with_default_deserializer(&block)
|
75
|
+
@default_deserializer = block
|
76
|
+
self
|
77
|
+
end
|
78
|
+
|
79
|
+
def default_serializer(&block)
|
80
|
+
if block_given?
|
81
|
+
@default_serializer = block
|
82
|
+
else
|
83
|
+
@default_serializer
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def default_deserializer(&block)
|
88
|
+
if block_given?
|
89
|
+
@default_deserializer = block
|
90
|
+
else
|
91
|
+
@default_deserializer
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
59
95
|
def object(tags, descriptor)
|
60
96
|
#TODO: accept real descriptor too
|
61
|
-
ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor))
|
97
|
+
ObjectBuilder.new(self, ObjectDescriptor.new(tags,descriptor), @default_serializer, @default_deserializer)
|
62
98
|
end
|
63
99
|
|
64
100
|
def tag(descriptor)
|
65
101
|
#TODO: accept real descriptor too
|
66
|
-
TagBuilder.new(self, TagDescriptor.new(descriptor))
|
102
|
+
TagBuilder.new(self, TagDescriptor.new(descriptor), @default_serializer, @default_deserializer)
|
67
103
|
end
|
68
104
|
|
69
105
|
end
|
@@ -71,16 +107,24 @@ module Hakuban
|
|
71
107
|
|
72
108
|
class ObjectBuilder
|
73
109
|
|
74
|
-
def initialize(store, descriptor)
|
75
|
-
@store, @descriptor = store, descriptor
|
110
|
+
def initialize(store, descriptor, serializer, deserializer)
|
111
|
+
@store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
|
76
112
|
end
|
77
113
|
|
78
114
|
def observe
|
79
|
-
ObjectObserve.new(@store, @descriptor)
|
115
|
+
ObjectObserve.new(@store, @descriptor, @deserializer)
|
80
116
|
end
|
81
117
|
|
82
118
|
def expose
|
83
|
-
ObjectExpose.new(@store, @descriptor)
|
119
|
+
ObjectExpose.new(@store, @descriptor, @serializer)
|
120
|
+
end
|
121
|
+
|
122
|
+
def with_serializer(&block)
|
123
|
+
@serializer = block
|
124
|
+
end
|
125
|
+
|
126
|
+
def with_deserializer(&block)
|
127
|
+
@deserializer = block
|
84
128
|
end
|
85
129
|
|
86
130
|
end
|
@@ -88,16 +132,24 @@ module Hakuban
|
|
88
132
|
|
89
133
|
class TagBuilder
|
90
134
|
|
91
|
-
def initialize(store, descriptor)
|
92
|
-
@store, @descriptor = store, descriptor
|
135
|
+
def initialize(store, descriptor, serializer, deserializer)
|
136
|
+
@store, @descriptor, @serializer, @deserializer = store, descriptor, serializer, deserializer
|
93
137
|
end
|
94
138
|
|
95
139
|
def observe
|
96
|
-
TagObserve.new(@store, @descriptor)
|
140
|
+
TagObserve.new(@store, @descriptor, @deserializer)
|
97
141
|
end
|
98
142
|
|
99
143
|
def expose
|
100
|
-
TagExpose.new(@store, @descriptor)
|
144
|
+
TagExpose.new(@store, @descriptor, @serializer)
|
145
|
+
end
|
146
|
+
|
147
|
+
def with_serializer(&block)
|
148
|
+
@serializer = block
|
149
|
+
end
|
150
|
+
|
151
|
+
def with_deserializer(&block)
|
152
|
+
@deserializer = block
|
101
153
|
end
|
102
154
|
|
103
155
|
end
|
@@ -107,15 +159,16 @@ module Hakuban
|
|
107
159
|
attr_reader :tags, :json
|
108
160
|
|
109
161
|
def initialize(tags,json)
|
110
|
-
@tags
|
162
|
+
@tags = Set.new(tags.map { |tag| tag.kind_of?(TagDescriptor) ? tag : TagDescriptor.new(tag) })
|
163
|
+
@json = json
|
111
164
|
end
|
112
165
|
|
113
166
|
def to_ffi
|
114
|
-
FFI::FFIObjectDescriptor.construct(@tags, @json)
|
167
|
+
FFI::FFIObjectDescriptor.construct(@tags.map(&:json), @json)
|
115
168
|
end
|
116
169
|
|
117
170
|
def self.from_ffi(ffi)
|
118
|
-
ObjectDescriptor.new(ffi.tags, ffi.json)
|
171
|
+
ObjectDescriptor.new(ffi.tags.map{ |tag| TagDescriptor.new(tag) }, ffi.json)
|
119
172
|
end
|
120
173
|
|
121
174
|
def ==(other)
|
@@ -163,68 +216,37 @@ module Hakuban
|
|
163
216
|
end
|
164
217
|
|
165
218
|
|
166
|
-
class EventQueue
|
167
|
-
|
168
|
-
def initialize(queue, prefix, register_method, pointer, unregister_method)
|
169
|
-
@queue = queue
|
170
|
-
@callback = EventQueue.generate_callback(@queue, prefix)
|
171
|
-
@registered_callback_pointer = ::FFI::AutoPointer.new(
|
172
|
-
register_method.call(pointer, @callback, ::FFI::Pointer::NULL),
|
173
|
-
EventQueue.generate_unregister(unregister_method, @callback)
|
174
|
-
)
|
175
|
-
#puts "Allocated callback pointer: #{@registered_callback_pointer}"
|
176
|
-
#puts "Allocated callback queue: #{@events}"
|
177
|
-
end
|
178
|
-
|
179
|
-
def self.generate_callback(queue, prefix)
|
180
|
-
proc { |_userdata, ffi_descriptor, ffi_action|
|
181
|
-
#puts "Callback for queue: #{queue}"
|
182
|
-
action = Hakuban::action_int_to_symbol(ffi_action)
|
183
|
-
descriptor = ObjectDescriptor.from_ffi(ffi_descriptor)
|
184
|
-
queue << prefix + [action, descriptor]
|
185
|
-
}
|
186
|
-
end
|
187
|
-
|
188
|
-
def self.generate_unregister(unregister_method, callback)
|
189
|
-
proc { |pointer|
|
190
|
-
#puts "Dropping callback pointer: #{pointer}"
|
191
|
-
callback # probably not needed, but I want to be sure this is captured
|
192
|
-
unregister_method.call(pointer)
|
193
|
-
}
|
194
|
-
end
|
195
|
-
|
196
|
-
end
|
197
|
-
|
198
|
-
|
199
219
|
class ObjectObserve
|
200
220
|
|
201
221
|
attr_reader :descriptor
|
202
222
|
|
203
|
-
def initialize(local_node, descriptor)
|
204
|
-
@local_node, @descriptor = local_node, descriptor
|
223
|
+
def initialize(local_node, descriptor, deserializer)
|
224
|
+
@local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
|
205
225
|
result = FFI::hakuban_object_observe_new(@local_node.local_node_pointer, descriptor.to_ffi)
|
206
226
|
Hakuban::raise_if_error(result)
|
207
|
-
@
|
208
|
-
@
|
227
|
+
@queues = []
|
228
|
+
@object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], proc { |ptr| FFI::hakuban_object_observe_drop(ptr) })
|
209
229
|
end
|
210
230
|
|
211
231
|
def object_state
|
212
232
|
raise "Attempt to use after 'drop'" if not @object_observe_pointer
|
213
|
-
|
233
|
+
if (state_ptr = FFI::hakuban_object_observe_state_borrow(@object_observe_pointer)).null?
|
234
|
+
nil
|
235
|
+
else
|
236
|
+
ObjectObserveState.new(state_ptr, @deserializer)
|
237
|
+
end
|
214
238
|
end
|
215
239
|
|
216
|
-
def
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
FFI::method(:hakuban_object_observe_changes_callback_register),
|
221
|
-
@object_observe_pointer,
|
222
|
-
FFI::method(:hakuban_object_callback_unregister)
|
223
|
-
)
|
224
|
-
end
|
240
|
+
def new_callback_event_queue
|
241
|
+
raise "Attempt to use after 'drop'" if not @object_observe_pointer
|
242
|
+
ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_object_observe_events_get(@object_observe_pointer))
|
243
|
+
end
|
225
244
|
|
226
|
-
def
|
227
|
-
@
|
245
|
+
def new_event_queue
|
246
|
+
raise "Attempt to use after 'drop'" if not @object_observe_pointer
|
247
|
+
queue = ObjectDescriptorEventQueue.new(self)
|
248
|
+
@queues << queue
|
249
|
+
queue
|
228
250
|
end
|
229
251
|
|
230
252
|
def inspect
|
@@ -234,13 +256,12 @@ module Hakuban
|
|
234
256
|
def drop
|
235
257
|
@object_observe_pointer.free
|
236
258
|
@object_observe_pointer = nil
|
259
|
+
@queues.each(&:close) #is this atomic?
|
237
260
|
@queues.clear
|
238
261
|
end
|
239
262
|
|
240
|
-
def
|
241
|
-
|
242
|
-
manager.contract = self
|
243
|
-
manager
|
263
|
+
def dropped?
|
264
|
+
@object_observe_pointer.nil?
|
244
265
|
end
|
245
266
|
|
246
267
|
end
|
@@ -250,40 +271,46 @@ module Hakuban
|
|
250
271
|
|
251
272
|
attr_reader :descriptor
|
252
273
|
|
253
|
-
def initialize(local_node, descriptor)
|
254
|
-
@local_node, @descriptor = local_node, descriptor
|
274
|
+
def initialize(local_node, descriptor, serializer)
|
275
|
+
@local_node, @descriptor, @serializer = local_node, descriptor, serializer
|
255
276
|
result = FFI::hakuban_object_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
|
256
277
|
Hakuban::raise_if_error(result)
|
278
|
+
@queues = []
|
257
279
|
@object_expose_pointer = ::FFI::AutoPointer.new(result[:object_expose_pointer], FFI::method(:hakuban_object_expose_drop))
|
258
|
-
@queues = {}
|
259
280
|
end
|
260
281
|
|
261
|
-
def set_object_state(version, data_type,
|
282
|
+
def set_object_state(version, data, data_type: [], assignment: 0)
|
262
283
|
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
263
|
-
|
264
|
-
|
265
|
-
result = FFI::hakuban_object_expose_state(@object_expose_pointer, FFI::FFIObjectExposeState.construct(version, data_type, serialized))
|
284
|
+
serialized_data_type, serialized_data = @serializer.call(data_type, data)
|
285
|
+
result = FFI::hakuban_object_expose_state(@object_expose_pointer, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_data), assignment)
|
266
286
|
Hakuban::raise_if_error(result)
|
267
287
|
result[:changed] == 1
|
268
288
|
end
|
269
289
|
|
270
|
-
def
|
290
|
+
def assignment
|
291
|
+
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
292
|
+
FFI::hakuban_object_expose_assignment(@object_expose_pointer)
|
293
|
+
end
|
294
|
+
|
295
|
+
def assigned?
|
296
|
+
assignment > 0
|
297
|
+
end
|
298
|
+
|
299
|
+
def desynchronize(assignment)
|
271
300
|
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
272
|
-
FFI::
|
301
|
+
FFI::hakuban_object_expose_desynchronize(@object_expose_pointer, assignment)
|
273
302
|
end
|
274
303
|
|
275
|
-
def
|
276
|
-
|
277
|
-
|
278
|
-
prefix,
|
279
|
-
FFI::method(:hakuban_object_expose_changes_callback_register),
|
280
|
-
@object_expose_pointer,
|
281
|
-
FFI::method(:hakuban_object_callback_unregister)
|
282
|
-
)
|
304
|
+
def new_callback_event_queue
|
305
|
+
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
306
|
+
ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_object_expose_events_get(@object_expose_pointer))
|
283
307
|
end
|
284
308
|
|
285
|
-
def
|
286
|
-
@
|
309
|
+
def new_event_queue
|
310
|
+
raise "Attempt to use after 'drop'" if not @object_expose_pointer
|
311
|
+
queue = ObjectDescriptorEventQueue.new(self)
|
312
|
+
@queues << queue
|
313
|
+
queue
|
287
314
|
end
|
288
315
|
|
289
316
|
def inspect
|
@@ -293,13 +320,11 @@ module Hakuban
|
|
293
320
|
def drop
|
294
321
|
@object_expose_pointer.free
|
295
322
|
@object_expose_pointer = nil
|
296
|
-
@queues.
|
297
|
-
end
|
323
|
+
@queues.each(&:close) #is this atomic?
|
324
|
+
@queues.clear end
|
298
325
|
|
299
|
-
def
|
300
|
-
|
301
|
-
manager.contract = self
|
302
|
-
manager
|
326
|
+
def dropped?
|
327
|
+
@object_expose_pointer.nil?
|
303
328
|
end
|
304
329
|
|
305
330
|
end
|
@@ -307,25 +332,41 @@ module Hakuban
|
|
307
332
|
|
308
333
|
class ObjectObserveState
|
309
334
|
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
@raw_state = raw_state
|
314
|
-
@synchronized = (raw_state[:synchronized] == 1)
|
315
|
-
@data_type = raw_state[:data_type].read_array_of_pointer(raw_state[:data_type_length]).map { |string| string.read_string() }
|
316
|
-
@data_version = raw_state[:version].read_array_of_type(::FFI::TYPE_INT64, :read_int64, raw_state[:version_length]) if not raw_state[:version].null?
|
317
|
-
@data = MessagePack.unpack(raw_state[:raw].read_string_length(raw_state[:raw_length])) if not raw_state[:raw].null?
|
318
|
-
ObjectSpace.define_finalizer(self, ObjectObserveState.finalize(@raw_state))
|
335
|
+
def initialize(state, deserializer)
|
336
|
+
@state, @deserializer = state, deserializer
|
337
|
+
ObjectSpace.define_finalizer(self, ObjectObserveState.finalize(@state))
|
319
338
|
end
|
320
339
|
|
321
|
-
def ObjectObserveState.finalize(
|
340
|
+
def ObjectObserveState.finalize(state)
|
322
341
|
proc { |_|
|
323
|
-
FFI::hakuban_object_observe_state_return(
|
342
|
+
FFI::hakuban_object_observe_state_return(state)
|
324
343
|
}
|
325
344
|
end
|
326
345
|
|
327
346
|
def version
|
328
|
-
@data_version
|
347
|
+
return @data_version if @data_version
|
348
|
+
ffi_version = FFI::hakuban_object_observe_state_get_data_version(@state)
|
349
|
+
@data_version = ffi_version[:version_elements].read_array_of_type(::FFI::TYPE_INT64, :read_int64, ffi_version[:version_length])
|
350
|
+
end
|
351
|
+
|
352
|
+
def data
|
353
|
+
return @data if @data
|
354
|
+
ffi_data = FFI::hakuban_object_observe_state_get_data(@state)
|
355
|
+
ffi_data_type = FFI::hakuban_object_observe_state_get_data_type(@state)
|
356
|
+
serialized_data_type = ffi_data_type[:type_elements].read_array_of_pointer(ffi_data_type[:type_length]).map { |string| string.read_string() }
|
357
|
+
@data_type, @data = @deserializer.call(serialized_data_type, ffi_data[:data_bytes].read_string_length(ffi_data[:data_length]))
|
358
|
+
@data
|
359
|
+
end
|
360
|
+
|
361
|
+
def data_type
|
362
|
+
return @data_type if @data_type
|
363
|
+
data
|
364
|
+
@data_type
|
365
|
+
end
|
366
|
+
|
367
|
+
def synchronized
|
368
|
+
return @synchronized if @synchronized
|
369
|
+
@synchronized = FFI::hakuban_object_observe_state_get_synchronized(@state)
|
329
370
|
end
|
330
371
|
|
331
372
|
def inspect
|
@@ -339,12 +380,12 @@ module Hakuban
|
|
339
380
|
|
340
381
|
attr_reader :descriptor
|
341
382
|
|
342
|
-
def initialize(local_node, descriptor)
|
343
|
-
@local_node, @descriptor = local_node, descriptor
|
383
|
+
def initialize(local_node, descriptor, deserializer)
|
384
|
+
@local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
|
344
385
|
result = FFI::hakuban_tag_observe_new(@local_node.local_node_pointer, @descriptor.to_ffi)
|
345
386
|
Hakuban::raise_if_error(result)
|
387
|
+
@queues = []
|
346
388
|
@tag_observe_pointer = ::FFI::AutoPointer.new(result[:tag_observe_pointer], FFI::method(:hakuban_tag_observe_drop))
|
347
|
-
@queues = {}
|
348
389
|
end
|
349
390
|
|
350
391
|
def object_descriptors
|
@@ -360,37 +401,30 @@ module Hakuban
|
|
360
401
|
result = FFI::hakuban_tag_observe_object_state_borrow(@tag_observe_pointer, object_descriptor.to_ffi)
|
361
402
|
return nil if result[:error] == 4
|
362
403
|
Hakuban::raise_if_error(result)
|
363
|
-
ObjectObserveState.new(result[:state])
|
404
|
+
ObjectObserveState.new(result[:state], @deserializer)
|
364
405
|
end
|
365
406
|
|
366
|
-
def
|
367
|
-
|
368
|
-
|
369
|
-
|
370
|
-
def send_objects_events_to(queue, *prefix)
|
371
|
-
@queues[queue] = EventQueue.new(
|
372
|
-
queue,
|
373
|
-
prefix,
|
374
|
-
FFI::method(:hakuban_tag_observe_objects_changes_callback_register),
|
375
|
-
@tag_observe_pointer,
|
376
|
-
FFI::method(:hakuban_object_callback_unregister)
|
377
|
-
)
|
407
|
+
def new_callback_event_queue
|
408
|
+
raise "Attempt to use after 'drop'" if not @tag_observe_pointer
|
409
|
+
ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_tag_observe_events_get(@tag_observe_pointer))
|
378
410
|
end
|
379
411
|
|
380
|
-
def
|
381
|
-
@
|
412
|
+
def new_event_queue
|
413
|
+
raise "Attempt to use after 'drop'" if not @tag_observe_pointer
|
414
|
+
queue = ObjectDescriptorEventQueue.new(self)
|
415
|
+
@queues << queue
|
416
|
+
queue
|
382
417
|
end
|
383
418
|
|
384
419
|
def drop
|
385
420
|
@tag_observe_pointer.free
|
386
421
|
@tag_observe_pointer = nil
|
422
|
+
@queues.each(&:close) #is this atomic?
|
387
423
|
@queues.clear
|
388
424
|
end
|
389
425
|
|
390
|
-
def
|
391
|
-
|
392
|
-
manager.contract = self
|
393
|
-
manager
|
426
|
+
def dropped?
|
427
|
+
@tag_observe_pointer.nil?
|
394
428
|
end
|
395
429
|
|
396
430
|
end
|
@@ -400,12 +434,12 @@ module Hakuban
|
|
400
434
|
|
401
435
|
attr_reader :descriptor
|
402
436
|
|
403
|
-
def initialize(local_node, descriptor)
|
404
|
-
@local_node, @descriptor = local_node, descriptor
|
437
|
+
def initialize(local_node, descriptor, serializer)
|
438
|
+
@local_node, @descriptor, @serializer = local_node, descriptor, serializer
|
405
439
|
result = FFI::hakuban_tag_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
|
406
440
|
Hakuban::raise_if_error(result)
|
441
|
+
@queues = []
|
407
442
|
@tag_expose_pointer = ::FFI::AutoPointer.new(result[:tag_expose_pointer], FFI::method(:hakuban_tag_expose_drop))
|
408
|
-
@queues = {}
|
409
443
|
end
|
410
444
|
|
411
445
|
def object_descriptors
|
@@ -416,43 +450,50 @@ module Hakuban
|
|
416
450
|
ret
|
417
451
|
end
|
418
452
|
|
419
|
-
def set_object_state(object_descriptor, version, data_type,
|
453
|
+
def set_object_state(object_descriptor, version, data, data_type: [], assignment: 0)
|
420
454
|
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
421
|
-
|
422
|
-
|
423
|
-
result = FFI::hakuban_tag_expose_object_state(@tag_expose_pointer, object_descriptor.to_ffi, FFI::FFIObjectExposeState.construct(version,
|
455
|
+
raise if not version #TODO: replace this by type check
|
456
|
+
serialized_data_type, serialized_data = @serializer.call(data_type, data)
|
457
|
+
result = FFI::hakuban_tag_expose_object_state(@tag_expose_pointer, object_descriptor.to_ffi, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_data), assignment)
|
424
458
|
Hakuban::raise_if_error(result)
|
425
459
|
result[:changed] == 1
|
426
460
|
end
|
427
461
|
|
428
|
-
def
|
429
|
-
|
462
|
+
def assignment(object_descriptor)
|
463
|
+
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
464
|
+
FFI::hakuban_tag_expose_object_assignment(@tag_expose_pointer, object_descriptor.to_ffi)
|
465
|
+
end
|
466
|
+
|
467
|
+
def assigned?(object_descriptor)
|
468
|
+
assignment(object_descriptor) > 0
|
469
|
+
end
|
470
|
+
|
471
|
+
def desynchronize(object_descriptor, assignment)
|
472
|
+
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
473
|
+
FFI::hakuban_tag_expose_object_desynchronize(@tag_expose_pointer, object_descriptor.to_ffi, assignment)
|
430
474
|
end
|
431
475
|
|
432
|
-
def
|
433
|
-
|
434
|
-
|
435
|
-
prefix,
|
436
|
-
FFI::method(:hakuban_tag_expose_objects_changes_callback_register),
|
437
|
-
@tag_expose_pointer,
|
438
|
-
FFI::method(:hakuban_object_callback_unregister)
|
439
|
-
)
|
476
|
+
def new_callback_event_queue
|
477
|
+
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
478
|
+
ObjectDescriptorCallbackEventQueue.new(FFI::hakuban_tag_expose_events_get(@tag_expose_pointer))
|
440
479
|
end
|
441
480
|
|
442
|
-
def
|
443
|
-
@
|
481
|
+
def new_event_queue
|
482
|
+
raise "Attempt to use after 'drop'" if not @tag_expose_pointer
|
483
|
+
queue = ObjectDescriptorEventQueue.new(self)
|
484
|
+
@queues << queue
|
485
|
+
queue
|
444
486
|
end
|
445
487
|
|
446
488
|
def drop
|
447
489
|
@tag_expose_pointer.free
|
448
490
|
@tag_expose_pointer = nil
|
491
|
+
@queues.each(&:close) #is this atomic?
|
449
492
|
@queues.clear
|
450
493
|
end
|
451
494
|
|
452
|
-
def
|
453
|
-
|
454
|
-
manager.contract = self
|
455
|
-
manager
|
495
|
+
def dropped?
|
496
|
+
@tag_expose_pointer.nil?
|
456
497
|
end
|
457
498
|
|
458
499
|
end
|
@@ -487,12 +528,6 @@ module Hakuban
|
|
487
528
|
@websocket_connector_pointer = ::FFI::AutoPointer.new(result[:websocket_connector_pointer], WebsocketConnector.generate_drop)
|
488
529
|
end
|
489
530
|
|
490
|
-
def start(local_node)
|
491
|
-
@local_node = local_node
|
492
|
-
FFI::hakuban_tokio_websocket_connector_start(Tokio.pointer, @websocket_connector_pointer, @local_node.local_node_pointer)
|
493
|
-
self
|
494
|
-
end
|
495
|
-
|
496
531
|
def self.generate_drop
|
497
532
|
proc { |pointer|
|
498
533
|
FFI::hakuban_tokio_websocket_connector_drop(Tokio.pointer, pointer)
|
@@ -502,149 +537,4 @@ module Hakuban
|
|
502
537
|
end
|
503
538
|
|
504
539
|
|
505
|
-
# convenience utils
|
506
|
-
|
507
|
-
#TODO: replace this mess (<Hash, dynamic mixings) with some "deref"
|
508
|
-
class ObjectManager < Hash
|
509
|
-
|
510
|
-
module ObservedObjectManager
|
511
|
-
def descriptor; self.values.first.descriptor; end
|
512
|
-
def state; self.values.first.state; end
|
513
|
-
def data; self.values.first.data; end
|
514
|
-
def version; self.values.first.verison; end
|
515
|
-
def synchronized; self.values.first.synchronized; end
|
516
|
-
def construct_object(contract, descriptor); ObservedObject.new(contract, descriptor); end
|
517
|
-
end
|
518
|
-
|
519
|
-
module ExposedObjectManager
|
520
|
-
def descriptor; self.values.first.descriptor; end
|
521
|
-
def state=(params); self.values.first.state = params; end
|
522
|
-
def data=(params); self.values.first.data = params; end
|
523
|
-
def state_set(params); self.values.first.state_set(params); end
|
524
|
-
def data_set(params); self.values.first.data_set(params); end
|
525
|
-
def construct_object(contract, descriptor); ExposedObject.new(contract, descriptor); end
|
526
|
-
end
|
527
|
-
|
528
|
-
module ObservedTagManager
|
529
|
-
def construct_object(contract, descriptor); ObservedObject.new(contract, descriptor); end
|
530
|
-
end
|
531
|
-
|
532
|
-
module ExposedTagManager
|
533
|
-
def construct_object(contract, descriptor); ExposedObject.new(contract, descriptor); end
|
534
|
-
end
|
535
|
-
|
536
|
-
attr_reader :objects, :contract
|
537
|
-
|
538
|
-
def contract=(contract)
|
539
|
-
raise "You can't change contracts, sorry." if @contract
|
540
|
-
@contract = contract
|
541
|
-
case @contract.class.name
|
542
|
-
when "Hakuban::ObjectObserve" then singleton_class.include(ObservedObjectManager)
|
543
|
-
when "Hakuban::ObjectExpose" then singleton_class.include(ExposedObjectManager)
|
544
|
-
when "Hakuban::TagObserve" then singleton_class.include(ObservedTagManager)
|
545
|
-
when "Hakuban::TagExpose" then singleton_class.include(ExposedTagManager)
|
546
|
-
else raise
|
547
|
-
end
|
548
|
-
|
549
|
-
@queue = Queue.new
|
550
|
-
@contract.send_events_to(@queue, :object)
|
551
|
-
if @contract.kind_of?(ObjectObserve) or @contract.kind_of?(ObjectExpose)
|
552
|
-
event, param, descriptor = @queue.pop
|
553
|
-
raise if event != :object or param != :insert or not descriptor
|
554
|
-
self[descriptor] = self.construct_object(@contract, descriptor)
|
555
|
-
end
|
556
|
-
|
557
|
-
@thread = Thread.new {
|
558
|
-
loop {
|
559
|
-
event,param,descriptor = @queue.shift
|
560
|
-
case [event, param]
|
561
|
-
when [:control, :quit]
|
562
|
-
break
|
563
|
-
when [:object, :insert]
|
564
|
-
self[descriptor] = construct_object(@contract, descriptor)
|
565
|
-
object_insert(self[descriptor])
|
566
|
-
when [:object, :change]
|
567
|
-
object_change(self[descriptor])
|
568
|
-
when [:object, :remove]
|
569
|
-
object_remove(delete(descriptor))
|
570
|
-
end
|
571
|
-
}
|
572
|
-
}
|
573
|
-
end
|
574
|
-
|
575
|
-
|
576
|
-
def object_insert(object)
|
577
|
-
# noop default
|
578
|
-
end
|
579
|
-
|
580
|
-
def object_change(object)
|
581
|
-
# noop default
|
582
|
-
end
|
583
|
-
|
584
|
-
def object_remove(object)
|
585
|
-
# noop default
|
586
|
-
end
|
587
|
-
|
588
|
-
|
589
|
-
def drop
|
590
|
-
@contract.stop_sending_objects_events_to(@queue)
|
591
|
-
@queue.push [:control, :quit]
|
592
|
-
@thread.join
|
593
|
-
@queue, @contract, @thread, @objects = nil, nil, nil, {}
|
594
|
-
end
|
595
|
-
|
596
|
-
|
597
|
-
class ManagedObject
|
598
|
-
attr_reader :contract, :descriptor
|
599
|
-
def initialize(contract, descriptor)
|
600
|
-
@contract, @descriptor = contract, descriptor
|
601
|
-
end
|
602
|
-
end
|
603
|
-
|
604
|
-
#TODO: remove the arity thing...
|
605
|
-
class ObservedObject < ManagedObject
|
606
|
-
def state
|
607
|
-
if @contract.method(:object_state).arity == 1
|
608
|
-
@contract.object_state(@descriptor)
|
609
|
-
else
|
610
|
-
@contract.object_state
|
611
|
-
end
|
612
|
-
end
|
613
|
-
def data
|
614
|
-
if @contract.method(:object_state).arity == 1
|
615
|
-
@contract.object_state(@descriptor)&.data
|
616
|
-
else
|
617
|
-
@contract.object_state&.data
|
618
|
-
end
|
619
|
-
end
|
620
|
-
end
|
621
|
-
|
622
|
-
class ExposedObject < ManagedObject
|
623
|
-
def state_set(state)
|
624
|
-
if @contract.method(:set_object_state).arity == 4
|
625
|
-
@contract.set_object_state(@descriptor, *state)
|
626
|
-
else
|
627
|
-
@contract.set_object_state(*state)
|
628
|
-
end
|
629
|
-
end
|
630
|
-
def state=(state)
|
631
|
-
state_set(state)
|
632
|
-
end
|
633
|
-
def data_set(value)
|
634
|
-
timestamp = Time.new.to_f
|
635
|
-
if @contract.method(:set_object_state).arity == 4
|
636
|
-
@contract.set_object_state(@descriptor,[1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],[],value)
|
637
|
-
else
|
638
|
-
@contract.set_object_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],[],value)
|
639
|
-
end
|
640
|
-
end
|
641
|
-
def data=(value)
|
642
|
-
data_set(value)
|
643
|
-
end
|
644
|
-
end
|
645
|
-
end
|
646
|
-
|
647
|
-
|
648
|
-
|
649
|
-
|
650
540
|
end
|