hakuban 0.5.3 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -7,6 +7,7 @@ require_relative './ffi.rb'
7
7
  #TODO: privative methods
8
8
  #TODO: error classes
9
9
 
10
+ #AutoPointers use proc-wrapped calls because they segfault sometimes when used with FFI::method(:xxx) (why?)
10
11
 
11
12
  module Hakuban
12
13
 
@@ -50,7 +51,7 @@ module Hakuban
50
51
 
51
52
  attr_reader :local_node_pointer #todo: hide
52
53
 
53
- def initialize(name=nil)
54
+ def initialize(name: nil)
54
55
  @default_serializer = lambda { |data_type, data|
55
56
  [["JSON"]+data_type, JSON.dump(data)]
56
57
  }
@@ -157,15 +158,16 @@ module Hakuban
157
158
  attr_reader :tags, :json
158
159
 
159
160
  def initialize(tags,json)
160
- @tags, @json = Set.new(tags), json
161
+ @tags = Set.new(tags.map { |tag| tag.kind_of?(TagDescriptor) ? tag : TagDescriptor.new(tag) })
162
+ @json = json
161
163
  end
162
164
 
163
165
  def to_ffi
164
- FFI::FFIObjectDescriptor.construct(@tags, @json)
166
+ FFI::FFIObjectDescriptor.construct(@tags.map(&:json), @json)
165
167
  end
166
168
 
167
169
  def self.from_ffi(ffi)
168
- ObjectDescriptor.new(ffi.tags, ffi.json)
170
+ ObjectDescriptor.new(ffi.tags.map{ |tag| TagDescriptor.new(tag) }, ffi.json)
169
171
  end
170
172
 
171
173
  def ==(other)
@@ -213,38 +215,35 @@ module Hakuban
213
215
  end
214
216
 
215
217
 
216
- class EventQueue
217
-
218
- def initialize(queue, prefix, register_method, pointer, unregister_method)
219
- @queue = queue
220
- @callback = EventQueue.generate_callback(@queue, prefix)
221
- @registered_callback_pointer = ::FFI::AutoPointer.new(
222
- register_method.call(pointer, @callback, ::FFI::Pointer::NULL),
223
- EventQueue.generate_unregister(unregister_method, @callback)
224
- )
225
- #puts "Allocated callback pointer: #{@registered_callback_pointer}"
226
- #puts "Allocated callback queue: #{@events}"
227
- 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
228
222
 
229
- def self.generate_callback(queue, prefix)
230
- proc { |_userdata, ffi_descriptor, ffi_action|
231
- #puts "Callback for queue: #{queue}"
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|
232
226
  action = Hakuban::action_int_to_symbol(ffi_action)
233
227
  descriptor = ObjectDescriptor.from_ffi(ffi_descriptor)
234
- queue << prefix + [action, descriptor]
228
+ callback.call(descriptor, action)
235
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
+ )
236
234
  end
237
235
 
238
- def self.generate_unregister(unregister_method, callback)
239
- proc { |pointer|
240
- #puts "Dropping callback pointer: #{pointer}"
241
- callback # probably not needed, but I want to be sure this is captured
242
- unregister_method.call(pointer)
243
- }
236
+ def callback_unregister
237
+ @callback_pointer.free
238
+ @callback_pointer = nil
244
239
  end
245
240
 
246
- end
241
+ def drop
242
+ @events_pointer.free
243
+ @events_pointer = nil
244
+ end
247
245
 
246
+ end
248
247
 
249
248
  class ObjectObserve
250
249
 
@@ -254,27 +253,21 @@ module Hakuban
254
253
  @local_node, @descriptor, @deserializer = local_node, descriptor, deserializer
255
254
  result = FFI::hakuban_object_observe_new(@local_node.local_node_pointer, descriptor.to_ffi)
256
255
  Hakuban::raise_if_error(result)
257
- @object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], FFI::method(:hakuban_object_observe_drop))
258
- @queues = {}
256
+ @object_observe_pointer = ::FFI::AutoPointer.new(result[:object_observe_pointer], proc { |ptr| FFI::hakuban_object_observe_drop(ptr) })
259
257
  end
260
258
 
261
259
  def object_state
262
260
  raise "Attempt to use after 'drop'" if not @object_observe_pointer
