omq 0.13.0 → 0.14.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.
data/lib/omq/options.rb CHANGED
@@ -9,6 +9,7 @@ module OMQ
9
9
  class Options
10
10
  DEFAULT_HWM = 1000
11
11
 
12
+
12
13
  # @param linger [Integer] linger period in seconds (default 0)
13
14
  #
14
15
  def initialize(linger: 0)
@@ -30,6 +31,40 @@ module OMQ
30
31
  @qos = 0 # 0 = fire-and-forget, 1 = at-least-once (see omq-qos gem)
31
32
  end
32
33
 
34
+
35
+ # @!attribute send_hwm
36
+ # @return [Integer] send high water mark (default 1000, 0 = unbounded)
37
+ # @!attribute recv_hwm
38
+ # @return [Integer] receive high water mark (default 1000, 0 = unbounded)
39
+ # @!attribute linger
40
+ # @return [Integer, nil] linger period in seconds (nil = wait forever, 0 = immediate)
41
+ # @!attribute identity
42
+ # @return [String] socket identity for ROUTER addressing (default "")
43
+ # @!attribute router_mandatory
44
+ # @return [Boolean] raise on unroutable messages (default false)
45
+ # @!attribute conflate
46
+ # @return [Boolean] keep only the latest message per topic (default false)
47
+ # @!attribute read_timeout
48
+ # @return [Numeric, nil] read timeout in seconds (nil = no timeout)
49
+ # @!attribute write_timeout
50
+ # @return [Numeric, nil] write timeout in seconds (nil = no timeout)
51
+ # @!attribute reconnect_interval
52
+ # @return [Numeric, Range] reconnect interval in seconds, or Range for exponential backoff
53
+ # @!attribute heartbeat_interval
54
+ # @return [Numeric, nil] PING interval in seconds (nil = disabled)
55
+ # @!attribute heartbeat_ttl
56
+ # @return [Numeric, nil] TTL advertised in PING (nil = use heartbeat_interval)
57
+ # @!attribute heartbeat_timeout
58
+ # @return [Numeric, nil] time without traffic before closing (nil = use heartbeat_interval)
59
+ # @!attribute max_message_size
60
+ # @return [Integer, nil] maximum message size in bytes
61
+ # @!attribute on_mute
62
+ # @return [Symbol] mute strategy (:block, :drop_newest, :drop_oldest)
63
+ # @!attribute mechanism
64
+ # @return [Protocol::ZMTP::Mechanism::Null, Protocol::ZMTP::Mechanism::Curve] security mechanism
65
+ # @!attribute qos
66
+ # @return [Integer] quality of service level (0 = fire-and-forget)
67
+ #
33
68
  attr_accessor :send_hwm, :recv_hwm,
34
69
  :linger, :identity,
35
70
  :router_mandatory, :conflate,
@@ -41,10 +76,19 @@ module OMQ
41
76
  :mechanism,
42
77
  :qos
43
78
 
79
+ # @return [Boolean] true if router_mandatory is set
44
80
  alias_method :router_mandatory?, :router_mandatory
81
+
82
+ # @return [Numeric, nil] alias for #read_timeout
45
83
  alias_method :recv_timeout, :read_timeout
84
+
85
+ # @param val [Numeric, nil] alias for #read_timeout=
46
86
  alias_method :recv_timeout=, :read_timeout=
87
+
88
+ # @return [Numeric, nil] alias for #write_timeout
47
89
  alias_method :send_timeout, :write_timeout
90
+
91
+ # @param val [Numeric, nil] alias for #write_timeout=
48
92
  alias_method :send_timeout=, :write_timeout=
49
93
  end
50
94
  end
data/lib/omq/pair.rb CHANGED
@@ -1,10 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OMQ
4
+ # PAIR socket — exclusive 1-to-1 bidirectional communication.
5
+ #
4
6
  class PAIR < Socket
5
7
  include Readable
6
8
  include Writable
7
9
 
10
+ # @param endpoints [String, nil] endpoint to bind/connect
11
+ # @param linger [Integer] linger period in seconds
12
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
13
+ #
8
14
  def initialize(endpoints = nil, linger: 0, backend: nil)
9
15
  _init_engine(:PAIR, linger: linger, backend: backend)
10
16
  _attach(endpoints, default: :connect)
data/lib/omq/pub_sub.rb CHANGED
@@ -1,15 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OMQ
4
+ # PUB socket — publish messages to all matching subscribers.
5
+ #
4
6
  class PUB < Socket
