omq 0.19.2 → 0.19.3
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 +18 -0
- data/lib/omq/engine/recv_pump.rb +6 -2
- data/lib/omq/engine.rb +60 -13
- data/lib/omq/routing/conn_send_pump.rb +1 -0
- data/lib/omq/routing/dealer.rb +6 -5
- data/lib/omq/routing/fair_queue.rb +5 -2
- data/lib/omq/routing/fair_recv.rb +28 -2
- data/lib/omq/routing/fan_out.rb +14 -1
- data/lib/omq/routing/pair.rb +15 -5
- data/lib/omq/routing/pub.rb +12 -1
- data/lib/omq/routing/pull.rb +4 -0
- data/lib/omq/routing/push.rb +14 -1
- data/lib/omq/routing/rep.rb +7 -5
- data/lib/omq/routing/req.rb +5 -5
- data/lib/omq/routing/round_robin.rb +17 -5
- data/lib/omq/routing/router.rb +7 -4
- data/lib/omq/routing/sub.rb +16 -2
- data/lib/omq/routing/xpub.rb +18 -2
- data/lib/omq/routing/xsub.rb +25 -3
- data/lib/omq/routing.rb +1 -0
- data/lib/omq/single_frame.rb +1 -0
- data/lib/omq/socket.rb +38 -28
- data/lib/omq/transport/inproc/direct_pipe.rb +6 -2
- data/lib/omq/transport/tcp.rb +1 -0
- data/lib/omq/version.rb +1 -1
- data/lib/omq/writable.rb +28 -19
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 77ae2b0a529e29f7c7c48478db4b9b0d81955b28dca48cd17c4f5dfb1dbfd118
|
|
4
|
+
data.tar.gz: 2ec4f56b9893527d6458e366835878902d3ec254893de2fa0e7579d52be73438
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: af73f9a2031c776167a616fe2e63775cd036c6f02f24cdb65059a4ffb81572e2747a9a4f662b96bd7574ecac3d48114f7ea3569bfbc1f6f3bc0f571a523df1ca
|
|
7
|
+
data.tar.gz: e919e6bce582848096c692a76e63081b038fb77a9796e914f2265c1825d36017278d46b014b639bbc100935be3ae82af1ac1bfde29951e49e2643ac942d6c251
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.19.3 — 2026-04-13
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Engine no longer reaches into `routing.recv_queue` directly.
|
|
8
|
+
Routing strategies now expose `#dequeue_recv` and `#unblock_recv`
|
|
9
|
+
as the engine-facing recv contract. `FairRecv` provides the
|
|
10
|
+
shared implementation for fair-queued sockets; sub/xsub/xpub
|
|
11
|
+
delegate inline; write-only push/pub raise on dequeue and no-op
|
|
12
|
+
on unblock. Sharpens the routing interface and keeps Engine out
|
|
13
|
+
of queue internals.
|
|
14
|
+
- `Writable#freeze_message` collapsed: single `all?` predicate
|
|
15
|
+
check drives three outcomes (already-frozen-array fast path,
|
|
16
|
+
freeze-in-place, convert-via-map/map!) instead of mirrored
|
|
17
|
+
fast/slow branches that each repeated the predicate.
|
|
18
|
+
- Hot-path optimized. Avoid the overhead of `parts.sum(&:bytesize)`
|
|
19
|
+
and use `parts.sum { |p| p.bytesize }` instead.
|
|
20
|
+
|
|
3
21
|
## 0.19.2 — 2026-04-13
|
|
4
22
|
|
|
5
23
|
### Added
|
data/lib/omq/engine/recv_pump.rb
CHANGED
|
@@ -91,18 +91,22 @@ module OMQ
|
|
|
91
91
|
loop do
|
|
92
92
|
count = 0
|
|
93
93
|
bytes = 0
|
|
94
|
+
|
|
94
95
|
while count < FAIRNESS_MESSAGES && bytes < FAIRNESS_BYTES
|
|
95
96
|
msg = conn.receive_message
|
|
96
97
|
msg = transform.call(msg).freeze
|
|
98
|
+
|
|
97
99
|
# Emit the verbose trace BEFORE enqueueing so the monitor
|
|
98
100
|
# fiber is woken before the application fiber -- the
|
|
99
101
|
# async scheduler is FIFO on the ready list, so this
|
|
100
102
|
# preserves log-before-stdout ordering for -vvv traces.
|
|
101
103
|
engine.emit_verbose_msg_received(conn, msg)
|
|
102
104
|
recv_queue.enqueue(msg)
|
|
105
|
+
|
|
103
106
|
count += 1
|
|
104
|
-
bytes += msg.sum
|
|
107
|
+
bytes += msg.sum { |part| part.bytesize } if count_bytes
|
|
105
108
|
end
|
|
109
|
+
|
|
106
110
|
task.yield
|
|
107
111
|
end
|
|
108
112
|
rescue Async::Stop, Async::Cancel
|
|
@@ -133,7 +137,7 @@ module OMQ
|
|
|
133
137
|
engine.emit_verbose_msg_received(conn, msg)
|
|
134
138
|
recv_queue.enqueue(msg)
|
|
135
139
|
count += 1
|
|
136
|
-
bytes += msg.sum
|
|
140
|
+
bytes += msg.sum { |part| part.bytesize } if count_bytes
|
|
137
141
|
end
|
|
138
142
|
task.yield
|
|
139
143
|
end
|
data/lib/omq/engine.rb
CHANGED
|
@@ -80,6 +80,7 @@ module OMQ
|
|
|
80
80
|
#
|
|
81
81
|
attr_reader :connections, :tasks, :lifecycle
|
|
82
82
|
|
|
83
|
+
|
|
83
84
|
# @!attribute [w] monitor_queue
|
|
84
85
|
# @param value [Async::Queue, nil] queue for monitor events
|
|
85
86
|
#
|
|
@@ -116,7 +117,9 @@ module OMQ
|
|
|
116
117
|
def spawn_inproc_retry(endpoint)
|
|
117
118
|
ri = @options.reconnect_interval
|
|
118
119
|
ivl = ri.is_a?(Range) ? ri.begin : ri
|
|
119
|
-
|
|
120
|
+
ann = "inproc reconnect #{endpoint}"
|
|
121
|
+
|
|
122
|
+
@tasks << @lifecycle.barrier.async(transient: true, annotation: ann) do
|
|
120
123
|
yield ivl
|
|
121
124
|
rescue Async::Stop, Async::Cancel
|
|
122
125
|
end
|
|
@@ -134,7 +137,9 @@ module OMQ
|
|
|
134
137
|
capture_parent_task(parent: parent)
|
|
135
138
|
transport = transport_for(endpoint)
|
|
136
139
|
listener = transport.bind(endpoint, self)
|
|
140
|
+
|
|
137
141
|
start_accept_loops(listener)
|
|
142
|
+
|
|
138
143
|
@listeners << listener
|
|
139
144
|
@last_endpoint = listener.endpoint
|
|
140
145
|
@last_tcp_port = listener.respond_to?(:port) ? listener.port : nil
|
|
@@ -155,6 +160,7 @@ module OMQ
|
|
|
155
160
|
capture_parent_task(parent: parent)
|
|
156
161
|
validate_endpoint!(endpoint)
|
|
157
162
|
@dialed.add(endpoint)
|
|
163
|
+
|
|
158
164
|
if endpoint.start_with?("inproc://")
|
|
159
165
|
# Inproc connect is synchronous and instant
|
|
160
166
|
transport = transport_for(endpoint)
|
|
@@ -188,6 +194,7 @@ module OMQ
|
|
|
188
194
|
def unbind(endpoint)
|
|
189
195
|
listener = @listeners.find { |l| l.endpoint == endpoint }
|
|
190
196
|
return unless listener
|
|
197
|
+
|
|
191
198
|
listener.stop
|
|
192
199
|
@listeners.delete(listener)
|
|
193
200
|
close_connections_at(endpoint)
|
|
@@ -235,8 +242,10 @@ module OMQ
|
|
|
235
242
|
#
|
|
236
243
|
def dequeue_recv
|
|
237
244
|
raise @fatal_error if @fatal_error
|
|
238
|
-
|
|
245
|
+
|
|
246
|
+
msg = routing.dequeue_recv
|
|
239
247
|
raise @fatal_error if msg.nil? && @fatal_error
|
|
248
|
+
|
|
240
249
|
msg
|
|
241
250
|
end
|
|
242
251
|
|
|
@@ -245,7 +254,7 @@ module OMQ
|
|
|
245
254
|
# pending {#dequeue_recv} with a nil return value.
|
|
246
255
|
#
|
|
247
256
|
def dequeue_recv_sentinel
|
|
248
|
-
routing.
|
|
257
|
+
routing.unblock_recv
|
|
249
258
|
end
|
|
250
259
|
|
|
251
260
|
|
|
@@ -274,6 +283,7 @@ module OMQ
|
|
|
274
283
|
# pumps when the connection is lost.
|
|
275
284
|
parent = @connections[conn]&.barrier || @lifecycle.barrier
|
|
276
285
|
task = RecvPump.start(parent, conn, recv_queue, self, transform)
|
|
286
|
+
|
|
277
287
|
@tasks << task if task
|
|
278
288
|
task
|
|
279
289
|
end
|
|
@@ -316,11 +326,20 @@ module OMQ
|
|
|
316
326
|
#
|
|
317
327
|
def close
|
|
318
328
|
return unless @lifecycle.open?
|
|
329
|
+
|
|
319
330
|
@lifecycle.start_closing!
|
|
320
331
|
stop_listeners unless @connections.empty?
|
|
321
|
-
|
|
332
|
+
|
|
333
|
+
if @options.linger.nil? || @options.linger > 0
|
|
334
|
+
drain_send_queues(@options.linger)
|
|
335
|
+
end
|
|
336
|
+
|
|
322
337
|
@lifecycle.finish_closing!
|
|
323
|
-
|
|
338
|
+
|
|
339
|
+
if @lifecycle.on_io_thread
|
|
340
|
+
Reactor.untrack_linger(@options.linger)
|
|
341
|
+
end
|
|
342
|
+
|
|
324
343
|
stop_listeners
|
|
325
344
|
tear_down_barrier
|
|
326
345
|
routing.stop rescue nil
|
|
@@ -337,9 +356,14 @@ module OMQ
|
|
|
337
356
|
#
|
|
338
357
|
def stop
|
|
339
358
|
return unless @lifecycle.alive?
|
|
359
|
+
|
|
340
360
|
@lifecycle.start_closing! if @lifecycle.open?
|
|
341
361
|
@lifecycle.finish_closing!
|
|
342
|
-
|
|
362
|
+
|
|
363
|
+
if @lifecycle.on_io_thread
|
|
364
|
+
Reactor.untrack_linger(@options.linger)
|
|
365
|
+
end
|
|
366
|
+
|
|
343
367
|
stop_listeners
|
|
344
368
|
tear_down_barrier
|
|
345
369
|
routing.stop rescue nil
|
|
@@ -407,8 +431,9 @@ module OMQ
|
|
|
407
431
|
#
|
|
408
432
|
def signal_fatal_error(error)
|
|
409
433
|
return unless @lifecycle.open?
|
|
434
|
+
|
|
410
435
|
@fatal_error = build_fatal_error(error)
|
|
411
|
-
routing.
|
|
436
|
+
routing.unblock_recv rescue nil
|
|
412
437
|
@lifecycle.peer_connected.resolve(nil) rescue nil
|
|
413
438
|
end
|
|
414
439
|
|
|
@@ -442,7 +467,10 @@ module OMQ
|
|
|
442
467
|
# @param parent [#async, nil] optional Async parent
|
|
443
468
|
#
|
|
444
469
|
def capture_parent_task(parent: nil)
|
|
445
|
-
|
|
470
|
+
task = @lifecycle.capture_parent_task(parent: parent, linger: @options.linger)
|
|
471
|
+
|
|
472
|
+
return unless task
|
|
473
|
+
|
|
446
474
|
Maintenance.start(@lifecycle.barrier, @options.mechanism, @tasks)
|
|
447
475
|
end
|
|
448
476
|
|
|
@@ -491,8 +519,13 @@ module OMQ
|
|
|
491
519
|
# +last_wire_size_in+.
|
|
492
520
|
def emit_verbose_msg_received(conn, parts)
|
|
493
521
|
return unless @verbose_monitor
|
|
522
|
+
|
|
494
523
|
detail = { parts: parts }
|
|
495
|
-
|
|
524
|
+
|
|
525
|
+
if conn.respond_to?(:last_wire_size_in)
|
|
526
|
+
detail[:wire_size] = conn.last_wire_size_in
|
|
527
|
+
end
|
|
528
|
+
|
|
496
529
|
emit_monitor_event(:message_received, detail: detail)
|
|
497
530
|
end
|
|
498
531
|
|
|
@@ -504,9 +537,14 @@ module OMQ
|
|
|
504
537
|
# @raise [ArgumentError] if the scheme is not registered
|
|
505
538
|
#
|
|
506
539
|
def transport_for(endpoint)
|
|
507
|
-
scheme
|
|
508
|
-
self.class.transports[scheme]
|
|
540
|
+
scheme = endpoint[/\A([^:]+):\/\//, 1]
|
|
541
|
+
transport = self.class.transports[scheme]
|
|
542
|
+
|
|
543
|
+
unless transport
|
|
509
544
|
raise ArgumentError, "unsupported transport: #{endpoint}"
|
|
545
|
+
end
|
|
546
|
+
|
|
547
|
+
transport
|
|
510
548
|
end
|
|
511
549
|
|
|
512
550
|
|
|
@@ -528,6 +566,7 @@ module OMQ
|
|
|
528
566
|
ensure
|
|
529
567
|
lifecycle&.close!
|
|
530
568
|
end
|
|
569
|
+
|
|
531
570
|
@tasks << task if task
|
|
532
571
|
end
|
|
533
572
|
|
|
@@ -539,7 +578,11 @@ module OMQ
|
|
|
539
578
|
# every routing strategy, so it is flagged rather than fixed here.
|
|
540
579
|
def drain_send_queues(timeout)
|
|
541
580
|
return unless @routing.respond_to?(:send_queues_drained?)
|
|
542
|
-
|
|
581
|
+
|
|
582
|
+
if timeout
|
|
583
|
+
deadline = Async::Clock.now + timeout
|
|
584
|
+
end
|
|
585
|
+
|
|
543
586
|
until @routing.send_queues_drained?
|
|
544
587
|
break if deadline && (deadline - Async::Clock.now) <= 0
|
|
545
588
|
sleep 0.001
|
|
@@ -554,12 +597,16 @@ module OMQ
|
|
|
554
597
|
|
|
555
598
|
def validate_endpoint!(endpoint)
|
|
556
599
|
transport = transport_for(endpoint)
|
|
557
|
-
|
|
600
|
+
|
|
601
|
+
if transport.respond_to?(:validate_endpoint!)
|
|
602
|
+
transport.validate_endpoint!(endpoint)
|
|
603
|
+
end
|
|
558
604
|
end
|
|
559
605
|
|
|
560
606
|
|
|
561
607
|
def start_accept_loops(listener)
|
|
562
608
|
return unless listener.respond_to?(:start_accept_loops)
|
|
609
|
+
|
|
563
610
|
listener.start_accept_loops(@lifecycle.barrier) do |io|
|
|
564
611
|
handle_accepted(io, endpoint: listener.endpoint)
|
|
565
612
|
end
|
data/lib/omq/routing/dealer.rb
CHANGED
|
@@ -11,6 +11,11 @@ module OMQ
|
|
|
11
11
|
include FairRecv
|
|
12
12
|
|
|
13
13
|
|
|
14
|
+
# @return [FairQueue]
|
|
15
|
+
#
|
|
16
|
+
attr_reader :recv_queue
|
|
17
|
+
|
|
18
|
+
|
|
14
19
|
# @param engine [Engine]
|
|
15
20
|
#
|
|
16
21
|
def initialize(engine)
|
|
@@ -21,11 +26,6 @@ module OMQ
|
|
|
21
26
|
end
|
|
22
27
|
|
|
23
28
|
|
|
24
|
-
# @return [FairQueue]
|
|
25
|
-
#
|
|
26
|
-
attr_reader :recv_queue
|
|
27
|
-
|
|
28
|
-
|
|
29
29
|
# @param connection [Connection]
|
|
30
30
|
#
|
|
31
31
|
def connection_added(connection)
|
|
@@ -58,6 +58,7 @@ module OMQ
|
|
|
58
58
|
@tasks.each(&:stop)
|
|
59
59
|
@tasks.clear
|
|
60
60
|
end
|
|
61
|
+
|
|
61
62
|
end
|
|
62
63
|
end
|
|
63
64
|
end
|
|
@@ -74,7 +74,9 @@ module OMQ
|
|
|
74
74
|
return try_dequeue if timeout == 0
|
|
75
75
|
|
|
76
76
|
loop do
|
|
77
|
-
|
|
77
|
+
if @closed && @drain.empty? && @queues.all? { |q| q.empty? }
|
|
78
|
+
return nil
|
|
79
|
+
end
|
|
78
80
|
|
|
79
81
|
msg = try_dequeue
|
|
80
82
|
return msg if msg
|
|
@@ -101,7 +103,7 @@ module OMQ
|
|
|
101
103
|
# @return [Boolean]
|
|
102
104
|
#
|
|
103
105
|
def empty?
|
|
104
|
-
@drain.empty? && @queues.all?
|
|
106
|
+
@drain.empty? && @queues.all? { |q| q.empty? }
|
|
105
107
|
end
|
|
106
108
|
|
|
107
109
|
|
|
@@ -132,6 +134,7 @@ module OMQ
|
|
|
132
134
|
msg = q.dequeue(timeout: 0)
|
|
133
135
|
return msg if msg
|
|
134
136
|
end
|
|
137
|
+
|
|
135
138
|
nil
|
|
136
139
|
end
|
|
137
140
|
end
|
|
@@ -7,6 +7,26 @@ module OMQ
|
|
|
7
7
|
# Including classes must have @engine, @recv_queue (FairQueue), and @tasks.
|
|
8
8
|
#
|
|
9
9
|
module FairRecv
|
|
10
|
+
# Dequeues the next received message. Blocks until one is available.
|
|
11
|
+
# Engine-facing contract — Engine must not touch @recv_queue directly.
|
|
12
|
+
#
|
|
13
|
+
# @return [Array<String>, nil]
|
|
14
|
+
#
|
|
15
|
+
def dequeue_recv
|
|
16
|
+
@recv_queue.dequeue
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# Wakes a blocked {#dequeue_recv} with a nil sentinel. Called by
|
|
21
|
+
# Engine on close (close_read) or fatal-error propagation.
|
|
22
|
+
#
|
|
23
|
+
# @return [void]
|
|
24
|
+
#
|
|
25
|
+
def unblock_recv
|
|
26
|
+
@recv_queue.push(nil)
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
|
|
10
30
|
private
|
|
11
31
|
|
|
12
32
|
# Creates a per-connection recv queue, registers it with @recv_queue,
|
|
@@ -18,10 +38,16 @@ module OMQ
|
|
|
18
38
|
def add_fair_recv_connection(conn, &transform)
|
|
19
39
|
conn_q = Routing.build_queue(@engine.options.recv_hwm, :block)
|
|
20
40
|
signaling = SignalingQueue.new(conn_q, @recv_queue)
|
|
41
|
+
|
|
21
42
|
@recv_queue.add_queue(conn, conn_q)
|
|
22
|
-
|
|
23
|
-
|
|
43
|
+
|
|
44
|
+
task = @engine.start_recv_pump(conn, signaling, &transform)
|
|
45
|
+
|
|
46
|
+
if task
|
|
47
|
+
@tasks << task
|
|
48
|
+
end
|
|
24
49
|
end
|
|
50
|
+
|
|
25
51
|
end
|
|
26
52
|
end
|
|
27
53
|
end
|
data/lib/omq/routing/fan_out.rb
CHANGED
|
@@ -134,7 +134,9 @@ module OMQ
|
|
|
134
134
|
loop do
|
|
135
135
|
frame = conn.read_frame
|
|
136
136
|
next unless frame.command?
|
|
137
|
+
|
|
137
138
|
cmd = Protocol::ZMTP::Codec::Command.from_body(frame.body)
|
|
139
|
+
|
|
138
140
|
case cmd.name
|
|
139
141
|
when "SUBSCRIBE"
|
|
140
142
|
on_subscribe(conn, cmd.data)
|
|
@@ -157,7 +159,13 @@ module OMQ
|
|
|
157
159
|
#
|
|
158
160
|
def start_conn_send_pump(conn, q)
|
|
159
161
|
use_wire = conn.respond_to?(:write_wire) && !conn.encrypted?
|
|
160
|
-
|
|
162
|
+
|
|
163
|
+
if @conflate
|
|
164
|
+
task = start_conn_send_pump_conflate(conn, q)
|
|
165
|
+
else
|
|
166
|
+
task = start_conn_send_pump_normal(conn, q, use_wire)
|
|
167
|
+
end
|
|
168
|
+
|
|
161
169
|
@conn_send_tasks[conn] = task
|
|
162
170
|
@tasks << task
|
|
163
171
|
end
|
|
@@ -177,6 +185,7 @@ module OMQ
|
|
|
177
185
|
loop do
|
|
178
186
|
batch = [q.dequeue]
|
|
179
187
|
Routing.drain_send_queue(q, batch)
|
|
188
|
+
|
|
180
189
|
if write_matching_batch(conn, batch, use_wire)
|
|
181
190
|
conn.flush
|
|
182
191
|
batch.each { |parts| @engine.emit_verbose_msg_sent(conn, parts) }
|
|
@@ -193,15 +202,19 @@ module OMQ
|
|
|
193
202
|
#
|
|
194
203
|
def write_matching_batch(conn, batch, use_wire)
|
|
195
204
|
sent = false
|
|
205
|
+
|
|
196
206
|
batch.each do |parts|
|
|
197
207
|
next unless subscribed?(conn, parts.first || EMPTY_BINARY)
|
|
208
|
+
|
|
198
209
|
if use_wire
|
|
199
210
|
conn.write_wire(Protocol::ZMTP::Codec::Frame.encode_message(parts))
|
|
200
211
|
else
|
|
201
212
|
conn.write_message(parts)
|
|
202
213
|
end
|
|
214
|
+
|
|
203
215
|
sent = true
|
|
204
216
|
end
|
|
217
|
+
|
|
205
218
|
sent
|
|
206
219
|
end
|
|
207
220
|
|
data/lib/omq/routing/pair.rb
CHANGED
|
@@ -12,6 +12,12 @@ module OMQ
|
|
|
12
12
|
class Pair
|
|
13
13
|
include FairRecv
|
|
14
14
|
|
|
15
|
+
|
|
16
|
+
# @return [FairQueue]
|
|
17
|
+
#
|
|
18
|
+
attr_reader :recv_queue
|
|
19
|
+
|
|
20
|
+
|
|
15
21
|
# @param engine [Engine]
|
|
16
22
|
#
|
|
17
23
|
def initialize(engine)
|
|
@@ -24,10 +30,6 @@ module OMQ
|
|
|
24
30
|
end
|
|
25
31
|
|
|
26
32
|
|
|
27
|
-
# @return [FairQueue]
|
|
28
|
-
#
|
|
29
|
-
attr_reader :recv_queue
|
|
30
|
-
|
|
31
33
|
# @param connection [Connection]
|
|
32
34
|
# @raise [RuntimeError] if a connection already exists
|
|
33
35
|
#
|
|
@@ -83,24 +85,32 @@ module OMQ
|
|
|
83
85
|
@send_queue.empty?
|
|
84
86
|
end
|
|
85
87
|
|
|
88
|
+
|
|
86
89
|
private
|
|
87
90
|
|
|
91
|
+
|
|
88
92
|
def start_send_pump(conn)
|
|
89
93
|
@send_pump = @engine.spawn_conn_pump_task(conn, annotation: "send pump") do
|
|
90
94
|
loop do
|
|
91
95
|
batch = [@send_queue.dequeue]
|
|
92
96
|
Routing.drain_send_queue(@send_queue, batch)
|
|
97
|
+
|
|
93
98
|
if batch.size == 1
|
|
94
99
|
conn.write_message(batch[0])
|
|
95
100
|
else
|
|
96
101
|
conn.write_messages(batch)
|
|
97
102
|
end
|
|
103
|
+
|
|
98
104
|
conn.flush
|
|
99
|
-
batch.each
|
|
105
|
+
batch.each do |parts|
|
|
106
|
+
@engine.emit_verbose_msg_sent(conn, parts)
|
|
107
|
+
end
|
|
100
108
|
end
|
|
101
109
|
end
|
|
110
|
+
|
|
102
111
|
@tasks << @send_pump
|
|
103
112
|
end
|
|
113
|
+
|
|
104
114
|
end
|
|
105
115
|
end
|
|
106
116
|
end
|
data/lib/omq/routing/pub.rb
CHANGED
|
@@ -20,13 +20,23 @@ module OMQ
|
|
|
20
20
|
end
|
|
21
21
|
|
|
22
22
|
|
|
23
|
-
# PUB is write-only.
|
|
23
|
+
# PUB is write-only. Engine-facing recv contract: dequeue raises,
|
|
24
|
+
# unblock is a no-op (fatal-error propagation still calls it).
|
|
24
25
|
#
|
|
25
26
|
def recv_queue
|
|
26
27
|
raise "PUB sockets cannot receive"
|
|
27
28
|
end
|
|
28
29
|
|
|
29
30
|
|
|
31
|
+
def dequeue_recv
|
|
32
|
+
raise "PUB sockets cannot receive"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def unblock_recv
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
|
|
30
40
|
# @param connection [Connection]
|
|
31
41
|
#
|
|
32
42
|
def connection_added(connection)
|
|
@@ -61,6 +71,7 @@ module OMQ
|
|
|
61
71
|
@tasks.each(&:stop)
|
|
62
72
|
@tasks.clear
|
|
63
73
|
end
|
|
74
|
+
|
|
64
75
|
end
|
|
65
76
|
end
|
|
66
77
|
end
|
data/lib/omq/routing/pull.rb
CHANGED
|
@@ -6,6 +6,8 @@ module OMQ
|
|
|
6
6
|
#
|
|
7
7
|
class Pull
|
|
8
8
|
include FairRecv
|
|
9
|
+
|
|
10
|
+
|
|
9
11
|
# @param engine [Engine]
|
|
10
12
|
#
|
|
11
13
|
def initialize(engine)
|
|
@@ -19,6 +21,7 @@ module OMQ
|
|
|
19
21
|
#
|
|
20
22
|
attr_reader :recv_queue
|
|
21
23
|
|
|
24
|
+
|
|
22
25
|
# @param connection [Connection]
|
|
23
26
|
#
|
|
24
27
|
def connection_added(connection)
|
|
@@ -49,6 +52,7 @@ module OMQ
|
|
|
49
52
|
@tasks.each(&:stop)
|
|
50
53
|
@tasks.clear
|
|
51
54
|
end
|
|
55
|
+
|
|
52
56
|
end
|
|
53
57
|
end
|
|
54
58
|
end
|
data/lib/omq/routing/push.rb
CHANGED
|
@@ -7,6 +7,7 @@ module OMQ
|
|
|
7
7
|
class Push
|
|
8
8
|
include RoundRobin
|
|
9
9
|
|
|
10
|
+
|
|
10
11
|
# @param engine [Engine]
|
|
11
12
|
#
|
|
12
13
|
def initialize(engine)
|
|
@@ -16,13 +17,23 @@ module OMQ
|
|
|
16
17
|
end
|
|
17
18
|
|
|
18
19
|
|
|
19
|
-
# PUSH is write-only.
|
|
20
|
+
# PUSH is write-only. Engine-facing recv contract: dequeue raises,
|
|
21
|
+
# unblock is a no-op (fatal-error propagation still calls it).
|
|
20
22
|
#
|
|
21
23
|
def recv_queue
|
|
22
24
|
raise "PUSH sockets cannot receive"
|
|
23
25
|
end
|
|
24
26
|
|
|
25
27
|
|
|
28
|
+
def dequeue_recv
|
|
29
|
+
raise "PUSH sockets cannot receive"
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def unblock_recv
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
26
37
|
# @param connection [Connection]
|
|
27
38
|
#
|
|
28
39
|
def connection_added(connection)
|
|
@@ -53,6 +64,7 @@ module OMQ
|
|
|
53
64
|
@tasks.clear
|
|
54
65
|
end
|
|
55
66
|
|
|
67
|
+
|
|
56
68
|
private
|
|
57
69
|
|
|
58
70
|
|
|
@@ -66,6 +78,7 @@ module OMQ
|
|
|
66
78
|
conn.receive_message # blocks until peer disconnects; then exits
|
|
67
79
|
end
|
|
68
80
|
end
|
|
81
|
+
|
|
69
82
|
end
|
|
70
83
|
end
|
|
71
84
|
end
|
data/lib/omq/routing/rep.rb
CHANGED
|
@@ -14,6 +14,11 @@ module OMQ
|
|
|
14
14
|
EMPTY_FRAME = "".b.freeze
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
# @return [FairQueue]
|
|
18
|
+
#
|
|
19
|
+
attr_reader :recv_queue
|
|
20
|
+
|
|
21
|
+
|
|
17
22
|
# @param engine [Engine]
|
|
18
23
|
#
|
|
19
24
|
def initialize(engine)
|
|
@@ -26,17 +31,14 @@ module OMQ
|
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
|
|
29
|
-
# @return [FairQueue]
|
|
30
|
-
#
|
|
31
|
-
attr_reader :recv_queue
|
|
32
|
-
|
|
33
34
|
# @param connection [Connection]
|
|
34
35
|
#
|
|
35
36
|
def connection_added(connection)
|
|
36
37
|
add_fair_recv_connection(connection) do |msg|
|
|
37
|
-
delimiter = msg.index
|
|
38
|
+
delimiter = msg.index { |p| p.empty? } || msg.size
|
|
38
39
|
envelope = msg[0, delimiter]
|
|
39
40
|
body = msg[(delimiter + 1)..] || []
|
|
41
|
+
|
|
40
42
|
@pending_replies << { conn: connection, envelope: envelope }
|
|
41
43
|
body
|
|
42
44
|
end
|
data/lib/omq/routing/req.rb
CHANGED
|
@@ -14,6 +14,11 @@ module OMQ
|
|
|
14
14
|
EMPTY_BINARY = ::Protocol::ZMTP::Codec::EMPTY_BINARY
|
|
15
15
|
|
|
16
16
|
|
|
17
|
+
# @return [FairQueue]
|
|
18
|
+
#
|
|
19
|
+
attr_reader :recv_queue
|
|
20
|
+
|
|
21
|
+
|
|
17
22
|
# @param engine [Engine]
|
|
18
23
|
#
|
|
19
24
|
def initialize(engine)
|
|
@@ -25,11 +30,6 @@ module OMQ
|
|
|
25
30
|
end
|
|
26
31
|
|
|
27
32
|
|
|
28
|
-
# @return [FairQueue]
|
|
29
|
-
#
|
|
30
|
-
attr_reader :recv_queue
|
|
31
|
-
|
|
32
|
-
|
|
33
33
|
# @param connection [Connection]
|
|
34
34
|
#
|
|
35
35
|
def connection_added(connection)
|
|
@@ -19,6 +19,9 @@ module OMQ
|
|
|
19
19
|
# their #initialize.
|
|
20
20
|
#
|
|
21
21
|
module RoundRobin
|
|
22
|
+
BATCH_MSG_CAP = 256
|
|
23
|
+
BATCH_BYTE_CAP = 512 * 1024
|
|
24
|
+
|
|
22
25
|
# @return [Boolean] true when the shared send queue is empty
|
|
23
26
|
# and no pump fiber is mid-write with a dequeued batch.
|
|
24
27
|
#
|
|
@@ -26,8 +29,10 @@ module OMQ
|
|
|
26
29
|
@send_queue.empty? && @in_flight == 0
|
|
27
30
|
end
|
|
28
31
|
|
|
32
|
+
|
|
29
33
|
private
|
|
30
34
|
|
|
35
|
+
|
|
31
36
|
# Initializes the shared send queue for the including class.
|
|
32
37
|
#
|
|
33
38
|
# @param engine [Engine]
|
|
@@ -83,6 +88,7 @@ module OMQ
|
|
|
83
88
|
#
|
|
84
89
|
def enqueue_round_robin(parts)
|
|
85
90
|
pipe = @direct_pipe
|
|
91
|
+
|
|
86
92
|
if pipe&.direct_recv_queue
|
|
87
93
|
pipe.send_message(transform_send(parts))
|
|
88
94
|
else
|
|
@@ -97,7 +103,9 @@ module OMQ
|
|
|
97
103
|
# @param parts [Array<String>]
|
|
98
104
|
# @return [Array<String>]
|
|
99
105
|
#
|
|
100
|
-
def transform_send(parts)
|
|
106
|
+
def transform_send(parts)
|
|
107
|
+
parts
|
|
108
|
+
end
|
|
101
109
|
|
|
102
110
|
|
|
103
111
|
# Spawns a send pump for one connection. Drains the shared send
|
|
@@ -129,23 +137,26 @@ module OMQ
|
|
|
129
137
|
batch = [@send_queue.dequeue]
|
|
130
138
|
drain_send_queue_capped(batch)
|
|
131
139
|
@in_flight += batch.size
|
|
140
|
+
|
|
132
141
|
begin
|
|
133
142
|
write_batch(conn, batch)
|
|
134
143
|
ensure
|
|
135
144
|
@in_flight -= batch.size
|
|
136
145
|
end
|
|
137
|
-
|
|
146
|
+
|
|
147
|
+
batch.each do |parts|
|
|
148
|
+
@engine.emit_verbose_msg_sent(conn, parts)
|
|
149
|
+
end
|
|
150
|
+
|
|
138
151
|
Async::Task.current.yield
|
|
139
152
|
end
|
|
140
153
|
end
|
|
154
|
+
|
|
141
155
|
@conn_send_tasks[conn] = task
|
|
142
156
|
@tasks << task
|
|
143
157
|
end
|
|
144
158
|
|
|
145
159
|
|
|
146
|
-
BATCH_MSG_CAP = 256
|
|
147
|
-
BATCH_BYTE_CAP = 512 * 1024
|
|
148
|
-
|
|
149
160
|
def drain_send_queue_capped(batch)
|
|
150
161
|
bytes = batch_bytes(batch[0])
|
|
151
162
|
while batch.size < BATCH_MSG_CAP && bytes < BATCH_BYTE_CAP
|
|
@@ -175,6 +186,7 @@ module OMQ
|
|
|
175
186
|
conn.flush
|
|
176
187
|
end
|
|
177
188
|
end
|
|
189
|
+
|
|
178
190
|
end
|
|
179
191
|
end
|
|
180
192
|
end
|
data/lib/omq/routing/router.rb
CHANGED
|
@@ -12,6 +12,13 @@ module OMQ
|
|
|
12
12
|
#
|
|
13
13
|
class Router
|
|
14
14
|
include FairRecv
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
# @return [FairQueue]
|
|
18
|
+
#
|
|
19
|
+
attr_reader :recv_queue
|
|
20
|
+
|
|
21
|
+
|
|
15
22
|
# @param engine [Engine]
|
|
16
23
|
#
|
|
17
24
|
def initialize(engine)
|
|
@@ -25,10 +32,6 @@ module OMQ
|
|
|
25
32
|
end
|
|
26
33
|
|
|
27
34
|
|
|
28
|
-
# @return [FairQueue]
|
|
29
|
-
#
|
|
30
|
-
attr_reader :recv_queue
|
|
31
|
-
|
|
32
35
|
# @param connection [Connection]
|
|
33
36
|
#
|
|
34
37
|
def connection_added(connection)
|
data/lib/omq/routing/sub.rb
CHANGED
|
@@ -8,6 +8,11 @@ module OMQ
|
|
|
8
8
|
#
|
|
9
9
|
class Sub
|
|
10
10
|
|
|
11
|
+
# @return [FairQueue]
|
|
12
|
+
#
|
|
13
|
+
attr_reader :recv_queue
|
|
14
|
+
|
|
15
|
+
|
|
11
16
|
# @param engine [Engine]
|
|
12
17
|
#
|
|
13
18
|
def initialize(engine)
|
|
@@ -19,9 +24,17 @@ module OMQ
|
|
|
19
24
|
end
|
|
20
25
|
|
|
21
26
|
|
|
22
|
-
#
|
|
27
|
+
# Engine-facing recv contract. Delegates to the FairQueue.
|
|
23
28
|
#
|
|
24
|
-
|
|
29
|
+
def dequeue_recv
|
|
30
|
+
@recv_queue.dequeue
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def unblock_recv
|
|
35
|
+
@recv_queue.push(nil)
|
|
36
|
+
end
|
|
37
|
+
|
|
25
38
|
|
|
26
39
|
# @param connection [Connection]
|
|
27
40
|
#
|
|
@@ -85,6 +98,7 @@ module OMQ
|
|
|
85
98
|
@tasks.each(&:stop)
|
|
86
99
|
@tasks.clear
|
|
87
100
|
end
|
|
101
|
+
|
|
88
102
|
end
|
|
89
103
|
end
|
|
90
104
|
end
|
data/lib/omq/routing/xpub.rb
CHANGED
|
@@ -14,6 +14,11 @@ module OMQ
|
|
|
14
14
|
class XPub
|
|
15
15
|
include FanOut
|
|
16
16
|
|
|
17
|
+
# @return [Async::LimitedQueue]
|
|
18
|
+
#
|
|
19
|
+
attr_reader :recv_queue
|
|
20
|
+
|
|
21
|
+
|
|
17
22
|
# @param engine [Engine]
|
|
18
23
|
#
|
|
19
24
|
def initialize(engine)
|
|
@@ -24,9 +29,17 @@ module OMQ
|
|
|
24
29
|
end
|
|
25
30
|
|
|
26
31
|
|
|
27
|
-
#
|
|
32
|
+
# Engine-facing recv contract. Delegates to the bounded queue.
|
|
28
33
|
#
|
|
29
|
-
|
|
34
|
+
def dequeue_recv
|
|
35
|
+
@recv_queue.dequeue
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def unblock_recv
|
|
40
|
+
@recv_queue.push(nil)
|
|
41
|
+
end
|
|
42
|
+
|
|
30
43
|
|
|
31
44
|
# @param connection [Connection]
|
|
32
45
|
#
|
|
@@ -63,8 +76,10 @@ module OMQ
|
|
|
63
76
|
@tasks.clear
|
|
64
77
|
end
|
|
65
78
|
|
|
79
|
+
|
|
66
80
|
private
|
|
67
81
|
|
|
82
|
+
|
|
68
83
|
# Expose subscription to application as data message.
|
|
69
84
|
#
|
|
70
85
|
def on_subscribe(conn, prefix)
|
|
@@ -79,6 +94,7 @@ module OMQ
|
|
|
79
94
|
super
|
|
80
95
|
@recv_queue.enqueue(["\x00#{prefix}".b])
|
|
81
96
|
end
|
|
97
|
+
|
|
82
98
|
end
|
|
83
99
|
end
|
|
84
100
|
end
|
data/lib/omq/routing/xsub.rb
CHANGED
|
@@ -10,6 +10,11 @@ module OMQ
|
|
|
10
10
|
#
|
|
11
11
|
class XSub
|
|
12
12
|
|
|
13
|
+
# @return [FairQueue]
|
|
14
|
+
#
|
|
15
|
+
attr_reader :recv_queue
|
|
16
|
+
|
|
17
|
+
|
|
13
18
|
# @param engine [Engine]
|
|
14
19
|
#
|
|
15
20
|
def initialize(engine)
|
|
@@ -22,9 +27,17 @@ module OMQ
|
|
|
22
27
|
end
|
|
23
28
|
|
|
24
29
|
|
|
25
|
-
#
|
|
30
|
+
# Engine-facing recv contract. Delegates to the FairQueue.
|
|
26
31
|
#
|
|
27
|
-
|
|
32
|
+
def dequeue_recv
|
|
33
|
+
@recv_queue.dequeue
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def unblock_recv
|
|
38
|
+
@recv_queue.push(nil)
|
|
39
|
+
end
|
|
40
|
+
|
|
28
41
|
|
|
29
42
|
# @param connection [Connection]
|
|
30
43
|
#
|
|
@@ -58,7 +71,9 @@ module OMQ
|
|
|
58
71
|
# @param parts [Array<String>]
|
|
59
72
|
#
|
|
60
73
|
def enqueue(parts)
|
|
61
|
-
@connections.each
|
|
74
|
+
@connections.each do |conn|
|
|
75
|
+
@conn_queues[conn]&.enqueue(parts)
|
|
76
|
+
end
|
|
62
77
|
end
|
|
63
78
|
|
|
64
79
|
|
|
@@ -78,16 +93,21 @@ module OMQ
|
|
|
78
93
|
@conn_queues.values.all?(&:empty?)
|
|
79
94
|
end
|
|
80
95
|
|
|
96
|
+
|
|
81
97
|
private
|
|
82
98
|
|
|
99
|
+
|
|
83
100
|
def start_conn_send_pump(conn, q)
|
|
84
101
|
task = @engine.spawn_conn_pump_task(conn, annotation: "send pump") do
|
|
85
102
|
loop do
|
|
86
103
|
parts = q.dequeue
|
|
87
104
|
frame = parts.first&.b
|
|
105
|
+
|
|
88
106
|
next if frame.nil? || frame.empty?
|
|
107
|
+
|
|
89
108
|
flag = frame.getbyte(0)
|
|
90
109
|
prefix = frame.byteslice(1..) || "".b
|
|
110
|
+
|
|
91
111
|
case flag
|
|
92
112
|
when 0x01
|
|
93
113
|
conn.send_command(Protocol::ZMTP::Codec::Command.subscribe(prefix))
|
|
@@ -96,9 +116,11 @@ module OMQ
|
|
|
96
116
|
end
|
|
97
117
|
end
|
|
98
118
|
end
|
|
119
|
+
|
|
99
120
|
@conn_send_tasks[conn] = task
|
|
100
121
|
@tasks << task
|
|
101
122
|
end
|
|
123
|
+
|
|
102
124
|
end
|
|
103
125
|
end
|
|
104
126
|
end
|
data/lib/omq/routing.rb
CHANGED
data/lib/omq/single_frame.rb
CHANGED
data/lib/omq/socket.rb
CHANGED
|
@@ -6,6 +6,30 @@ module OMQ
|
|
|
6
6
|
# Socket base class.
|
|
7
7
|
#
|
|
8
8
|
class Socket
|
|
9
|
+
extend Forwardable
|
|
10
|
+
|
|
11
|
+
# Creates a new socket and binds it to the given endpoint.
|
|
12
|
+
#
|
|
13
|
+
# @param endpoint [String]
|
|
14
|
+
# @param opts [Hash] keyword arguments forwarded to {#initialize}
|
|
15
|
+
# @return [Socket]
|
|
16
|
+
#
|
|
17
|
+
def self.bind(endpoint, **opts)
|
|
18
|
+
new("@#{endpoint}", **opts)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
# Creates a new socket and connects it to the given endpoint.
|
|
23
|
+
#
|
|
24
|
+
# @param endpoint [String]
|
|
25
|
+
# @param opts [Hash] keyword arguments forwarded to {#initialize}
|
|
26
|
+
# @return [Socket]
|
|
27
|
+
#
|
|
28
|
+
def self.connect(endpoint, **opts)
|
|
29
|
+
new(">#{endpoint}", **opts)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
|
|
9
33
|
# @return [Options]
|
|
10
34
|
#
|
|
11
35
|
attr_reader :options
|
|
@@ -25,8 +49,6 @@ module OMQ
|
|
|
25
49
|
|
|
26
50
|
# Delegate socket option accessors to @options.
|
|
27
51
|
#
|
|
28
|
-
extend Forwardable
|
|
29
|
-
|
|
30
52
|
def_delegators :@options,
|
|
31
53
|
:send_hwm, :send_hwm=,
|
|
32
54
|
:recv_hwm, :recv_hwm=,
|
|
@@ -49,28 +71,6 @@ module OMQ
|
|
|
49
71
|
:mechanism, :mechanism=
|
|
50
72
|
|
|
51
73
|
|
|
52
|
-
# Creates a new socket and binds it to the given endpoint.
|
|
53
|
-
#
|
|
54
|
-
# @param endpoint [String]
|
|
55
|
-
# @param opts [Hash] keyword arguments forwarded to {#initialize}
|
|
56
|
-
# @return [Socket]
|
|
57
|
-
#
|
|
58
|
-
def self.bind(endpoint, **opts)
|
|
59
|
-
new("@#{endpoint}", **opts)
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
# Creates a new socket and connects it to the given endpoint.
|
|
64
|
-
#
|
|
65
|
-
# @param endpoint [String]
|
|
66
|
-
# @param opts [Hash] keyword arguments forwarded to {#initialize}
|
|
67
|
-
# @return [Socket]
|
|
68
|
-
#
|
|
69
|
-
def self.connect(endpoint, **opts)
|
|
70
|
-
new(">#{endpoint}", **opts)
|
|
71
|
-
end
|
|
72
|
-
|
|
73
|
-
|
|
74
74
|
# @param endpoints [String, nil] optional endpoint with prefix convention
|
|
75
75
|
# (+@+ for bind, +>+ for connect, plain uses subclass default)
|
|
76
76
|
# @param linger [Integer] linger period in seconds (default 0)
|
|
@@ -141,19 +141,27 @@ module OMQ
|
|
|
141
141
|
|
|
142
142
|
|
|
143
143
|
# @return [Async::Promise] resolves when first peer completes handshake
|
|
144
|
-
def peer_connected
|
|
144
|
+
def peer_connected
|
|
145
|
+
@engine.peer_connected
|
|
146
|
+
end
|
|
145
147
|
|
|
146
148
|
|
|
147
149
|
# @return [Async::Promise] resolves when first subscriber joins (PUB/XPUB only)
|
|
148
|
-
def subscriber_joined
|
|
150
|
+
def subscriber_joined
|
|
151
|
+
@engine.routing.subscriber_joined
|
|
152
|
+
end
|
|
149
153
|
|
|
150
154
|
|
|
151
155
|
# @return [Async::Promise] resolves when all peers disconnect (after having had peers)
|
|
152
|
-
def all_peers_gone
|
|
156
|
+
def all_peers_gone
|
|
157
|
+
@engine.all_peers_gone
|
|
158
|
+
end
|
|
153
159
|
|
|
154
160
|
|
|
155
161
|
# @return [Integer] current number of peer connections
|
|
156
|
-
def connection_count
|
|
162
|
+
def connection_count
|
|
163
|
+
@engine.connections.size
|
|
164
|
+
end
|
|
157
165
|
|
|
158
166
|
|
|
159
167
|
# Signals end-of-stream on the receive side. A subsequent
|
|
@@ -270,6 +278,7 @@ module OMQ
|
|
|
270
278
|
#
|
|
271
279
|
def attach_endpoints(endpoints, default:)
|
|
272
280
|
return unless endpoints
|
|
281
|
+
|
|
273
282
|
case endpoints
|
|
274
283
|
when /\A@(.+)\z/
|
|
275
284
|
bind($1)
|
|
@@ -318,5 +327,6 @@ module OMQ
|
|
|
318
327
|
def ensure_parent_task(parent: nil)
|
|
319
328
|
@engine.capture_parent_task(parent: parent)
|
|
320
329
|
end
|
|
330
|
+
|
|
321
331
|
end
|
|
322
332
|
end
|
|
@@ -110,14 +110,18 @@ module OMQ
|
|
|
110
110
|
|
|
111
111
|
# @return [Boolean] always false; inproc pipes are never encrypted
|
|
112
112
|
#
|
|
113
|
-
def encrypted?
|
|
113
|
+
def encrypted?
|
|
114
|
+
false
|
|
115
|
+
end
|
|
114
116
|
|
|
115
117
|
|
|
116
118
|
# No-op — inproc has no IO buffer to flush.
|
|
117
119
|
#
|
|
118
120
|
# @return [nil]
|
|
119
121
|
#
|
|
120
|
-
def flush
|
|
122
|
+
def flush
|
|
123
|
+
nil
|
|
124
|
+
end
|
|
121
125
|
|
|
122
126
|
|
|
123
127
|
# Receives a multi-frame message.
|
data/lib/omq/transport/tcp.rb
CHANGED
data/lib/omq/version.rb
CHANGED
data/lib/omq/writable.rb
CHANGED
|
@@ -7,6 +7,11 @@ module OMQ
|
|
|
7
7
|
#
|
|
8
8
|
module Writable
|
|
9
9
|
include QueueWritable
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
EMPTY_PART = "".b.freeze
|
|
13
|
+
|
|
14
|
+
|
|
10
15
|
# Sends a message.
|
|
11
16
|
#
|
|
12
17
|
# @param message [String, Array<String>] message parts
|
|
@@ -33,8 +38,20 @@ module OMQ
|
|
|
33
38
|
send(message)
|
|
34
39
|
end
|
|
35
40
|
|
|
41
|
+
|
|
42
|
+
# Waits until the socket is writable.
|
|
43
|
+
#
|
|
44
|
+
# @param timeout [Numeric, nil] timeout in seconds
|
|
45
|
+
# @return [true]
|
|
46
|
+
#
|
|
47
|
+
def wait_writable(timeout = @options.write_timeout)
|
|
48
|
+
true
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
|
|
36
52
|
private
|
|
37
53
|
|
|
54
|
+
|
|
38
55
|
# Converts a message into a frozen array of frozen binary strings.
|
|
39
56
|
#
|
|
40
57
|
# @param message [String, Array<String>]
|
|
@@ -44,21 +61,23 @@ module OMQ
|
|
|
44
61
|
parts = message.is_a?(Array) ? message : [message]
|
|
45
62
|
raise ArgumentError, "message has no parts" if parts.empty?
|
|
46
63
|
|
|
47
|
-
|
|
64
|
+
all_ready = parts.all? { |p| p.is_a?(String) && p.frozen? && p.encoding == Encoding::BINARY }
|
|
65
|
+
|
|
66
|
+
# Already a frozen array of frozen binary strings → return as-is.
|
|
67
|
+
return parts if all_ready && parts.frozen?
|
|
68
|
+
|
|
69
|
+
# Items are ready; just freeze the outer array.
|
|
70
|
+
return parts.freeze if all_ready
|
|
71
|
+
|
|
72
|
+
# Items need conversion. Mutate in place when we can.
|
|
48
73
|
if parts.frozen?
|
|
49
|
-
|
|
50
|
-
parts = parts.map { |p| frozen_binary(p) }
|
|
74
|
+
parts.map { |p| frozen_binary(p) }.freeze
|
|
51
75
|
else
|
|
52
|
-
|
|
53
|
-
parts.map! { |p| frozen_binary(p) }
|
|
54
|
-
end
|
|
76
|
+
parts.map! { |p| frozen_binary(p) }.freeze
|
|
55
77
|
end
|
|
56
|
-
parts.freeze
|
|
57
78
|
end
|
|
58
79
|
|
|
59
80
|
|
|
60
|
-
EMPTY_PART = "".b.freeze
|
|
61
|
-
|
|
62
81
|
def frozen_binary(obj)
|
|
63
82
|
return EMPTY_PART if obj.nil?
|
|
64
83
|
s = obj.to_s
|
|
@@ -66,15 +85,5 @@ module OMQ
|
|
|
66
85
|
s.b.freeze
|
|
67
86
|
end
|
|
68
87
|
|
|
69
|
-
public
|
|
70
|
-
|
|
71
|
-
# Waits until the socket is writable.
|
|
72
|
-
#
|
|
73
|
-
# @param timeout [Numeric, nil] timeout in seconds
|
|
74
|
-
# @return [true]
|
|
75
|
-
#
|
|
76
|
-
def wait_writable(timeout = @options.write_timeout)
|
|
77
|
-
true
|
|
78
|
-
end
|
|
79
88
|
end
|
|
80
89
|
end
|