263
- ObjectObserveState.new(FFI::hakuban_object_observe_state_borrow(@object_observe_pointer), @deserializer)
264
- end
265
-
266
- def send_events_to(queue, *prefix)
267
- @queues[queue] = EventQueue.new(
268
- queue,
269
- prefix,
270
- FFI::method(:hakuban_object_observe_changes_callback_register),
271
- @object_observe_pointer,
272
- FFI::method(:hakuban_object_callback_unregister)
273
- )
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
274
266
  end
275
267
 
276
- def stop_sending_objects_events_to(queue)
277
- @queues.delete(queue)
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))
278
271
  end
279
272
 
280
273
  def inspect
@@ -284,13 +277,10 @@ module Hakuban
284
277
  def drop
285
278
  @object_observe_pointer.free
286
279
  @object_observe_pointer = nil
287
- @queues.clear
288
280
  end
289
281
 
290
- def manage(manager=nil)
291
- manager ||= ObjectManager.new
292
- manager.contract = self
293
- manager
282
+ def dropped?
283
+ @object_observe_pointer.nil?
294
284
  end
295
285
 
296
286
  end
@@ -305,34 +295,33 @@ module Hakuban
305
295
  result = FFI::hakuban_object_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
306
296
  Hakuban::raise_if_error(result)
307
297
  @object_expose_pointer = ::FFI::AutoPointer.new(result[:object_expose_pointer], FFI::method(:hakuban_object_expose_drop))
308
- @queues = {}
309
298
  end
310
299
 
311
- def set_object_state(version, data, data_type = [])
300
+ def set_object_state(version, data, data_type: [], assignment: 0)
312
301
  raise "Attempt to use after 'drop'" if not @object_expose_pointer
313
302
  serialized_data_type, serialized_data = @serializer.call(data_type, data)
314
- result = FFI::hakuban_object_expose_state(@object_expose_pointer, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_data))
303
+ result = FFI::hakuban_object_expose_state(@object_expose_pointer, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_data), assignment)
315
304
  Hakuban::raise_if_error(result)
316
305
  result[:changed] == 1
317
306
  end
318
307
 
319
- def assigned
308
+ def assignment
320
309
  raise "Attempt to use after 'drop'" if not @object_expose_pointer
321
- FFI::hakuban_object_expose_assigned(@object_expose_pointer) == 1
310
+ FFI::hakuban_object_expose_assignment(@object_expose_pointer)
322
311
  end
323
312
 
324
- def send_events_to(queue, *prefix)
325
- @queues[queue] = EventQueue.new(
326
- queue,
327
- prefix,
328
- FFI::method(:hakuban_object_expose_changes_callback_register),
329
- @object_expose_pointer,
330
- FFI::method(:hakuban_object_callback_unregister)
331
- )
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)
332
320
  end
333
321
 
334
- def stop_sending_objects_events_to(queue)
335
- @queues.delete(queue)
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))
336
325
  end
337
326
 
338
327
  def inspect
@@ -342,13 +331,10 @@ module Hakuban
342
331
  def drop
343
332
  @object_expose_pointer.free
344
333
  @object_expose_pointer = nil
345
- @queues.clear
346
334
  end
347
335
 
348
- def manage(manager=nil)
349
- manager ||= ObjectManager.new
350
- manager.contract = self
351
- manager
336
+ def dropped?
337
+ @object_expose_pointer.nil?
352
338
  end
353
339
 
354
340
  end
@@ -356,25 +342,41 @@ module Hakuban
356
342
 
357
343
  class ObjectObserveState
358
344
 
359
- attr_reader :data, :data_type, :data_version, :synchronized
360
-
361
- def initialize(raw_state, deserializer)
362
- @raw_state = raw_state
363
- @synchronized = (raw_state[:synchronized] == 1)
364
- @data_version = raw_state[:version].read_array_of_type(::FFI::TYPE_INT64, :read_int64, raw_state[:version_length]) if not raw_state[:version].null?
365
- serialized_data_type = raw_state[:data_type].read_array_of_pointer(raw_state[:data_type_length]).map { |string| string.read_string() }
366
- @data_type, @data = deserializer.call(serialized_data_type, raw_state[:raw].read_string_length(raw_state[:raw_length])) if not raw_state[:raw].null?
367
- 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))
368
348
  end
369
349
 
370
- def ObjectObserveState.finalize(raw_state)
350
+ def ObjectObserveState.finalize(state)
371
351
  proc { |_|
372
- FFI::hakuban_object_observe_state_return(raw_state)
352
+ FFI::hakuban_object_observe_state_return(state)
373
353
  }
374
354
  end
375
355
 
376
356
  def version
377
- @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)
378
380
  end
379
381
 