5
7
  include Writable
6
8
 
9
+ # @param endpoints [String, nil] endpoint to bind/connect
10
+ # @param linger [Integer] linger period in seconds
11
+ # @param on_mute [Symbol] mute strategy for slow subscribers
12
+ # @param conflate [Boolean] keep only latest message per topic
13
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
14
+ #
7
15
  def initialize(endpoints = nil, linger: 0, on_mute: :drop_newest, conflate: false, backend: nil)
8
16
  _init_engine(:PUB, linger: linger, on_mute: on_mute, conflate: conflate, backend: backend)
9
17
  _attach(endpoints, default: :bind)
10
18
  end
11
19
  end
12
20
 
21
+
13
22
  # SUB socket.
14
23
  #
15
24
  class SUB < Socket
@@ -19,6 +28,7 @@ module OMQ
19
28
  #
20
29
  EVERYTHING = ''
21
30
 
31
+
22
32
  # @param endpoints [String, nil]
23
33
  # @param linger [Integer]
24
34
  # @param subscribe [String, nil] subscription prefix; +nil+ (default)
@@ -31,6 +41,7 @@ module OMQ
31
41
  self.subscribe(subscribe) unless subscribe.nil?
32
42
  end
33
43
 
44
+
34
45
  # Subscribes to a topic prefix.
35
46
  #
36
47
  # @param prefix [String]
@@ -40,6 +51,7 @@ module OMQ
40
51
  @engine.routing.subscribe(prefix)
41
52
  end
42
53
 
54
+
43
55
  # Unsubscribes from a topic prefix.
44
56
  #
45
57
  # @param prefix [String]
@@ -50,16 +62,27 @@ module OMQ
50
62
  end
51
63
  end
52
64
 
65
+
66
+ # XPUB socket — like PUB but exposes subscription events to the application.
67
+ #
53
68
  class XPUB < Socket
54
69
  include Readable
55
70
  include Writable
56
71
 
72
+ # @param endpoints [String, nil] endpoint to bind/connect
73
+ # @param linger [Integer] linger period in seconds
74
+ # @param on_mute [Symbol] mute strategy for slow subscribers
75
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
76
+ #
57
77
  def initialize(endpoints = nil, linger: 0, on_mute: :drop_newest, backend: nil)
58
78
  _init_engine(:XPUB, linger: linger, on_mute: on_mute, backend: backend)
59
79
  _attach(endpoints, default: :bind)
60
80
  end
61
81
  end
62
82
 
83
+
84
+ # XSUB socket — like SUB but subscriptions are sent as data frames.
85
+ #
63
86
  class XSUB < Socket
64
87
  include Readable
65
88
  include Writable
@@ -68,6 +91,8 @@ module OMQ
68
91
  # @param linger [Integer]
69
92
  # @param subscribe [String, nil] subscription prefix; +nil+ (default)
70
93
  # means no subscription — send a subscribe frame explicitly.
94
+ # @param on_mute [Symbol] mute strategy (:block, :drop_newest, :drop_oldest)
95
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
71
96
  #
72
97
  def initialize(endpoints = nil, linger: 0, subscribe: nil, on_mute: :block, backend: nil)
73
98
  _init_engine(:XSUB, linger: linger, on_mute: on_mute, backend: backend)
data/lib/omq/push_pull.rb CHANGED
@@ -1,18 +1,35 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OMQ
4
+ # PUSH socket — push messages to connected PULL peers via round-robin.
5
+ #
4
6
  class PUSH < Socket
5
7
  include Writable
6
8
 
9
+ # @param endpoints [String, nil] endpoint to bind/connect
10
+ # @param linger [Integer] linger period in seconds
11
+ # @param send_hwm [Integer, nil] send high water mark (nil uses default)
12
+ # @param send_timeout [Numeric, nil] send timeout in seconds
13
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
14
+ #
7
15
  def initialize(endpoints = nil, linger: 0, send_hwm: nil, send_timeout: nil, backend: nil)
8
16
  _init_engine(:PUSH, linger: linger, send_hwm: send_hwm, send_timeout: send_timeout, backend: backend)
9
17
  _attach(endpoints, default: :connect)
10
18
  end
11
19
  end
12
20
 
21
+
22
+ # PULL socket — receive messages from PUSH peers via fair-queue.
23
+ #
13
24
  class PULL < Socket
14
25
  include Readable
15
26
 
