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.
- checksums.yaml +4 -4
- data/bin/{hakuban-thread-engine → hakuban-engine} +12 -11
- data/bin/hakuban-observe +61 -0
- data/lib/hakuban/contract.rb +148 -0
- data/lib/hakuban/descriptor.rb +85 -0
- data/lib/hakuban/engine.rb +73 -29
- data/lib/hakuban/exchange.rb +115 -0
- data/lib/hakuban/ffi-object.rb +123 -0
- data/lib/hakuban/ffi.rb +133 -150
- data/lib/hakuban/logger.rb +16 -0
- data/lib/hakuban/object_state.rb +105 -0
- data/lib/hakuban/object_state_sink.rb +56 -0
- data/lib/hakuban/object_state_stream.rb +39 -0
- data/lib/hakuban/refinements.rb +23 -0
- data/lib/hakuban/stream.rb +189 -0
- data/lib/hakuban/tokio-websocket-connector.rb +39 -0
- data/lib/hakuban/version.rb +1 -1
- data/lib/hakuban.rb +21 -6
- metadata +40 -20
- data/bin/hakuban-observer +0 -64
- data/lib/hakuban/async.rb +0 -38
- data/lib/hakuban/event-queue.rb +0 -75
- data/lib/hakuban/hakuban.rb +0 -545
- data/lib/hakuban/manager.rb +0 -398
- data/lib/hakuban/thread.rb +0 -35
@@ -0,0 +1,123 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
|
4
|
+
module Hakuban
|
5
|
+
|
6
|
+
class FFIObject
|
7
|
+
|
8
|
+
class PointerAlreadyDropped < Exception; end
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@pointer_mutex = Mutex.new
|
12
|
+
end
|
13
|
+
|
14
|
+
private def initialize_from_ffi_pointer(pointer, drop_fn, clone_fn)
|
15
|
+
@pointer_mutex = Mutex.new
|
16
|
+
initialize_pointer(pointer, drop_fn, clone_fn)
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
def self.from_ffi_pointer(cls, pointer)
|
21
|
+
cls.allocate.tap { |new_instance| new_instance.initialize_from_ffi_pointer(pointer) }
|
22
|
+
end
|
23
|
+
|
24
|
+
private def initialize_pointer(pointer, drop_fn, clone_fn)
|
25
|
+
@pointer, @drop_fn, @clone_fn = pointer.address, drop_fn, clone_fn
|
26
|
+
ObjectSpace.define_finalizer(self, FFIObject::generate_finalizer(drop_fn, @pointer))
|
27
|
+
end
|
28
|
+
|
29
|
+
|
30
|
+
def initialize_copy(original)
|
31
|
+
Thread.handle_interrupt(Object => :never) {
|
32
|
+
if @clone_fn.nil?
|
33
|
+
@pointer_mutex.synchronize {
|
34
|
+
raise PointerAlreadyDropped if original.instance_variable_get(:@pointer).nil?
|
35
|
+
original.instance_variable_set(:@pointer, nil)
|
36
|
+
ObjectSpace.undefine_finalizer(original)
|
37
|
+
}
|
38
|
+
else
|
39
|
+
original.with_pointer { |pointer|
|
40
|
+
@pointer = FFI::method(@clone_fn).call(::FFI::Pointer.new(pointer)).address
|
41
|
+
}
|
42
|
+
end
|
43
|
+
@pointer_mutex = Mutex.new
|
44
|
+
ObjectSpace.undefine_finalizer(self)
|
45
|
+
ObjectSpace.define_finalizer(self, FFIObject::generate_finalizer(@drop_fn, @pointer))
|
46
|
+
}
|
47
|
+
end
|
48
|
+
|
49
|
+
|
50
|
+
def with_pointer
|
51
|
+
@pointer_mutex.synchronize {
|
52
|
+
if @pointer
|
53
|
+
yield ::FFI::Pointer.new(@pointer)
|
54
|
+
else
|
55
|
+
raise PointerAlreadyDropped
|
56
|
+
end
|
57
|
+
}
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
def self.with_pointers(objects,&original_block)
|
62
|
+
do_locked = proc { |original_block, remaining_objects, pointers|
|
63
|
+
if remaining_objects.size == 0
|
64
|
+
original_block.call(pointers)
|
65
|
+
else
|
66
|
+
object, i = remaining_objects.shift
|
67
|
+
object.with_pointer { |pointer|
|
68
|
+
pointers[i] = pointer
|
69
|
+
do_locked.call(original_block, remaining_objects, pointers)
|
70
|
+
}
|
71
|
+
end
|
72
|
+
}
|
73
|
+
do_locked.call(original_block, objects.each.with_index.sort_by { |object, i| object.instance_variable_get(:@pointer) || -i }, Array.new(objects.size))
|
74
|
+
end
|
75
|
+
|
76
|
+
|
77
|
+
def drop
|
78
|
+
@pointer_mutex.synchronize {
|
79
|
+
Thread.handle_interrupt(Object => :never) {
|
80
|
+
if !!@pointer
|
81
|
+
ObjectSpace.undefine_finalizer(self)
|
82
|
+
FFI::method(@drop_fn).call(::FFI::Pointer.new(@pointer))
|
83
|
+
@pointer = nil
|
84
|
+
end
|
85
|
+
}
|
86
|
+
}
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def dropped?
|
91
|
+
!@pointer
|
92
|
+
end
|
93
|
+
|
94
|
+
|
95
|
+
def self.generate_finalizer(symbol, pointer_address)
|
96
|
+
proc { |_|
|
97
|
+
FFI::method(symbol).call(::FFI::Pointer.new(pointer_address))
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
# this should always be called with interrupts disabled, in the same section where pointer creation occurs
|
103
|
+
def do_and_drop_or_return(&block)
|
104
|
+
if block
|
105
|
+
begin
|
106
|
+
Thread.handle_interrupt(Object => :immediate) {
|
107
|
+
yield self
|
108
|
+
}
|
109
|
+
ensure
|
110
|
+
self.drop
|
111
|
+
end
|
112
|
+
else
|
113
|
+
self
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def inspect
|
118
|
+
"#<#{self.class.name} #{self.dropped? ? "DROPPED" : "%016X"%@pointer}>"
|
119
|
+
end
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
data/lib/hakuban/ffi.rb
CHANGED
@@ -1,174 +1,157 @@
|
|
1
1
|
require 'json'
|
2
2
|
require 'ffi'
|
3
|
+
require 'hakuban/ffi-object'
|
4
|
+
require 'hakuban/refinements'
|
3
5
|
|
4
|
-
|
5
|
-
# all functions which can trigger synchronuous callbacks, and those which wait on rust locks are "blocking: true" to avoid deadlock on GIL
|
6
6
|
module Hakuban::FFI
|
7
7
|
|
8
|
-
|
9
|
-
|
10
|
-
end
|
11
|
-
|
12
|
-
class FFIObjectObserveResult < FFI::Struct
|
13
|
-
layout :error, :uint8, :object_observe_pointer, :pointer
|
14
|
-
end
|
15
|
-
|
16
|
-
class FFIObjectExposeResult < FFI::Struct
|
17
|
-
layout :error, :uint8, :object_expose_pointer, :pointer
|
18
|
-
end
|
19
|
-
|
20
|
-
class FFITagObserveResult < FFI::Struct
|
21
|
-
layout :error, :uint8, :tag_observe_pointer, :pointer
|
22
|
-
end
|
23
|
-
|
24
|
-
class FFITagExposeResult < FFI::Struct
|
25
|
-
layout :error, :uint8, :tag_expose_pointer, :pointer
|
26
|
-
end
|
27
|
-
|
28
|
-
class FFIObjectObserveStateGetVersionResult < FFI::Struct
|
29
|
-
layout :version_length, :size_t, :version_elements, :pointer
|
30
|
-
end
|
8
|
+
extend FFI::Library
|
9
|
+
ffi_lib 'hakuban'
|
31
10
|
|
32
|
-
class FFIObjectObserveStateGetDataTypeResult < FFI::Struct
|
33
|
-
layout :type_length, :size_t, :type_elements, :pointer
|
34
|
-
end
|
35
11
|
|
36
|
-
class
|
37
|
-
|
38
|
-
end
|
12
|
+
class FFIError < Exception; end
|
13
|
+
class FFIErrorInvalidString < FFIError; end
|
14
|
+
class FFIErrorInvalidJSON < FFIError; end
|
15
|
+
class FFIErrorInvalidURL < FFIError; end
|
16
|
+
class FFIErrorInvalidLogLevel < FFIError; end
|
17
|
+
class FFIErrorUnknownError < FFIError; end
|
39
18
|
|
40
|
-
class FFIObjectExposeState < FFI::Struct
|
41
|
-
layout :version_length, :size_t, :version, :pointer, :data_type_length, :size_t, :data_type, :pointer, :raw_length, :size_t, :raw, :pointer
|
42
|
-
attr_accessor :data_type_strings
|
43
|
-
|
44
|
-
def self.construct(version, data_type, data)
|
45
|
-
state = FFIObjectExposeState::new
|
46
|
-
state[:version_length] = version.size
|
47
|
-
state[:version] = FFI::MemoryPointer.new(:int64, version.size)
|
48
|
-
state[:version].write_array_of_int64(version)
|
49
|
-
state[:data_type_length] = data_type.size
|
50
|
-
state[:data_type] = FFI::MemoryPointer.new(:pointer, data_type.size)
|
51
|
-
state.data_type_strings = data_type.map {|string| FFI::MemoryPointer.from_string(string)}
|
52
|
-
state[:data_type].write_array_of_pointer(state.data_type_strings)
|
53
|
-
state[:raw] = FFI::MemoryPointer.from_string(data)
|
54
|
-
state[:raw_length] = data.size
|
55
|
-
state
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class FFIObjectExposeStateResult < FFI::Struct
|
60
|
-
layout :error, :uint8, :changed, :uint8
|
61
|
-
end
|
62
|
-
|
63
|
-
class FFITokioWebsocketConnectorNewResult < FFI::Struct
|
64
|
-
layout :error, :uint8, :websocket_connector_pointer, :pointer
|
65
|
-
end
|
66
|
-
|
67
|
-
class FFITagObserveObjectStateBorrowResult < FFI::Struct
|
68
|
-
layout :error, :uint8, :state, :pointer
|
69
|
-
end
|
70
|
-
|
71
|
-
class FFIObjectDescriptor < FFI::Struct
|
72
|
-
layout :tags_count, :size_t, :tags, :pointer, :json, :pointer
|
73
|
-
attr_accessor :tags_strings
|
74
|
-
|
75
|
-
def tags
|
76
|
-
self[:tags].read_array_of_pointer(self[:tags_count]).map { |string| JSON.parse(string.read_string()) } # does this copy the string?
|
77
|
-
end
|
78
19
|
|
79
|
-
|
80
|
-
|
20
|
+
class FFIResultStatus < FFI::Struct
|
21
|
+
layout :id, :uint8
|
22
|
+
ENUM = [:Ok, :Pointer, :Pending, :EndOfStream, :InvalidString, :InvalidJSON, :InvalidURL, :InvalidLogLevel, :LoggerInitializationError, :ConnectionTerminated].freeze
|
23
|
+
|
24
|
+
def to_sym
|
25
|
+
ENUM[self[:id]]
|
81
26
|
end
|
82
|
-
|
83
|
-
def
|
84
|
-
|
85
|
-
descriptor[:tags_count] = tags.size
|
86
|
-
descriptor.tags_strings = tags.map { |tag| FFI::MemoryPointer.from_string(JSON.dump(tag)) }
|
87
|
-
descriptor[:tags] = FFI::MemoryPointer.new(:pointer, descriptor.tags_strings.size)
|
88
|
-
descriptor[:tags].write_array_of_pointer(descriptor.tags_strings)
|
89
|
-
descriptor[:json] = FFI::MemoryPointer.from_string(JSON.dump(json))
|
90
|
-
descriptor
|
27
|
+
|
28
|
+
def to_exception
|
29
|
+
Hakuban::FFI::const_get("FFIError"+ENUM[self[:id]].to_s)
|
91
30
|
end
|
92
|
-
|
93
31
|
end
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
32
|
+
|
33
|
+
|
34
|
+
class FFIResult < FFI::Struct
|
35
|
+
layout :status, FFIResultStatus, :pointer, :pointer
|
36
|
+
|
37
|
+
def unwrap
|
38
|
+
return true if self[:status].to_sym == :Ok
|
39
|
+
return nil if self[:status].to_sym == :EndOfStream
|
40
|
+
raise self[:status].to_exception if self[:status].to_sym != :Pointer
|
41
|
+
self[:pointer]
|
100
42
|
end
|
101
|
-
|
102
|
-
def
|
103
|
-
|
104
|
-
descriptor[:json] = FFI::MemoryPointer.from_string(JSON.dump(json))
|
105
|
-
descriptor
|
43
|
+
|
44
|
+
def status
|
45
|
+
self[:status].to_sym
|
106
46
|
end
|
107
|
-
|
108
47
|
end
|
109
|
-
|
110
|
-
|
111
|
-
class
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
48
|
+
|
49
|
+
|
50
|
+
class FFIFuture
|
51
|
+
|
52
|
+
using ThreadExt
|
53
|
+
|
54
|
+
#This HAS to be called with interrupts disabled.
|
55
|
+
#If both, internal exception, and an interrupt fire, pay attention not to drop one of them.
|
56
|
+
def self.await(future_pointer)
|
57
|
+
error = result_thread = result = nil
|
58
|
+
future_pointer_for_blocking_await = Hakuban::FFI::hakuban_future_clone(future_pointer)
|
59
|
+
begin
|
60
|
+
# ThreadError gets raised here on process shutdown
|
61
|
+
result_thread = Thread.new { Hakuban::FFI::hakuban_future_await(future_pointer_for_blocking_await) }
|
62
|
+
Thread.handle_interrupt(Object => :immediate) { result_thread.join }
|
63
|
+
rescue Object => e
|
64
|
+
error = e
|
65
|
+
end
|
66
|
+
Hakuban::FFI::hakuban_future_drop(future_pointer)
|
67
|
+
if result_thread
|
68
|
+
result = result_thread.join_with_warning
|
69
|
+
else
|
70
|
+
Hakuban::FFI::hakuban_future_drop(future_pointer_for_blocking_await)
|
71
|
+
end
|
72
|
+
return [result, error]
|
116
73
|
end
|
74
|
+
|
117
75
|
end
|
118
|
-
|
119
76
|
|
120
|
-
extend FFI::Library
|
121
77
|
|
122
|
-
|
78
|
+
class FFIArray < FFI::Struct
|
79
|
+
layout :length, :size_t, :pointer, :pointer
|
80
|
+
end
|
123
81
|
|
124
|
-
callback :object_callback, [ :pointer, FFIObjectDescriptor.by_value, :uint8 ], :void
|
125
|
-
#callback :tag_callback, [ :pointer, FFITagDescriptor.by_value, :uint8 ], :void
|
126
|
-
|
127
|
-
attach_function :hakuban_local_node_new, [ :string ], FFILocalNodeNewResult.by_value
|
128
|
-
attach_function :hakuban_local_node_drop, [ :pointer ], :void, blocking: true
|
129
|
-
|
130
|
-
attach_function :hakuban_object_observe_new, [ :pointer, FFIObjectDescriptor.by_value ], FFIObjectObserveResult.by_value, blocking: true
|
131
|
-
attach_function :hakuban_object_observe_drop, [ :pointer ], :void, blocking: true
|
132
|
-
attach_function :hakuban_object_observe_state_borrow, [ :pointer ], :pointer, blocking: true
|
133
|
-
attach_function :hakuban_object_observe_events_get, [ :pointer ], :pointer, blocking: true
|
134
|
-
|
135
|
-
attach_function :hakuban_object_expose_new, [ :pointer, FFIObjectDescriptor.by_value], FFIObjectExposeResult.by_value, blocking: true
|
136
|
-
attach_function :hakuban_object_expose_drop, [ :pointer ], :void, blocking: true
|
137
|
-
attach_function :hakuban_object_expose_state, [ :pointer, FFIObjectExposeState.by_value, :uint64 ], FFIObjectExposeStateResult.by_value, blocking: true
|
138
|
-
attach_function :hakuban_object_expose_assignment, [ :pointer ], :uint64, blocking: true
|
139
|
-
attach_function :hakuban_object_expose_desynchronize, [ :pointer, :uint64 ], :void, blocking: true
|
140
|
-
attach_function :hakuban_object_expose_events_get, [ :pointer ], :pointer, blocking: true
|
141
|
-
|
142
|
-
attach_function :hakuban_tag_observe_new, [ :pointer, FFITagDescriptor.by_value ], FFITagObserveResult.by_value, blocking: true
|
143
|
-
attach_function :hakuban_tag_observe_drop, [ :pointer ], :void, blocking: true
|
144
|
-
attach_function :hakuban_tag_observe_object_descriptors_borrow, [ :pointer ], FFIObjectDescriptors.by_value, blocking: true
|
145
|
-
attach_function :hakuban_tag_observe_object_state_borrow, [ :pointer, FFIObjectDescriptor.by_value ], FFITagObserveObjectStateBorrowResult.by_value, blocking: true
|
146
|
-
attach_function :hakuban_tag_observe_events_get, [ :pointer ], :pointer, blocking: true
|
147
|
-
|
148
|
-
attach_function :hakuban_tag_expose_new, [ :pointer, FFITagDescriptor.by_value ], FFITagExposeResult.by_value, blocking: true
|
149
|
-
attach_function :hakuban_tag_expose_drop, [ :pointer ], :void, blocking: true
|
150
|
-
attach_function :hakuban_tag_expose_object_descriptors_borrow, [ :pointer ], FFIObjectDescriptors.by_value, blocking: true
|
151
|
-
attach_function :hakuban_tag_expose_object_state, [ :pointer, FFIObjectDescriptor.by_value, FFIObjectExposeState.by_value, :uint64 ], FFIObjectExposeStateResult.by_value, blocking: true
|
152
|
-
attach_function :hakuban_tag_expose_object_assignment, [ :pointer, FFIObjectDescriptor.by_value ], :uint64, blocking: true
|
153
|
-
attach_function :hakuban_tag_expose_object_desynchronize, [ :pointer, FFIObjectDescriptor.by_value, :uint64 ], :void, blocking: true
|
154
|
-
attach_function :hakuban_tag_expose_events_get, [ :pointer ], :pointer, blocking: true
|
155
|
-
|
156
|
-
attach_function :hakuban_object_observe_state_get_synchronized, [ :pointer ], :uint64
|
157
|
-
attach_function :hakuban_object_observe_state_get_data_version, [ :pointer ], FFIObjectObserveStateGetVersionResult.by_value
|
158
|
-
attach_function :hakuban_object_observe_state_get_data_type, [ :pointer ], FFIObjectObserveStateGetDataTypeResult.by_value
|
159
|
-
attach_function :hakuban_object_observe_state_get_data, [ :pointer ], FFIObjectObserveStateGetDataResult.by_value
|
160
|
-
|
161
|
-
attach_function :hakuban_object_descriptor_events_callback_register, [ :pointer, :object_callback, :pointer ], :pointer, blocking: true
|
162
|
-
attach_function :hakuban_object_descriptor_events_callback_unregister, [ :pointer ], :void, blocking: true
|
163
|
-
attach_function :hakuban_object_descriptor_events_return, [ :pointer ], :void, blocking: true
|
164
|
-
|
165
|
-
attach_function :hakuban_object_observe_state_return, [ :pointer ], :void
|
166
|
-
attach_function :hakuban_object_descriptors_return, [ FFIObjectDescriptors.by_value ], :void
|
167
82
|
|
168
|
-
|
169
|
-
|
170
|
-
|
83
|
+
# Every function which can cause a callback has to be "blocking: true".
|
84
|
+
# Otherwise they are very likely to deadlock on GVL and other locks acquired by Connection(s) on incomming message
|
85
|
+
# Fortunately, we don't use callbacks any more. And the only function with unbounded execution time is the future-await. So, only setting that one as blocking.
|
86
|
+
|
87
|
+
callback :waker, [ :pointer ], :void
|
88
|
+
|
89
|
+
attach_function :hakuban_logger_initialize, [ :string ], FFIResult.by_value
|
171
90
|
|
172
|
-
attach_function :
|
91
|
+
attach_function :hakuban_exchange_new, [ ], FFIResult.by_value
|
92
|
+
attach_function :hakuban_exchange_drop, [ :pointer ], :void
|
93
|
+
attach_function :hakuban_exchange_clone, [ :pointer ], :pointer
|
173
94
|
|
95
|
+
attach_function :hakuban_tokio_init_multi_thread, [ :size_t ], :pointer
|
96
|
+
attach_function :hakuban_tokio_websocket_connector_new, [ :pointer, :pointer, :string ], FFIResult.by_value
|
97
|
+
attach_function :hakuban_tokio_websocket_connector_drop, [ :pointer ], :void
|
98
|
+
|
99
|
+
attach_function :hakuban_object_observe_contract_new, [ :pointer, :pointer ], :pointer
|
100
|
+
attach_function :hakuban_object_observe_contract_drop, [ :pointer ], :void
|
101
|
+
attach_function :hakuban_object_observe_contract_next, [ :pointer ], :pointer
|
102
|
+
|
103
|
+
attach_function :hakuban_object_expose_contract_new, [ :pointer, :pointer, :uint32 ], :pointer
|
104
|
+
attach_function :hakuban_object_expose_contract_drop, [ :pointer ], :void
|
105
|
+
attach_function :hakuban_object_expose_contract_next, [ :pointer ], :pointer
|
106
|
+
|
107
|
+
attach_function :hakuban_tag_observe_contract_new, [ :pointer, :pointer ], :pointer
|
108
|
+
attach_function :hakuban_tag_observe_contract_drop, [ :pointer ], :void
|
109
|
+
attach_function :hakuban_tag_observe_contract_next, [ :pointer ], :pointer
|
110
|
+
|
111
|
+
attach_function :hakuban_tag_expose_contract_new, [ :pointer, :pointer, :uint32 ], :pointer
|
112
|
+
attach_function :hakuban_tag_expose_contract_drop, [ :pointer ], :void
|
113
|
+
attach_function :hakuban_tag_expose_contract_next, [ :pointer ], :pointer
|
114
|
+
|
115
|
+
attach_function :hakuban_object_state_stream_drop, [ :pointer ], :void
|
116
|
+
attach_function :hakuban_object_state_stream_next, [ :pointer ], :pointer
|
117
|
+
attach_function :hakuban_object_state_stream_descriptor, [ :pointer ], :pointer
|
118
|
+
|
119
|
+
attach_function :hakuban_object_state_sink_drop, [ :pointer ], :void
|
120
|
+
attach_function :hakuban_object_state_sink_next, [ :pointer ], :pointer
|
121
|
+
attach_function :hakuban_object_state_sink_send, [ :pointer, :pointer ], :pointer
|
122
|
+
attach_function :hakuban_object_state_sink_descriptor, [ :pointer ], :pointer
|
123
|
+
|
124
|
+
attach_function :hakuban_object_state_sink_params_drop, [ :pointer ], :void
|
125
|
+
attach_function :hakuban_object_state_sink_params_clone, [ :pointer ], :pointer
|
126
|
+
|
127
|
+
attach_function :hakuban_object_state_new, [ :size_t, :pointer, :size_t, :pointer, :size_t, :pointer, :uint64 ], FFIResult.by_value
|
128
|
+
attach_function :hakuban_object_state_drop, [ :pointer ], :void
|
129
|
+
attach_function :hakuban_object_state_clone, [ :pointer ], :pointer
|
130
|
+
attach_function :hakuban_object_state_data, [ :pointer ], FFIArray.by_value
|
131
|
+
attach_function :hakuban_object_state_format, [ :pointer ], FFIArray.by_value
|
132
|
+
attach_function :hakuban_object_state_version, [ :pointer ], FFIArray.by_value
|
133
|
+
attach_function :hakuban_object_state_synchronized_ago, [ :pointer ], :uint64
|
134
|
+
|
135
|
+
attach_function :hakuban_tag_descriptor_new, [ :string ], FFIResult.by_value
|
136
|
+
attach_function :hakuban_tag_descriptor_drop, [ :pointer ], :void
|
137
|
+
attach_function :hakuban_tag_descriptor_clone, [ :pointer ], :pointer
|
138
|
+
attach_function :hakuban_tag_descriptor_json, [ :pointer ], :string
|
139
|
+
|
140
|
+
attach_function :hakuban_object_descriptor_new, [ :string, :size_t, :pointer ], FFIResult.by_value
|
141
|
+
attach_function :hakuban_object_descriptor_drop, [ :pointer ], :void
|
142
|
+
attach_function :hakuban_object_descriptor_clone, [ :pointer ], :pointer
|
143
|
+
attach_function :hakuban_object_descriptor_json, [ :pointer ], :string
|
144
|
+
attach_function :hakuban_object_descriptor_tags, [ :pointer ], FFIArray.by_value
|
145
|
+
|
146
|
+
attach_function :hakuban_future_clone, [ :pointer ], :pointer
|
147
|
+
attach_function :hakuban_future_drop, [ :pointer ], :void
|
148
|
+
#attach_function :hakuban_future_poll, [ :pointer, :waker, :pointer ], FFIResult.by_value #this is pita to get working right with interrupts, and without leaking memory in weird cases
|
149
|
+
attach_function :hakuban_future_await, [ :pointer ], FFIResult.by_value, blocking: true
|
150
|
+
|
151
|
+
attach_function :hakuban_array_drop, [ FFIArray.by_value ], :void
|
152
|
+
|
153
|
+
FFIResultStatus.freeze
|
154
|
+
FFIResult.freeze
|
155
|
+
FFIFuture.freeze
|
156
|
+
freeze
|
174
157
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Hakuban
|
2
|
+
|
3
|
+
@@logger_initialized = false
|
4
|
+
|
5
|
+
def self.logger_initialize(default_level, skip_if_already_initialized: false)
|
6
|
+
Hakuban::hakuban_initialize
|
7
|
+
if @@logger_initialized and !skip_if_already_initialized
|
8
|
+
raise "Logger already initialized. This can't be done more than once. Make sure logger_initialize is called before any Exchange gets constructed."
|
9
|
+
end
|
10
|
+
if not @@logger_initialized
|
11
|
+
raise "Invalid default log level string" if FFI::hakuban_logger_initialize(default_level).status != :Ok
|
12
|
+
@@logger_initialized = true
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'hakuban/ffi-object.rb'
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
module Hakuban
|
6
|
+
|
7
|
+
class ObjectState < FFIObject
|
8
|
+
|
9
|
+
class WrongFormatError < Exception; end
|
10
|
+
|
11
|
+
def initialize(data, version: nil, format: nil, synchronized_us_ago: 0, &block)
|
12
|
+
super()
|
13
|
+
if version.nil?
|
14
|
+
timestamp = Time.new
|
15
|
+
version = [0, timestamp.to_i, timestamp.nsec]
|
16
|
+
end
|
17
|
+
@version = version
|
18
|
+
@format = [format || []].flatten
|
19
|
+
@synchronized_us_ago = synchronized_us_ago
|
20
|
+
@data = data
|
21
|
+
@pointer = false
|
22
|
+
Thread.handle_interrupt(Object => :never) {
|
23
|
+
self.do_and_drop_or_return(&block)
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize_from_ffi_pointer(pointer)
|
28
|
+
super(pointer, :hakuban_object_state_drop, :hakuban_object_state_clone)
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
|
33
|
+
def with_pointer
|
34
|
+
@pointer_mutex.synchronize {
|
35
|
+
if @pointer == false
|
36
|
+
Hakuban::hakuban_initialize
|
37
|
+
version_pointer = ::FFI::MemoryPointer.new(:int64, @version.size)
|
38
|
+
version_pointer.write_array_of_int64(@version)
|
39
|
+
format_pointer = ::FFI::MemoryPointer.new(:pointer, @format.size)
|
40
|
+
format_strings = format.map {|string| ::FFI::MemoryPointer.from_string(string)}
|
41
|
+
format_pointer.write_array_of_pointer(format_strings)
|
42
|
+
data_pointer = ::FFI::MemoryPointer.from_string(@data)
|
43
|
+
pointer = FFI::hakuban_object_state_new(@version.size, version_pointer, @format.size, format_pointer, @data.bytesize, data_pointer, @synchronized_us_ago).unwrap
|
44
|
+
initialize_pointer(pointer, :hakuban_object_state_drop, :hakuban_object_state_clone)
|
45
|
+
end
|
46
|
+
yield ::FFI::Pointer.new(@pointer)
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def data
|
51
|
+
return @data if defined? @data
|
52
|
+
array = with_pointer { |pointer| FFI::hakuban_object_state_data(pointer) }
|
53
|
+
#array[:data_bytes].read_array_of_char(array[:length]) #.pack('c*').clone()
|
54
|
+
@data = array[:pointer].read_string(array[:length]).clone()
|
55
|
+
end
|
56
|
+
|
57
|
+
def format
|
58
|
+
return @format if defined? @format
|
59
|
+
array = with_pointer { |pointer| FFI::hakuban_object_state_format(pointer) }
|
60
|
+
@format = array[:pointer].read_array_of_pointer(array[:length]).map { |string| string.read_string().clone() }
|
61
|
+
end
|
62
|
+
|
63
|
+
def version
|
64
|
+
return @version if defined? @version
|
65
|
+
array = with_pointer { |pointer| FFI::hakuban_object_state_version(pointer) }
|
66
|
+
@version = array[:pointer].read_array_of_int64(array[:length])
|
67
|
+
end
|
68
|
+
|
69
|
+
def synchronized_us_ago
|
70
|
+
return @synchronized_us_ago if defined? @synchronized_us_ago
|
71
|
+
@synchronized_us_ago = with_pointer { |pointer| FFI::hakuban_object_state_synchronized_ago(pointer) }
|
72
|
+
end
|
73
|
+
|
74
|
+
def with_data(data)
|
75
|
+
ObjectState.new(data, version: version, format: format, synchronized_us_ago: synchronized_us_ago)
|
76
|
+
end
|
77
|
+
|
78
|
+
def with_version(version)
|
79
|
+
ObjectState.new(data, version: version, format: format, synchronized_us_ago: synchronized_us_ago)
|
80
|
+
end
|
81
|
+
|
82
|
+
def with_format(format)
|
83
|
+
ObjectState.new(data, version: version, format: format, synchronized_us_ago: synchronized_us_ago)
|
84
|
+
end
|
85
|
+
|
86
|
+
def with_synchronized_us_ago(synchronized_us_ago)
|
87
|
+
ObjectState.new(data, version: version, format: format, synchronized_us_ago: synchronized_us_ago)
|
88
|
+
end
|
89
|
+
|
90
|
+
def json_deserialize
|
91
|
+
raise WrongFormatError if format[-1] != "JSON"
|
92
|
+
ObjectState.new(JSON.parse(data), version: version, format: format[0...-1], synchronized_us_ago: synchronized_us_ago)
|
93
|
+
end
|
94
|
+
|
95
|
+
def json_serialize
|
96
|
+
ObjectState.new(data.to_json, version: version, format: format+["JSON"], synchronized_us_ago: synchronized_us_ago)
|
97
|
+
end
|
98
|
+
|
99
|
+
def inspect
|
100
|
+
"#<ObjectState @synchronized=%p @version=%p>"%[synchronized_us_ago, version]
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
require 'hakuban/ffi-object.rb'
|
2
|
+
require 'hakuban/stream.rb'
|
3
|
+
|
4
|
+
|
5
|
+
module Hakuban
|
6
|
+
|
7
|
+
class ObjectStateSink < FFIObject
|
8
|
+
|
9
|
+
private_class_method :new
|
10
|
+
|
11
|
+
|
12
|
+
def initialize_from_ffi_pointer(pointer)
|
13
|
+
super(pointer, :hakuban_object_state_sink_drop, nil)
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
include Stream
|
18
|
+
|
19
|
+
|
20
|
+
def next(drop_at_the_end_of_block=true, &block)
|
21
|
+
process_item(
|
22
|
+
lambda { |pointer| FFI::hakuban_object_state_sink_next(pointer) },
|
23
|
+
lambda { |pointer| FFIObject::from_ffi_pointer(ObjectStateSinkParams, pointer) },
|
24
|
+
&block
|
25
|
+
)
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def send(object_state)
|
30
|
+
process_item(
|
31
|
+
lambda { |pointer| object_state.with_pointer { |object_state_pointer| FFI::hakuban_object_state_sink_send(pointer, object_state_pointer) } },
|
32
|
+
lambda { |_pointer| nil },
|
33
|
+
&lambda {}
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
def descriptor
|
39
|
+
Hakuban::FFIObject.from_ffi_pointer(Hakuban::ObjectDescriptor, with_pointer { |pointer| FFI::hakuban_object_state_sink_descriptor(pointer) })
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
class ObjectStateSinkParams < FFIObject
|
46
|
+
|
47
|
+
private_class_method :new
|
48
|
+
|
49
|
+
def initialize_from_ffi_pointer(pointer)
|
50
|
+
super(pointer, :hakuban_object_state_sink_params_drop, :hakuban_object_state_sink_params_clone)
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'hakuban/ffi.rb'
|
2
|
+
require 'hakuban/ffi-object.rb'
|
3
|
+
require 'hakuban/stream.rb'
|
4
|
+
require 'hakuban/refinements.rb'
|
5
|
+
|
6
|
+
module Hakuban
|
7
|
+
|
8
|
+
|
9
|
+
class ObjectStateStream < FFIObject
|
10
|
+
|
11
|
+
private_class_method :new
|
12
|
+
|
13
|
+
|
14
|
+
def initialize_from_ffi_pointer(pointer)
|
15
|
+
super(pointer, :hakuban_object_state_stream_drop, nil)
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
|
20
|
+
include Stream
|
21
|
+
|
22
|
+
|
23
|
+
|
24
|
+
def next(&block)
|
25
|
+
process_item(
|
26
|
+
lambda { |pointer| FFI::hakuban_object_state_stream_next(pointer) },
|
27
|
+
lambda { |pointer| FFIObject::from_ffi_pointer(ObjectState, pointer) },
|
28
|
+
&block
|
29
|
+
)
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def descriptor
|
34
|
+
Hakuban::FFIObject.from_ffi_pointer(Hakuban::ObjectDescriptor, with_pointer { |pointer| FFI::hakuban_object_state_stream_descriptor(pointer) })
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ThreadExt
|
2
|
+
|
3
|
+
refine Thread do
|
4
|
+
|
5
|
+
def join_with_warning(timeout=60)
|
6
|
+
loop {
|
7
|
+
self.join(60)
|
8
|
+
return self.value if !self.status
|
9
|
+
$stderr.puts "Thread doesn't want to die: \n"+item_thread.backtrace.inspect+"\nat:\n"+caller.join("\n")
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
|
14
|
+
def Thread.process_interrupts
|
15
|
+
Thread.handle_interrupt(Object => :immediate) {
|
16
|
+
Thread.pass
|
17
|
+
}
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|