380
382
  def inspect
@@ -393,7 +395,6 @@ module Hakuban
393
395
  result = FFI::hakuban_tag_observe_new(@local_node.local_node_pointer, @descriptor.to_ffi)
394
396
  Hakuban::raise_if_error(result)
395
397
  @tag_observe_pointer = ::FFI::AutoPointer.new(result[:tag_observe_pointer], FFI::method(:hakuban_tag_observe_drop))
396
- @queues = {}
397
398
  end
398
399
 
399
400
  def object_descriptors
@@ -412,34 +413,18 @@ module Hakuban
412
413
  ObjectObserveState.new(result[:state], @deserializer)
413
414
  end
414
415
 
415
- def send_events_to(*params)
416
- self.send_objects_events_to(*params)
417
- end
418
-
419
- def send_objects_events_to(queue, *prefix)
420
- @queues[queue] = EventQueue.new(
421
- queue,
422
- prefix,
423
- FFI::method(:hakuban_tag_observe_objects_changes_callback_register),
424
- @tag_observe_pointer,
425
- FFI::method(:hakuban_object_callback_unregister)
426
- )
427
- end
428
-
429
- def stop_sending_objects_events_to(queue)
430
- @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))
431
419
  end
432
420
 
433
421
  def drop
434
422
  @tag_observe_pointer.free
435
423
  @tag_observe_pointer = nil
436
- @queues.clear
437
424
  end
438
425
 
439
- def manage(manager=nil)
440
- manager ||= ObjectManager.new
441
- manager.contract = self
442
- manager
426
+ def dropped?
427
+ @tag_observe_pointer.nil?
443
428
  end
444
429
 
445
430
  end
@@ -454,7 +439,6 @@ module Hakuban
454
439
  result = FFI::hakuban_tag_expose_new(@local_node.local_node_pointer, descriptor.to_ffi)
455
440
  Hakuban::raise_if_error(result)
456
441
  @tag_expose_pointer = ::FFI::AutoPointer.new(result[:tag_expose_pointer], FFI::method(:hakuban_tag_expose_drop))
457
- @queues = {}
458
442
  end
459
443
 
460
444
  def object_descriptors
@@ -465,42 +449,41 @@ module Hakuban
465
449
  ret
466
450
  end
467
451
 
468
- def set_object_state(object_descriptor, version, data, data_type = [])
452
+ def set_object_state(object_descriptor, version, data, data_type: [], assignment: 0)
469
453
  raise "Attempt to use after 'drop'" if not @tag_expose_pointer
454
+ raise if not version #TODO: replace this by type check
470
455
  serialized_data_type, serialized_data = @serializer.call(data_type, data)
471
- result = FFI::hakuban_tag_expose_object_state(@tag_expose_pointer, object_descriptor.to_ffi, FFI::FFIObjectExposeState.construct(version, serialized_data_type, serialized_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)
472
457
  Hakuban::raise_if_error(result)
473
458
  result[:changed] == 1
474
459
  end
475
460
 
476
- def send_events_to(*params)
477
- self.send_objects_events_to(*params)
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)
478
464
  end
479
465
 
480
- def send_objects_events_to(queue, *prefix)
481
- @queues[queue] = EventQueue.new(
482
- queue,
483
- prefix,
484
- FFI::method(:hakuban_tag_expose_objects_changes_callback_register),
485
- @tag_expose_pointer,
486
- FFI::method(:hakuban_object_callback_unregister)
487
- )
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)
488
473
  end
489
474
 
490
- def stop_sending_objects_events_to(queue)
491
- @queues.delete(queue)
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))
492
478
  end
493
479
 
494
480
  def drop
495
481
  @tag_expose_pointer.free
496
482
  @tag_expose_pointer = nil
497
- @queues.clear
498
483
  end
499
484
 
500
- def manage(manager=nil)
501
- manager ||= ObjectManager.new
502
- manager.contract = self
503
- manager
485
+ def dropped?
486
+ @tag_expose_pointer.nil?
504
487
  end
505
488
 
506
489
  end
@@ -544,150 +527,4 @@ module Hakuban
544
527
  end
545
528
 
546
529
 