27
+ # @param endpoints [String, nil] endpoint to bind/connect
28
+ # @param linger [Integer] linger period in seconds
29
+ # @param recv_hwm [Integer, nil] receive high water mark (nil uses default)
30
+ # @param recv_timeout [Numeric, nil] receive timeout in seconds
31
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
32
+ #
16
33
  def initialize(endpoints = nil, linger: 0, recv_hwm: nil, recv_timeout: nil, backend: nil)
17
34
  _init_engine(:PULL, linger: linger, recv_hwm: recv_hwm, recv_timeout: recv_timeout, backend: backend)
18
35
  _attach(endpoints, default: :bind)
@@ -35,6 +35,7 @@ module OMQ
35
35
  dequeue(timeout: nil)
36
36
  end
37
37
 
38
+
38
39
  # Yields each received message until the socket is closed or
39
40
  # a receive timeout expires.
40
41
  #
data/lib/omq/readable.rb CHANGED
@@ -11,6 +11,7 @@ module OMQ
11
11
  # Maximum messages to prefetch from the recv queue per drain.
12
12
  RECV_BATCH_SIZE = 64
13
13
 
14
+
14
15
  # Receives the next message. Returns from a local prefetch
15
16
  # buffer when available, otherwise drains up to
16
17
  # {RECV_BATCH_SIZE} messages from the recv queue in one
@@ -23,6 +24,7 @@ module OMQ
23
24
  @recv_mutex.synchronize { @recv_buffer.shift } || fill_recv_buffer
24
25
  end
25
26
 
27
+
26
28
  # Waits until the socket is readable.
27
29
  #
28
30
  # @param timeout [Numeric, nil] timeout in seconds
data/lib/omq/req_rep.rb CHANGED
@@ -1,20 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OMQ
4
+ # REQ socket — send a request, then receive one reply (strict alternation).
5
+ #
4
6
  class REQ < Socket
5
7
  include Readable
6
8
  include Writable
7
9
 
10
+ # @param endpoints [String, nil] endpoint to bind/connect
11
+ # @param linger [Integer] linger period in seconds
12
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
13
+ #
8
14
  def initialize(endpoints = nil, linger: 0, backend: nil)
9
15
  _init_engine(:REQ, linger: linger, backend: backend)
10
16
  _attach(endpoints, default: :connect)
11
17
  end
12
18
  end
13
19
 
20
+
21
+ # REP socket — receive a request, then send one reply (strict alternation).
22
+ #
14
23
  class REP < Socket
15
24
  include Readable
16
25
  include Writable
17
26
 
27
+ # @param endpoints [String, nil] endpoint to bind/connect
28
+ # @param linger [Integer] linger period in seconds
29
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
30
+ #
18
31
  def initialize(endpoints = nil, linger: 0, backend: nil)
19
32
  _init_engine(:REP, linger: linger, backend: backend)
20
33
  _attach(endpoints, default: :bind)
@@ -1,27 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module OMQ
4
+ # DEALER socket — asynchronous round-robin send, fair-queue receive.
5
+ #
4
6
  class DEALER < Socket
5
7
  include Readable
6
8
  include Writable
7
9
 
10
+ # @param endpoints [String, nil] endpoint to bind/connect
11
+ # @param linger [Integer] linger period in seconds
12
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
13
+ #
8
14
  def initialize(endpoints = nil, linger: 0, backend: nil)
9
15
  _init_engine(:DEALER, linger: linger, backend: backend)
10
16
  _attach(endpoints, default: :connect)
11
17
  end
12
18
  end
13
19
 
20
+
14
21
  # ROUTER socket.
15
22
  #
16
23
  class ROUTER < Socket
17
24
  include Readable
18
25
  include Writable
19
26
 
27
+ # @param endpoints [String, nil] endpoint to bind/connect
28
+ # @param linger [Integer] linger period in seconds
29
+ # @param backend [Symbol, nil] :ruby (default) or :ffi
30
+ #
20
31
  def initialize(endpoints = nil, linger: 0, backend: nil)
21
32
  _init_engine(:ROUTER, linger: linger, backend: backend)
22
33
  _attach(endpoints, default: :bind)
23
34
  end
24
35
 
36
+
25
37
  # Sends a message to a specific peer by identity.
26
38
  #
27
39
  # @param receiver [String] peer identity
@@ -7,7 +7,7 @@ module OMQ
7
7
  # Used by Router and Rep, which have per-connection queues but do not
8
8
  # include the RoundRobin mixin.
9
9
  #
