omq 0.17.9 → 0.19.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/CHANGELOG.md +99 -0
- data/README.md +7 -5
- data/lib/omq/engine/connection_lifecycle.rb +32 -8
- data/lib/omq/engine/reconnect.rb +4 -1
- data/lib/omq/engine/recv_pump.rb +29 -2
- data/lib/omq/engine/socket_lifecycle.rb +19 -1
- data/lib/omq/engine.rb +36 -1
- data/lib/omq/pair.rb +2 -2
- data/lib/omq/pub_sub.rb +8 -8
- data/lib/omq/push_pull.rb +4 -4
- data/lib/omq/queue_interface.rb +3 -1
- data/lib/omq/reactor.rb +41 -20
- data/lib/omq/readable.rb +3 -1
- data/lib/omq/req_rep.rb +4 -4
- data/lib/omq/router_dealer.rb +4 -4
- data/lib/omq/routing/conn_send_pump.rb +7 -3
- data/lib/omq/routing/dealer.rb +2 -0
- data/lib/omq/routing/fair_queue.rb +14 -3
- data/lib/omq/routing/fan_out.rb +41 -4
- data/lib/omq/routing/pair.rb +1 -1
- data/lib/omq/routing/req.rb +10 -1
- data/lib/omq/routing/round_robin.rb +1 -1
- data/lib/omq/routing.rb +5 -4
- data/lib/omq/socket.rb +44 -58
- data/lib/omq/transport/inproc/direct_pipe.rb +16 -2
- data/lib/omq/transport/inproc.rb +41 -7
- data/lib/omq/transport/ipc.rb +26 -7
- data/lib/omq/transport/tcp.rb +81 -27
- data/lib/omq/version.rb +1 -1
- data/lib/omq/writable.rb +5 -1
- data/lib/omq.rb +2 -1
- metadata +3 -3
data/lib/omq/reactor.rb
CHANGED
|
@@ -15,12 +15,15 @@ module OMQ
|
|
|
15
15
|
# thread are dispatched to the IO thread via {.run}.
|
|
16
16
|
#
|
|
17
17
|
module Reactor
|
|
18
|
+
THREAD_NAME = 'omq-io'
|
|
19
|
+
|
|
18
20
|
@mutex = Mutex.new
|
|
19
21
|
@thread = nil
|
|
20
22
|
@root_task = nil
|
|
21
23
|
@work_queue = nil
|
|
22
24
|
@lingers = Hash.new(0) # linger value → count of active sockets
|
|
23
25
|
|
|
26
|
+
|
|
24
27
|
class << self
|
|
25
28
|
# Returns the root Async task inside the shared IO thread.
|
|
26
29
|
# Starts the thread exactly once (double-checked lock).
|
|
@@ -29,15 +32,19 @@ module OMQ
|
|
|
29
32
|
#
|
|
30
33
|
def root_task
|
|
31
34
|
return @root_task if @root_task
|
|
35
|
+
|
|
32
36
|
@mutex.synchronize do
|
|
33
37
|
return @root_task if @root_task
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
@
|
|
37
|
-
@thread.
|
|
38
|
-
@
|
|
38
|
+
|
|
39
|
+
ready = Thread::Queue.new
|
|
40
|
+
@work_queue = Async::Queue.new
|
|
41
|
+
@thread = Thread.new { run_reactor(ready) }
|
|
42
|
+
@thread.name = THREAD_NAME
|
|
43
|
+
@root_task = ready.pop
|
|
44
|
+
|
|
39
45
|
at_exit { stop! }
|
|
40
46
|
end
|
|
47
|
+
|
|
41
48
|
@root_task
|
|
42
49
|
end
|
|
43
50
|
|
|
@@ -50,16 +57,20 @@ module OMQ
|
|
|
50
57
|
#
|
|
51
58
|
# @return [Object] the block's return value
|
|
52
59
|
#
|
|
53
|
-
def run(&block)
|
|
54
|
-
|
|
55
|
-
|
|
60
|
+
def run(timeout: nil, &block)
|
|
61
|
+
task = Async::Task.current?
|
|
62
|
+
|
|
63
|
+
if task
|
|
64
|
+
if timeout
|
|
65
|
+
task.with_timeout(timeout, IO::TimeoutError) { yield }
|
|
66
|
+
else
|
|
67
|
+
yield
|
|
68
|
+
end
|
|
56
69
|
else
|
|
57
|
-
result =
|
|
70
|
+
result = Async::Promise.new
|
|
58
71
|
root_task # ensure started
|
|
59
|
-
@work_queue.push([block, result])
|
|
60
|
-
|
|
61
|
-
raise value if status == :error
|
|
62
|
-
value
|
|
72
|
+
@work_queue.push([block, result, timeout])
|
|
73
|
+
result.wait
|
|
63
74
|
end
|
|
64
75
|
end
|
|
65
76
|
|
|
@@ -90,17 +101,21 @@ module OMQ
|
|
|
90
101
|
#
|
|
91
102
|
def stop!
|
|
92
103
|
return unless @thread&.alive?
|
|
104
|
+
|
|
93
105
|
max_linger = @lingers.empty? ? 0 : @lingers.keys.max
|
|
94
106
|
@work_queue&.push(nil)
|
|
95
107
|
@thread&.join(max_linger + 1)
|
|
108
|
+
|
|
96
109
|
@thread = nil
|
|
97
110
|
@root_task = nil
|
|
98
111
|
@work_queue = nil
|
|
99
112
|
@lingers = Hash.new(0)
|
|
100
113
|
end
|
|
101
114
|
|
|
115
|
+
|
|
102
116
|
private
|
|
103
117
|
|
|
118
|
+
|
|
104
119
|
# Runs the shared Async reactor.
|
|
105
120
|
#
|
|
106
121
|
# Processes work items dispatched via {.run} while engine
|
|
@@ -111,18 +126,24 @@ module OMQ
|
|
|
111
126
|
def run_reactor(ready)
|
|
112
127
|
Async do |task|
|
|
113
128
|
ready.push(task)
|
|
129
|
+
|
|
114
130
|
loop do
|
|
115
|
-
item = @work_queue.dequeue
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
task.async do
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
131
|
+
item = @work_queue.dequeue or break
|
|
132
|
+
block, result, timeout = item
|
|
133
|
+
|
|
134
|
+
task.async do |t|
|
|
135
|
+
if timeout
|
|
136
|
+
result.fulfill do
|
|
137
|
+
t.with_timeout(timeout, IO::TimeoutError) { block.call }
|
|
138
|
+
end
|
|
139
|
+
else
|
|
140
|
+
result.fulfill { block.call }
|
|
141
|
+
end
|
|
122
142
|
end
|
|
123
143
|
end
|
|
124
144
|
end
|
|
125
145
|
end
|
|
146
|
+
|
|
126
147
|
end
|
|
127
148
|
end
|
|
128
149
|
end
|
data/lib/omq/readable.rb
CHANGED
|
@@ -14,7 +14,9 @@ module OMQ
|
|
|
14
14
|
# @raise [IO::TimeoutError] if read_timeout exceeded
|
|
15
15
|
#
|
|
16
16
|
def receive
|
|
17
|
-
Reactor.run
|
|
17
|
+
Reactor.run timeout: @options.read_timeout do |task|
|
|
18
|
+
@engine.dequeue_recv
|
|
19
|
+
end
|
|
18
20
|
end
|
|
19
21
|
|
|
20
22
|
|
data/lib/omq/req_rep.rb
CHANGED
|
@@ -12,8 +12,8 @@ module OMQ
|
|
|
12
12
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
13
13
|
#
|
|
14
14
|
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
init_engine(:REQ, linger: linger, backend: backend)
|
|
16
|
+
attach_endpoints(endpoints, default: :connect)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -29,8 +29,8 @@ module OMQ
|
|
|
29
29
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
30
30
|
#
|
|
31
31
|
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
init_engine(:REP, linger: linger, backend: backend)
|
|
33
|
+
attach_endpoints(endpoints, default: :bind)
|
|
34
34
|
end
|
|
35
35
|
end
|
|
36
36
|
end
|
data/lib/omq/router_dealer.rb
CHANGED
|
@@ -12,8 +12,8 @@ module OMQ
|
|
|
12
12
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
13
13
|
#
|
|
14
14
|
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
init_engine(:DEALER, linger: linger, backend: backend)
|
|
16
|
+
attach_endpoints(endpoints, default: :connect)
|
|
17
17
|
end
|
|
18
18
|
end
|
|
19
19
|
|
|
@@ -29,8 +29,8 @@ module OMQ
|
|
|
29
29
|
# @param backend [Symbol, nil] :ruby (default) or :ffi
|
|
30
30
|
#
|
|
31
31
|
def initialize(endpoints = nil, linger: 0, backend: nil)
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
init_engine(:ROUTER, linger: linger, backend: backend)
|
|
33
|
+
attach_endpoints(endpoints, default: :bind)
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
|
|
@@ -21,15 +21,19 @@ module OMQ
|
|
|
21
21
|
loop do
|
|
22
22
|
batch = [q.dequeue]
|
|
23
23
|
Routing.drain_send_queue(q, batch)
|
|
24
|
+
|
|
24
25
|
if batch.size == 1
|
|
25
|
-
conn.write_message
|
|
26
|
+
conn.write_message batch.first
|
|
26
27
|
else
|
|
27
|
-
conn.write_messages
|
|
28
|
+
conn.write_messages batch
|
|
28
29
|
end
|
|
30
|
+
|
|
29
31
|
conn.flush
|
|
30
|
-
|
|
32
|
+
|
|
33
|
+
batch.each { |parts| engine.emit_verbose_msg_sent(conn, parts) }
|
|
31
34
|
end
|
|
32
35
|
end
|
|
36
|
+
|
|
33
37
|
tasks << task
|
|
34
38
|
task
|
|
35
39
|
end
|
data/lib/omq/routing/dealer.rb
CHANGED
|
@@ -10,6 +10,7 @@ module OMQ
|
|
|
10
10
|
include RoundRobin
|
|
11
11
|
include FairRecv
|
|
12
12
|
|
|
13
|
+
|
|
13
14
|
# @param engine [Engine]
|
|
14
15
|
#
|
|
15
16
|
def initialize(engine)
|
|
@@ -24,6 +25,7 @@ module OMQ
|
|
|
24
25
|
#
|
|
25
26
|
attr_reader :recv_queue
|
|
26
27
|
|
|
28
|
+
|
|
27
29
|
# @param connection [Connection]
|
|
28
30
|
#
|
|
29
31
|
def connection_added(connection)
|
|
@@ -104,8 +104,10 @@ module OMQ
|
|
|
104
104
|
@drain.empty? && @queues.all?(&:empty?)
|
|
105
105
|
end
|
|
106
106
|
|
|
107
|
+
|
|
107
108
|
private
|
|
108
109
|
|
|
110
|
+
|
|
109
111
|
# Drains orphaned queues first (preserves FIFO for disconnected
|
|
110
112
|
# peers), then tries each active queue once in round-robin order.
|
|
111
113
|
#
|
|
@@ -162,16 +164,25 @@ module OMQ
|
|
|
162
164
|
# @param timeout [Numeric, nil] dequeue timeout
|
|
163
165
|
# @return [Array<String>, nil]
|
|
164
166
|
#
|
|
165
|
-
def dequeue(timeout: nil)
|
|
167
|
+
def dequeue(timeout: nil)
|
|
168
|
+
@inner.dequeue(timeout: timeout)
|
|
169
|
+
end
|
|
170
|
+
|
|
166
171
|
|
|
167
172
|
# @return [Boolean]
|
|
168
173
|
#
|
|
169
|
-
def empty?
|
|
174
|
+
def empty?
|
|
175
|
+
@inner.empty?
|
|
176
|
+
end
|
|
177
|
+
|
|
170
178
|
|
|
171
179
|
# @param item [Object, nil]
|
|
172
180
|
# @return [void]
|
|
173
181
|
#
|
|
174
|
-
def push(item)
|
|
182
|
+
def push(item)
|
|
183
|
+
@inner.push(item)
|
|
184
|
+
end
|
|
185
|
+
|
|
175
186
|
end
|
|
176
187
|
end
|
|
177
188
|
end
|
data/lib/omq/routing/fan_out.rb
CHANGED
|
@@ -17,18 +17,25 @@ module OMQ
|
|
|
17
17
|
# their #initialize.
|
|
18
18
|
#
|
|
19
19
|
module FanOut
|
|
20
|
+
# Shared frozen empty binary string to avoid repeated allocations.
|
|
21
|
+
EMPTY_BINARY = ::Protocol::ZMTP::Codec::EMPTY_BINARY
|
|
22
|
+
|
|
23
|
+
|
|
20
24
|
# @return [Async::Promise] resolves when the first subscriber joins
|
|
21
25
|
#
|
|
22
26
|
attr_reader :subscriber_joined
|
|
23
27
|
|
|
28
|
+
|
|
24
29
|
# @return [Boolean] true when all per-connection send queues are empty
|
|
25
30
|
#
|
|
26
31
|
def send_queues_drained?
|
|
27
32
|
@conn_queues.values.all?(&:empty?)
|
|
28
33
|
end
|
|
29
34
|
|
|
35
|
+
|
|
30
36
|
private
|
|
31
37
|
|
|
38
|
+
|
|
32
39
|
def init_fan_out(engine)
|
|
33
40
|
@connections = Set.new
|
|
34
41
|
@subscriptions = {} # connection => Set of prefixes
|
|
@@ -156,6 +163,15 @@ module OMQ
|
|
|
156
163
|
end
|
|
157
164
|
|
|
158
165
|
|
|
166
|
+
# Send pump variant for non-conflate fan-out: dequeues, batch-drains,
|
|
167
|
+
# writes each subscribed message, then flushes once.
|
|
168
|
+
#
|
|
169
|
+
# @param conn [Connection]
|
|
170
|
+
# @param q [Async::LimitedQueue, DropQueue]
|
|
171
|
+
# @param use_wire [Boolean] true iff the encoded wire bytes can
|
|
172
|
+
# be shared across peers (unencrypted ZMTP)
|
|
173
|
+
# @return [Async::Task]
|
|
174
|
+
#
|
|
159
175
|
def start_conn_send_pump_normal(conn, q, use_wire)
|
|
160
176
|
@engine.spawn_conn_pump_task(conn, annotation: "send pump") do
|
|
161
177
|
loop do
|
|
@@ -163,38 +179,59 @@ module OMQ
|
|
|
163
179
|
Routing.drain_send_queue(q, batch)
|
|
164
180
|
if write_matching_batch(conn, batch, use_wire)
|
|
165
181
|
conn.flush
|
|
166
|
-
batch.each { |parts| @engine.
|
|
182
|
+
batch.each { |parts| @engine.emit_verbose_msg_sent(conn, parts) }
|
|
167
183
|
end
|
|
168
184
|
end
|
|
169
185
|
end
|
|
170
186
|
end
|
|
171
187
|
|
|
172
188
|
|
|
189
|
+
# Writes every batch entry whose topic matches a subscription on
|
|
190
|
+
# +conn+ to +conn+'s write buffer. Does not flush.
|
|
191
|
+
#
|
|
192
|
+
# @return [Boolean] true iff at least one message was written
|
|
193
|
+
#
|
|
173
194
|
def write_matching_batch(conn, batch, use_wire)
|
|
174
195
|
sent = false
|
|
175
196
|
batch.each do |parts|
|
|
176
197
|
next unless subscribed?(conn, parts.first || EMPTY_BINARY)
|
|
177
|
-
use_wire
|
|
198
|
+
if use_wire
|
|
199
|
+
conn.write_wire(Protocol::ZMTP::Codec::Frame.encode_message(parts))
|
|
200
|
+
else
|
|
201
|
+
conn.write_message(parts)
|
|
202
|
+
end
|
|
178
203
|
sent = true
|
|
179
204
|
end
|
|
180
205
|
sent
|
|
181
206
|
end
|
|
182
207
|
|
|
183
208
|
|
|
209
|
+
# Send pump variant for conflate mode: keeps only the latest
|
|
210
|
+
# subscribed message per batch. Stale duplicates are dropped.
|
|
211
|
+
#
|
|
212
|
+
# @param conn [Connection]
|
|
213
|
+
# @param q [Async::LimitedQueue, DropQueue]
|
|
214
|
+
# @return [Async::Task]
|
|
215
|
+
#
|
|
184
216
|
def start_conn_send_pump_conflate(conn, q)
|
|
185
217
|
@engine.spawn_conn_pump_task(conn, annotation: "send pump") do
|
|
186
218
|
loop do
|
|
187
219
|
batch = [q.dequeue]
|
|
188
220
|
Routing.drain_send_queue(q, batch)
|
|
221
|
+
|
|
189
222
|
# Keep only the latest message that matches the subscription.
|
|
190
|
-
latest = batch.reverse.find
|
|
223
|
+
latest = batch.reverse.find do |parts|
|
|
224
|
+
subscribed?(conn, parts.first || EMPTY_BINARY)
|
|
225
|
+
end
|
|
191
226
|
next unless latest
|
|
227
|
+
|
|
192
228
|
conn.write_message(latest)
|
|
193
229
|
conn.flush
|
|
194
|
-
@engine.
|
|
230
|
+
@engine.emit_verbose_msg_sent(conn, latest)
|
|
195
231
|
end
|
|
196
232
|
end
|
|
197
233
|
end
|
|
234
|
+
|
|
198
235
|
end
|
|
199
236
|
end
|
|
200
237
|
end
|
data/lib/omq/routing/pair.rb
CHANGED
|
@@ -96,7 +96,7 @@ module OMQ
|
|
|
96
96
|
conn.write_messages(batch)
|
|
97
97
|
end
|
|
98
98
|
conn.flush
|
|
99
|
-
batch.each { |parts| @engine.
|
|
99
|
+
batch.each { |parts| @engine.emit_verbose_msg_sent(conn, parts) }
|
|
100
100
|
end
|
|
101
101
|
end
|
|
102
102
|
@tasks << @send_pump
|
data/lib/omq/routing/req.rb
CHANGED
|
@@ -10,6 +10,10 @@ module OMQ
|
|
|
10
10
|
include RoundRobin
|
|
11
11
|
include FairRecv
|
|
12
12
|
|
|
13
|
+
# Shared frozen empty binary string to avoid repeated allocations.
|
|
14
|
+
EMPTY_BINARY = ::Protocol::ZMTP::Codec::EMPTY_BINARY
|
|
15
|
+
|
|
16
|
+
|
|
13
17
|
# @param engine [Engine]
|
|
14
18
|
#
|
|
15
19
|
def initialize(engine)
|
|
@@ -64,11 +68,16 @@ module OMQ
|
|
|
64
68
|
@tasks.clear
|
|
65
69
|
end
|
|
66
70
|
|
|
71
|
+
|
|
67
72
|
private
|
|
68
73
|
|
|
74
|
+
|
|
69
75
|
# REQ prepends empty delimiter frame on the wire.
|
|
70
76
|
#
|
|
71
|
-
def transform_send(parts)
|
|
77
|
+
def transform_send(parts)
|
|
78
|
+
[EMPTY_BINARY, *parts]
|
|
79
|
+
end
|
|
80
|
+
|
|
72
81
|
end
|
|
73
82
|
end
|
|
74
83
|
end
|
|
@@ -134,7 +134,7 @@ module OMQ
|
|
|
134
134
|
ensure
|
|
135
135
|
@in_flight -= batch.size
|
|
136
136
|
end
|
|
137
|
-
batch.each { |parts| @engine.
|
|
137
|
+
batch.each { |parts| @engine.emit_verbose_msg_sent(conn, parts) }
|
|
138
138
|
Async::Task.current.yield
|
|
139
139
|
end
|
|
140
140
|
end
|
data/lib/omq/routing.rb
CHANGED
|
@@ -15,16 +15,17 @@ module OMQ
|
|
|
15
15
|
# the socket's send/recv queues.
|
|
16
16
|
#
|
|
17
17
|
module Routing
|
|
18
|
-
# Shared frozen empty binary string to avoid repeated allocations.
|
|
19
|
-
EMPTY_BINARY = "".b.freeze
|
|
20
|
-
|
|
21
|
-
|
|
22
18
|
# Plugin registry for socket types not built into omq.
|
|
23
19
|
# Populated by sister gems via +Routing.register+.
|
|
24
20
|
#
|
|
25
21
|
@registry = {}
|
|
26
22
|
|
|
23
|
+
|
|
27
24
|
class << self
|
|
25
|
+
# @return [Hash{Symbol => Class}] plugin registry
|
|
26
|
+
attr_reader :registry
|
|
27
|
+
|
|
28
|
+
|
|
28
29
|
# Registers a routing strategy class for a socket type.
|
|
29
30
|
# Called by omq-draft (and other plugins) at require time.
|
|
30
31
|
#
|
data/lib/omq/socket.rb
CHANGED
|
@@ -88,7 +88,7 @@ module OMQ
|
|
|
88
88
|
def bind(endpoint, parent: nil)
|
|
89
89
|
ensure_parent_task(parent: parent)
|
|
90
90
|
Reactor.run do
|
|
91
|
-
@engine.bind(endpoint)
|
|
91
|
+
@engine.bind(endpoint) # TODO: use timeout?
|
|
92
92
|
@last_tcp_port = @engine.last_tcp_port
|
|
93
93
|
end
|
|
94
94
|
end
|
|
@@ -102,7 +102,7 @@ module OMQ
|
|
|
102
102
|
#
|
|
103
103
|
def connect(endpoint, parent: nil)
|
|
104
104
|
ensure_parent_task(parent: parent)
|
|
105
|
-
Reactor.run { @engine.connect(endpoint) }
|
|
105
|
+
Reactor.run { @engine.connect(endpoint) } # TODO: use timeout?
|
|
106
106
|
end
|
|
107
107
|
|
|
108
108
|
|
|
@@ -112,7 +112,7 @@ module OMQ
|
|
|
112
112
|
# @return [void]
|
|
113
113
|
#
|
|
114
114
|
def disconnect(endpoint)
|
|
115
|
-
Reactor.run { @engine.disconnect(endpoint) }
|
|
115
|
+
Reactor.run { @engine.disconnect(endpoint) } # TODO: use timeout?
|
|
116
116
|
end
|
|
117
117
|
|
|
118
118
|
|
|
@@ -122,7 +122,7 @@ module OMQ
|
|
|
122
122
|
# @return [void]
|
|
123
123
|
#
|
|
124
124
|
def unbind(endpoint)
|
|
125
|
-
Reactor.run { @engine.unbind(endpoint) }
|
|
125
|
+
Reactor.run { @engine.unbind(endpoint) } # TODO: use timeout?
|
|
126
126
|
end
|
|
127
127
|
|
|
128
128
|
|
|
@@ -183,9 +183,11 @@ module OMQ
|
|
|
183
183
|
#
|
|
184
184
|
def monitor(verbose: false, &block)
|
|
185
185
|
ensure_parent_task
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
|
|
187
|
+
queue = Async::LimitedQueue.new(64)
|
|
188
|
+
@engine.monitor_queue = queue
|
|
188
189
|
@engine.verbose_monitor = verbose
|
|
190
|
+
|
|
189
191
|
Reactor.run do
|
|
190
192
|
@engine.parent_task.async(transient: true, annotation: "monitor") do
|
|
191
193
|
while (event = queue.dequeue)
|
|
@@ -218,7 +220,7 @@ module OMQ
|
|
|
218
220
|
# @return [nil]
|
|
219
221
|
#
|
|
220
222
|
def close
|
|
221
|
-
Reactor.run { @engine.close }
|
|
223
|
+
Reactor.run { @engine.close } # TODO: use timeout?
|
|
222
224
|
nil
|
|
223
225
|
end
|
|
224
226
|
|
|
@@ -230,7 +232,7 @@ module OMQ
|
|
|
230
232
|
# @return [nil]
|
|
231
233
|
#
|
|
232
234
|
def stop
|
|
233
|
-
Reactor.run { @engine.stop }
|
|
235
|
+
Reactor.run { @engine.stop } # TODO: use timeout?
|
|
234
236
|
nil
|
|
235
237
|
end
|
|
236
238
|
|
|
@@ -253,42 +255,13 @@ module OMQ
|
|
|
253
255
|
end
|
|
254
256
|
|
|
255
257
|
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
# Runs a block with a timeout. Uses Async's with_timeout if inside
|
|
260
|
-
# a reactor, otherwise falls back to Timeout.timeout.
|
|
261
|
-
#
|
|
262
|
-
# @param seconds [Numeric]
|
|
263
|
-
# @raise [IO::TimeoutError]
|
|
264
|
-
#
|
|
265
|
-
def with_timeout(seconds, &block)
|
|
266
|
-
return yield if seconds.nil?
|
|
267
|
-
if Async::Task.current?
|
|
268
|
-
Async::Task.current.with_timeout(seconds, &block)
|
|
269
|
-
else
|
|
270
|
-
Timeout.timeout(seconds, &block)
|
|
271
|
-
end
|
|
272
|
-
rescue Async::TimeoutError, Timeout::Error
|
|
273
|
-
raise IO::TimeoutError, "timed out"
|
|
274
|
-
end
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
# Sets the engine's parent task before the first bind or connect.
|
|
278
|
-
# Must be called OUTSIDE Reactor.run so that non-Async callers
|
|
279
|
-
# get the IO thread's root task, not an ephemeral work task.
|
|
280
|
-
#
|
|
281
|
-
def ensure_parent_task(parent: nil)
|
|
282
|
-
@engine.capture_parent_task(parent: parent)
|
|
283
|
-
end
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
# Connects or binds based on endpoint prefix convention.
|
|
258
|
+
# Connects or binds based on endpoint prefix convention. Called
|
|
259
|
+
# from subclass initializers (including out-of-tree socket types).
|
|
287
260
|
#
|
|
288
261
|
# @param endpoints [String, nil]
|
|
289
262
|
# @param default [Symbol] :connect or :bind
|
|
290
263
|
#
|
|
291
|
-
def
|
|
264
|
+
def attach_endpoints(endpoints, default:)
|
|
292
265
|
return unless endpoints
|
|
293
266
|
case endpoints
|
|
294
267
|
when /\A@(.+)\z/
|
|
@@ -301,29 +274,42 @@ module OMQ
|
|
|
301
274
|
end
|
|
302
275
|
|
|
303
276
|
|
|
304
|
-
# Initializes engine and options for a socket type.
|
|
277
|
+
# Initializes engine and options for a socket type. Called from
|
|
278
|
+
# subclass initializers (including out-of-tree socket types).
|
|
305
279
|
#
|
|
306
280
|
# @param socket_type [Symbol]
|
|
307
281
|
# @param linger [Integer]
|
|
308
282
|
#
|
|
309
|
-
def
|
|
310
|
-
|
|
311
|
-
|
|
283
|
+
def init_engine(socket_type, linger:, send_hwm: nil, recv_hwm: nil,
|
|
284
|
+
send_timeout: nil, recv_timeout: nil, conflate: false,
|
|
285
|
+
on_mute: nil, backend: nil)
|
|
312
286
|
@options = Options.new(linger: linger)
|
|
313
|
-
@options.send_hwm
|
|
314
|
-
@options.recv_hwm
|
|
315
|
-
@options.send_timeout
|
|
316
|
-
@options.recv_timeout
|
|
317
|
-
@options.conflate
|
|
318
|
-
@options.on_mute
|
|
319
|
-
@engine
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
287
|
+
@options.send_hwm = send_hwm if send_hwm
|
|
288
|
+
@options.recv_hwm = recv_hwm if recv_hwm
|
|
289
|
+
@options.send_timeout = send_timeout if send_timeout
|
|
290
|
+
@options.recv_timeout = recv_timeout if recv_timeout
|
|
291
|
+
@options.conflate = conflate
|
|
292
|
+
@options.on_mute = on_mute if on_mute
|
|
293
|
+
@engine = case backend
|
|
294
|
+
when nil, :ruby
|
|
295
|
+
Engine.new(socket_type, @options)
|
|
296
|
+
when :ffi
|
|
297
|
+
FFI::Engine.new(socket_type, @options)
|
|
298
|
+
else
|
|
299
|
+
raise ArgumentError, "unknown backend: #{backend}"
|
|
300
|
+
end
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
|
|
304
|
+
private
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
# Sets the engine's parent task before the first bind or connect.
|
|
308
|
+
# Must be called OUTSIDE Reactor.run so that non-Async callers
|
|
309
|
+
# get the IO thread's root task, not an ephemeral work task.
|
|
310
|
+
#
|
|
311
|
+
def ensure_parent_task(parent: nil)
|
|
312
|
+
@engine.capture_parent_task(parent: parent)
|
|
327
313
|
end
|
|
328
314
|
end
|
|
329
315
|
end
|
|
@@ -112,6 +112,7 @@ module OMQ
|
|
|
112
112
|
#
|
|
113
113
|
def encrypted? = false
|
|
114
114
|
|
|
115
|
+
|
|
115
116
|
# No-op — inproc has no IO buffer to flush.
|
|
116
117
|
#
|
|
117
118
|
# @return [nil]
|
|
@@ -127,11 +128,16 @@ module OMQ
|
|
|
127
128
|
def receive_message
|
|
128
129
|
loop do
|
|
129
130
|
item = @receive_queue.dequeue
|
|
131
|
+
|
|
130
132
|
raise EOFError, "connection closed" if item.nil?
|
|
133
|
+
|
|
131
134
|
if item.is_a?(Array) && item.first == :command
|
|
132
|
-
|
|
135
|
+
if block_given?
|
|
136
|
+
yield Protocol::ZMTP::Codec::Frame.new(item[1].to_body, command: true)
|
|
137
|
+
end
|
|
133
138
|
next
|
|
134
139
|
end
|
|
140
|
+
|
|
135
141
|
return item
|
|
136
142
|
end
|
|
137
143
|
end
|
|
@@ -157,6 +163,7 @@ module OMQ
|
|
|
157
163
|
loop do
|
|
158
164
|
item = @receive_queue.dequeue
|
|
159
165
|
raise EOFError, "connection closed" if item.nil?
|
|
166
|
+
|
|
160
167
|
if item.is_a?(Array) && item.first == :command
|
|
161
168
|
return Protocol::ZMTP::Codec::Frame.new(item[1].to_body, command: true)
|
|
162
169
|
end
|
|
@@ -174,11 +181,18 @@ module OMQ
|
|
|
174
181
|
@send_queue&.enqueue(nil) # close sentinel
|
|
175
182
|
end
|
|
176
183
|
|
|
184
|
+
|
|
177
185
|
private
|
|
178
186
|
|
|
187
|
+
|
|
179
188
|
def apply_transform(parts)
|
|
180
|
-
|
|
189
|
+
if @direct_recv_transform
|
|
190
|
+
@direct_recv_transform.call(parts).freeze
|
|
191
|
+
else
|
|
192
|
+
parts
|
|
193
|
+
end
|
|
181
194
|
end
|
|
195
|
+
|
|
182
196
|
end
|
|
183
197
|
end
|
|
184
198
|
end
|