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