10
- class ConnSendPump
10
+ module ConnSendPump
11
11
  # Spawns the pump task and registers it in +tasks+.
12
12
  #
13
13
  # @param engine [Engine]
@@ -19,6 +19,7 @@ module OMQ
19
19
  init_round_robin(engine)
20
20
  end
21
21
 
22
+
22
23
  # @return [FairQueue]
23
24
  #
24
25
  attr_reader :recv_queue
@@ -31,6 +32,7 @@ module OMQ
31
32
  add_round_robin_send_connection(connection)
32
33
  end
33
34
 
35
+
34
36
  # @param connection [Connection]
35
37
  #
36
38
  def connection_removed(connection)
@@ -39,12 +41,17 @@ module OMQ
39
41
  remove_round_robin_send_connection(connection)
40
42
  end
41
43
 
44
+
42
45
  # @param parts [Array<String>]
43
46
  #
44
47
  def enqueue(parts)
45
48
  enqueue_round_robin(parts)
46
49
  end
47
50
 
51
+
52
+ # Stops all background tasks.
53
+ #
54
+ # @return [void]
48
55
  #
49
56
  def stop
50
57
  @tasks.each(&:stop)
@@ -12,6 +12,8 @@ module OMQ
12
12
  # SignalingQueue wrapper, which also wakes a blocked #dequeue.
13
13
  #
14
14
  class FairQueue
15
+ # Creates an empty fair queue with no per-connection queues.
16
+ #
15
17
  def initialize
16
18
  @queues = [] # ordered list of per-connection inner queues
17
19
  @mapping = {} # connection => inner queue
@@ -21,6 +23,7 @@ module OMQ
21
23
  @closed = false
22
24
  end
23
25
 
26
+
24
27
  # Registers a per-connection queue. Called when a connection is added.
25
28
  #
26
29
  # @param conn [Connection]
@@ -31,6 +34,7 @@ module OMQ
31
34
  @queues << q
32
35
  end
33
36
 
37
+
34
38
  # Removes the per-connection queue for a disconnected peer.
35
39
  #
36
40
  # If the queue is empty it is removed immediately. If it still has
@@ -47,6 +51,7 @@ module OMQ
47
51
  # Non-empty orphaned queues stay in @queues until drained
48
52
  end
49
53
 
54
+
50
55
  # Wakes a blocked #dequeue. Called by SignalingQueue after each enqueue.
51
56
  #
52
57
  def signal
@@ -54,6 +59,7 @@ module OMQ
54
59
  @condition.signal
55
60
  end
56
61
 
62
+
57
63
  # Returns the next message from any per-connection queue, in fair
58
64
  # round-robin order. Blocks until a message is available.
59
65
  #
@@ -81,6 +87,7 @@ module OMQ
81
87
  end
82
88
  end
83
89
 
90
+
84
91
  # Injects a nil sentinel to unblock a waiting #dequeue.
85
92
  # Called by Engine on close or fatal error.
86
93
  #
@@ -89,6 +96,7 @@ module OMQ
89
96
  @condition.signal
90
97
  end
91
98
 
99
+
92
100
  # @return [Boolean]
93
101
  #
94
102
  def empty?
@@ -126,18 +134,38 @@ module OMQ
126
134
  # signals the FairQueue to wake a blocked #dequeue.
127
135
  #
128
136
  class SignalingQueue
137
+ # @param inner [Async::LimitedQueue] the per-connection bounded queue
138
+ # @param fair_queue [FairQueue] the parent fair queue to signal on enqueue
139
+ #
129
140
  def initialize(inner, fair_queue)
130
141
  @inner = inner
131
142
  @fair = fair_queue
132
143
  end
133
144
 
145
+
146
+ # Enqueues a message and signals the fair queue.
147
+ #
148
+ # @param msg [Array<String>]
149
+ # @return [void]
150
+ #
134
151
  def enqueue(msg)
135
152
  @inner.enqueue(msg)
136
153
  @fair.signal
137
154
  end
138
155
 
156
+
157
+ # @param timeout [Numeric, nil] dequeue timeout
158
+ # @return [Array<String>, nil]
159
+ #
139
160
  def dequeue(timeout: nil) = @inner.dequeue(timeout: timeout)
161
+
162
+ # @return [Boolean]
163
+ #
140
164
  def empty? = @inner.empty?
165
+
166
+ # @param item [Object, nil]
167
+ # @return [void]
168
+ #
141
169
  def push(item) = @inner.push(item)