547
- # convenience utils
548
-
549
- #TODO: replace this mess (<Hash, dynamic mixins) with some "deref"
550
- class ObjectManager < Hash
551
-
552
- module ObservedObjectManager
553
- def descriptor; self.values.first.descriptor; end
554
- def state; self.values.first.state; end
555
- def data; self.values.first.data; end
556
- def version; self.values.first.verison; end
557
- def synchronized; self.values.first.synchronized; end
558
- def construct_object(contract, descriptor); ObservedObject.new(contract, descriptor); end
559
- end
560
-
561
- module ExposedObjectManager
562
- def descriptor; self.values.first.descriptor; end
563
- def state=(params); self.values.first.state = params; end
564
- def data=(params); self.values.first.data = params; end
565
- def state_set(params); self.values.first.state_set(params); end
566
- def data_set(params); self.values.first.data_set(params); end
567
- def construct_object(contract, descriptor); ExposedObject.new(contract, descriptor); end
568
- end
569
-
570
- module ObservedTagManager
571
- def construct_object(contract, descriptor); ObservedObject.new(contract, descriptor); end
572
- end
573
-
574
- module ExposedTagManager
575
- def construct_object(contract, descriptor); ExposedObject.new(contract, descriptor); end
576
- end
577
-
578
- attr_reader :objects, :contract
579
-
580
- def contract=(contract)
581
- raise "You can't change contracts, sorry." if @contract
582
- @contract = contract
583
- case @contract.class.name
584
- when "Hakuban::ObjectObserve" then singleton_class.include(ObservedObjectManager)
585
- when "Hakuban::ObjectExpose" then singleton_class.include(ExposedObjectManager)
586
- when "Hakuban::TagObserve" then singleton_class.include(ObservedTagManager)
587
- when "Hakuban::TagExpose" then singleton_class.include(ExposedTagManager)
588
- else raise
589
- end
590
-
591
- @queue = Queue.new
592
- @contract.send_events_to(@queue, :object)
593
- if @contract.kind_of?(ObjectObserve) or @contract.kind_of?(ObjectExpose)
594
- event, param, descriptor = @queue.pop
595
- raise if event != :object or param != :insert or not descriptor
596
- self[descriptor] = self.construct_object(@contract, descriptor)
597
- end
598
-
599
- @thread = Thread.new {
600
- loop {
601
- event,param,descriptor = @queue.shift
602
- case [event, param]
603
- when [:control, :quit]
604
- break
605
- when [:object, :insert]
606
- self[descriptor] = construct_object(@contract, descriptor)
607
- object_insert(self[descriptor])
608
- when [:object, :change]
609
- object_change(self[descriptor])
610
- when [:object, :remove]
611
- object_remove(delete(descriptor))
612
- end
613
- }
614
- }
615
- end
616
-
617
-
618
- def object_insert(object)
619
- # noop default
620
- end
621
-
622
- def object_change(object)
623
- # noop default
624
- end
625
-
626
- def object_remove(object)
627
- # noop default
628
- end
629
-
630
-
631
- def drop
632
- @contract.stop_sending_objects_events_to(@queue)
633
- @queue.push [:control, :quit]
634
- @thread.join
635
- @queue, @contract, @thread, @objects = nil, nil, nil, {}
636
- end
637
-
638
-
639
- class ManagedObject < OpenStruct
640
- attr_reader :contract, :descriptor
641
- def initialize(contract, descriptor)
642
- @contract, @descriptor = contract, descriptor
643
- super()
644
- end
645
- end
646
-
647
- #TODO: remove the arity thing...
648
- class ObservedObject < ManagedObject
649
- def state
650
- if @contract.method(:object_state).arity == 1
651
- @contract.object_state(@descriptor)
652
- else
653
- @contract.object_state
654
- end
655
- end
656
- def data
657
- if @contract.method(:object_state).arity == 1
658
- @contract.object_state(@descriptor)&.data
659
- else
660
- @contract.object_state&.data
661
- end
662
- end
663
- end
664
-
665
- class ExposedObject < ManagedObject
666
- def state_set(state)
667
- if @contract.method(:set_object_state).arity == -4
668
- @contract.set_object_state(@descriptor, *state)
669
- else
670
- @contract.set_object_state(*state)
671
- end
672
- end
673
- def state=(state)
674
- state_set(state)
675
- end
676
- def data_set(value)
677
- timestamp = Time.new.to_f
678
- if @contract.method(:set_object_state).arity == -4
679
- @contract.set_object_state(@descriptor,[1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
680
- else
681
- @contract.set_object_state([1, timestamp.floor, ((timestamp - timestamp.floor)*1000000000).floor, 0],value)
682
- end
683
- end
684
- def data=(value)
685
- data_set(value)
686
- end
687
- end
688
- end
689
-
690
-
691
-
692
-
693
530
  end