142
170
  end
143
171
  end
@@ -17,10 +17,11 @@ module OMQ
17
17
  # their #initialize.
18
18
  #
19
19
  module FanOut
20
+ # @return [Async::Promise] resolves when the first subscriber joins
21
+ #
20
22
  attr_reader :subscriber_joined
21
23
 
22
- # True when all per-connection send queues are empty.
23
- # Used by Engine#drain_send_queues during linger.
24
+ # @return [Boolean] true when all per-connection send queues are empty
24
25
  #
25
26
  def send_queues_drained?
26
27
  @conn_queues.values.all?(&:empty?)
@@ -31,6 +32,7 @@ module OMQ
31
32
  def init_fan_out(engine)
32
33
  @connections = Set.new
33
34
  @subscriptions = {} # connection => Set of prefixes
35
+ @subscribe_all = Set.new # connections subscribed to "" (match-all fast path)
34
36
  @conn_queues = {} # connection => per-connection send queue
35
37
  @conn_send_tasks = {} # connection => send pump task
36
38
  @conflate = engine.options.conflate
@@ -42,11 +44,13 @@ module OMQ
42
44
  # @return [Boolean] whether the connection is subscribed to the topic
43
45
  #
44
46
  def subscribed?(conn, topic)
47
+ return true if @subscribe_all.include?(conn)
45
48
  subs = @subscriptions[conn]
46
49
  return false unless subs
47
50
  subs.any? { |prefix| topic.start_with?(prefix) }
48
51
  end
49
52
 
53
+
50
54
  # Called when a subscription command is received from a peer.
51
55
  # Override in subclasses to expose subscriptions to the
52
56
  # application (e.g. XPUB enqueues to recv_queue).
@@ -56,9 +60,11 @@ module OMQ
56
60
  #
57
61
  def on_subscribe(conn, prefix)
58
62
  @subscriptions[conn] << prefix.b.freeze
63
+ @subscribe_all.add(conn) if prefix.empty?
59
64
  @subscriber_joined.resolve(conn) unless @subscriber_joined.resolved?
60
65
  end
61
66
 
67
+
62
68
  # Called when a cancel command is received from a peer.
63
69
  # Override in subclasses (e.g. XPUB enqueues to recv_queue).
64
70
  #
@@ -67,6 +73,7 @@ module OMQ
67
73
  #
68
74
  def on_cancel(conn, prefix)
69
75
  @subscriptions[conn]&.delete(prefix)
76
+ @subscribe_all.delete(conn) if prefix.empty?
70
77
  end
71
78
 
72
79
 
@@ -88,6 +95,7 @@ module OMQ
88
95
  # @param conn [Connection]
89
96
  #
90
97
  def remove_fan_out_send_connection(conn)
98
+ @subscribe_all.delete(conn)
91
99
  @conn_queues.delete(conn)
92
100
  @conn_send_tasks.delete(conn)&.stop
93
101
  end
@@ -108,7 +116,7 @@ module OMQ
108
116
  #
109
117
  def fan_out_enqueue(parts)
110
118
  @connections.each do |conn|
111
- @conn_queues[conn]&.enqueue(parts)
119
+ @conn_queues[conn].enqueue(parts)
112
120
  end
113
121
  end
114
122
 
@@ -120,8 +128,10 @@ module OMQ
120
128
  next unless frame.command?
121
129
  cmd = Protocol::ZMTP::Codec::Command.from_body(frame.body)
122
130
  case cmd.name
123
- when "SUBSCRIBE" then on_subscribe(conn, cmd.data)
124
- when "CANCEL" then on_cancel(conn, cmd.data)
131
+ when "SUBSCRIBE"
132
+ on_subscribe(conn, cmd.data)
133
+ when "CANCEL"
134
+ on_cancel(conn, cmd.data)
125
135
  end
126
136
  end
127
137
  rescue *CONNECTION_LOST
@@ -131,7 +141,7 @@ module OMQ
131
141
 
132
142
 
133
143
  # Starts a dedicated send pump for one subscriber connection.
134
- # Uses write_wire (pre-encoded bytes) for non-CURVE TCP connections
144
+ # Uses write_wire (pre-encoded bytes) for non-encrypted TCP connections
135
145
  # to avoid re-encoding the same message N times during fan-out.
136
146
  # In conflate mode, drains the batch and keeps only the latest
137
147
  # message per topic before writing.
@@ -140,12 +150,13 @@ module OMQ
140
150
  # @param q [Async::LimitedQueue, DropQueue]
141
151
  #
142
152
  def start_conn_send_pump(conn, q)
143
- use_wire = conn.respond_to?(:write_wire) && !(conn.respond_to?(:curve?) && conn.curve?)
153
+ use_wire = conn.respond_to?(:write_wire) && !conn.encrypted?
144
154
  task = @conflate ? start_conn_send_pump_conflate(conn, q) : start_conn_send_pump_normal(conn, q, use_wire)
145
155
  @conn_send_tasks[conn] = task
146
156
  @tasks << task
147
157
  end
148
158
 
159
+
149
160
  def start_conn_send_pump_normal(conn, q, use_wire)
150
161
  @engine.spawn_pump_task(annotation: "send pump") do
151
162
  loop do
@@ -159,6 +170,7 @@ module OMQ
159
170
  end
160
171
  end
161
172
 
173
+
162
174
  def write_matching_batch(conn, batch, use_wire)
163
175
  sent = false
164
176
  batch.each do |parts|
@@ -23,6 +23,7 @@ module OMQ
23
23
  @tasks = []
24
24
  end
25
25
 
26
+
26
27
  # @return [FairQueue]
27
28
  #
28
29
  attr_reader :recv_queue
@@ -45,6 +46,7 @@ module OMQ
45
46
  end
46
47
  end
47
48
 
49
+
48
50
  # @param connection [Connection]
49
51
  #
50
52
  def connection_removed(connection)
@@ -57,6 +59,7 @@ module OMQ
57
59
  end
58
60
  end
59
61
 
62
+
60
63
  # @param parts [Array<String>]
61
64
  #
62
65
  def enqueue(parts)
@@ -70,13 +73,18 @@ module OMQ
70
73
  end
71
74
  end
72
75
 
76
+
77
+ # Stops all background tasks.
78
+ #
79
+ # @return [void]
73
80
  #
74
81
  def stop
75
82
  @tasks.each(&:stop)
76
83
  @tasks.clear
77
84
  end
78
85
 
79
- # True when the staging and send queues are empty.
86
+
87
+ # @return [Boolean] true when the staging and send queues are empty
80
88
  #
81
89
  def send_queues_drained?
82
90
  @staging_queue.empty? && (@send_queue.nil? || @send_queue.empty?)
@@ -19,12 +19,14 @@ module OMQ
19
19
  init_fan_out(engine)
20
20
  end
21
21
 
22
+
22
23
  # PUB is write-only.
23
24
  #
24
25
  def recv_queue
25
26
  raise "PUB sockets cannot receive"
26
27
  end
27
28
 
29
+
28
30
  # @param connection [Connection]
29
31
  #
30
32
  def connection_added(connection)
@@ -34,6 +36,7 @@ module OMQ
34
36
  add_fan_out_send_connection(connection)
35
37
  end
36
38
 
39
+
37
40
  # @param connection [Connection]
38
41
  #
39
42
  def connection_removed(connection)
@@ -42,12 +45,17 @@ module OMQ
42
45
  remove_fan_out_send_connection(connection)
43
46
  end
44
47
 
48
+
45
49
  # @param parts [Array<String>]
46
50
  #
47
51
  def enqueue(parts)
48
52
  fan_out_enqueue(parts)
49
53
  end
50
54
 
55
+
56
+ # Stops all background tasks.
57
+ #
58
+ # @return [void]
51
59
  #
52
60
  def stop
53
61
  @tasks.each(&:stop)
@@ -14,6 +14,7 @@ module OMQ
14
14
  @tasks = []
15
15
  end
16
16
 
17
+
17
18
  # @return [FairQueue]
18
19
  #
19
20
  attr_reader :recv_queue
@@ -24,6 +25,7 @@ module OMQ
24
25
  add_fair_recv_connection(connection)
25
26
  end
26
27
 
28
+
27
29
  # @param connection [Connection]
28
30
  #
29
31
  def connection_removed(connection)
@@ -31,12 +33,17 @@ module OMQ
31
33
  # recv pump stops on EOFError
32
34
  end
33
35
 
36
+
34
37
  # PULL is read-only.
35
38
  #
36
39
  def enqueue(_parts)
37
40
  raise "PULL sockets cannot send"
38
41
  end
39
42
 
43
+
44
+ # Stops all background tasks.
45
+ #
46
+ # @return [void]
40
47
  #
41
48
  def stop
42
49
  @tasks.each(&:stop)