redis_queued_locks 1.7.0 → 1.9.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/.ruby-version +1 -1
- data/CHANGELOG.md +60 -1
- data/README.md +485 -46
- data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue/log_visitor.rb +4 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/dequeue_from_lock_queue.rb +4 -1
- data/lib/redis_queued_locks/acquier/acquire_lock/instr_visitor.rb +20 -5
- data/lib/redis_queued_locks/acquier/acquire_lock/log_visitor.rb +24 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock/log_visitor.rb +56 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/try_to_lock.rb +37 -30
- data/lib/redis_queued_locks/acquier/acquire_lock/with_acq_timeout.rb +41 -7
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire/log_visitor.rb +8 -0
- data/lib/redis_queued_locks/acquier/acquire_lock/yield_expire.rb +21 -9
- data/lib/redis_queued_locks/acquier/acquire_lock.rb +61 -22
- data/lib/redis_queued_locks/acquier/clear_dead_requests.rb +5 -1
- data/lib/redis_queued_locks/acquier/extend_lock_ttl.rb +5 -1
- data/lib/redis_queued_locks/acquier/lock_info.rb +4 -3
- data/lib/redis_queued_locks/acquier/locks.rb +2 -2
- data/lib/redis_queued_locks/acquier/queue_info.rb +2 -2
- data/lib/redis_queued_locks/acquier/release_all_locks.rb +12 -2
- data/lib/redis_queued_locks/acquier/release_lock.rb +12 -2
- data/lib/redis_queued_locks/client.rb +320 -10
- data/lib/redis_queued_locks/errors.rb +8 -0
- data/lib/redis_queued_locks/instrument.rb +8 -1
- data/lib/redis_queued_locks/logging.rb +8 -1
- data/lib/redis_queued_locks/resource.rb +59 -1
- data/lib/redis_queued_locks/swarm/acquirers.rb +44 -0
- data/lib/redis_queued_locks/swarm/flush_zombies.rb +133 -0
- data/lib/redis_queued_locks/swarm/probe_hosts.rb +69 -0
- data/lib/redis_queued_locks/swarm/redis_client_builder.rb +67 -0
- data/lib/redis_queued_locks/swarm/supervisor.rb +83 -0
- data/lib/redis_queued_locks/swarm/swarm_element/isolated.rb +287 -0
- data/lib/redis_queued_locks/swarm/swarm_element/threaded.rb +351 -0
- data/lib/redis_queued_locks/swarm/swarm_element.rb +8 -0
- data/lib/redis_queued_locks/swarm/zombie_info.rb +145 -0
- data/lib/redis_queued_locks/swarm.rb +241 -0
- data/lib/redis_queued_locks/utilities/lock.rb +22 -0
- data/lib/redis_queued_locks/utilities.rb +75 -0
- data/lib/redis_queued_locks/version.rb +2 -2
- data/lib/redis_queued_locks.rb +2 -0
- data/redis_queued_locks.gemspec +6 -10
- metadata +24 -6
- data/lib/redis_queued_locks/watcher.rb +0 -1
@@ -0,0 +1,287 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 1.9.0
|
5
|
+
# rubocop:disable Metrics/ClassLength
|
6
|
+
class RedisQueuedLocks::Swarm::SwarmElement::Isolated
|
7
|
+
# @since 1.9.0
|
8
|
+
include RedisQueuedLocks::Utilities
|
9
|
+
|
10
|
+
# @return [RedisQueuedLocks::Client]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
# @since 1.9.0
|
14
|
+
attr_reader :rql_client
|
15
|
+
|
16
|
+
# @return [Ractor,NilClass]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
# @since 1.9.0
|
20
|
+
attr_reader :swarm_element
|
21
|
+
|
22
|
+
# @return [RedisQueuedLocks::Utilities::Lock]
|
23
|
+
#
|
24
|
+
# @api private
|
25
|
+
# @since 1.9.0
|
26
|
+
attr_reader :sync
|
27
|
+
|
28
|
+
# @return [void]
|
29
|
+
#
|
30
|
+
# @api private
|
31
|
+
# @since 1.9.0
|
32
|
+
def initialize(rql_client)
|
33
|
+
@rql_client = rql_client
|
34
|
+
@swarm_element = nil
|
35
|
+
@sync = RedisQueuedLocks::Utilities::Lock.new
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [void]
|
39
|
+
#
|
40
|
+
# @api private
|
41
|
+
# @since 1.9.0
|
42
|
+
def try_swarm!
|
43
|
+
return unless enabled?
|
44
|
+
|
45
|
+
sync.synchronize do
|
46
|
+
swarm_loop__kill
|
47
|
+
swarm!
|
48
|
+
swarm_loop__start
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# @return [void]
|
53
|
+
#
|
54
|
+
# @api private
|
55
|
+
# @since 1.9.0
|
56
|
+
def reswarm_if_dead!
|
57
|
+
return unless enabled?
|
58
|
+
|
59
|
+
sync.synchronize do
|
60
|
+
if swarmed__stopped?
|
61
|
+
swarm_loop__start
|
62
|
+
elsif swarmed__dead? || idle?
|
63
|
+
swarm!
|
64
|
+
swarm_loop__start
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [void]
|
70
|
+
#
|
71
|
+
# @api private
|
72
|
+
# @since 1.9.0
|
73
|
+
def try_kill!
|
74
|
+
sync.synchronize do
|
75
|
+
swarm_loop__kill
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# @return [Boolean]
|
80
|
+
#
|
81
|
+
# @api private
|
82
|
+
# @since 1.9.0
|
83
|
+
def enabled?
|
84
|
+
# NOTE: provide an <is enabled> logic here by analyzing the redis queued locks config.
|
85
|
+
end
|
86
|
+
|
87
|
+
# @return [Hash<Symbol,Boolean|Hash<Symbol,String|Boolean>>]
|
88
|
+
# Format: {
|
89
|
+
# enabled: <Boolean>,
|
90
|
+
# ractor: {
|
91
|
+
# running: <Boolean>,
|
92
|
+
# state: <String>,
|
93
|
+
# },
|
94
|
+
# main_loop: {
|
95
|
+
# running: <Boolean>,
|
96
|
+
# state: <String>
|
97
|
+
# }
|
98
|
+
# }
|
99
|
+
#
|
100
|
+
# @api private
|
101
|
+
# @since 1.9.0
|
102
|
+
def status
|
103
|
+
sync.synchronize do
|
104
|
+
ractor_running = swarmed__alive?
|
105
|
+
ractor_state = swarmed? ? ractor_status(swarm_element) : 'non_initialized'
|
106
|
+
|
107
|
+
main_loop_running = nil
|
108
|
+
main_loop_state = nil
|
109
|
+
begin
|
110
|
+
main_loop_running = swarmed__running?
|
111
|
+
main_loop_state =
|
112
|
+
main_loop_running ? swarm_loop__status[:main_loop][:state] : 'non_initialized'
|
113
|
+
rescue Ractor::ClosedError
|
114
|
+
# NOTE: it can happend when you run RedisQueuedLocks::Swarm#deswarm!;
|
115
|
+
main_loop_running = false
|
116
|
+
main_loop_state = 'non_initialized'
|
117
|
+
end
|
118
|
+
|
119
|
+
{
|
120
|
+
enabled: enabled?,
|
121
|
+
ractor: {
|
122
|
+
running: ractor_running,
|
123
|
+
state: ractor_state
|
124
|
+
},
|
125
|
+
main_loop: {
|
126
|
+
running: main_loop_running,
|
127
|
+
state: main_loop_state
|
128
|
+
}
|
129
|
+
}
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
private
|
134
|
+
|
135
|
+
# Swarm element lifecycle have the following scheme:
|
136
|
+
# => 1) init (swarm!): create a ractor, main loop is not started;
|
137
|
+
# => 2) start (swarm_loop__start!): run main lopp inside the ractor;
|
138
|
+
# => 3) stop (swarm_loop__stop!): stop the main loop inside a ractor;
|
139
|
+
# => 4) kill (swarm_loop__kill!): kill the main loop inside teh ractor and kill a ractor;
|
140
|
+
#
|
141
|
+
# @return [void]
|
142
|
+
#
|
143
|
+
# @api private
|
144
|
+
# @since 1.9.0
|
145
|
+
def swarm!
|
146
|
+
# IMPORTANT №1: initialize @swarm_element here with Ractor;
|
147
|
+
# IMPORTANT №2: your Ractor should invoke .swarm_loop inside (see below);
|
148
|
+
# IMPORTANT №3: you should pass the main loop logic as a block to .swarm_loop;
|
149
|
+
end
|
150
|
+
|
151
|
+
# @param main_loop_spawner [Block]
|
152
|
+
# @return [void]
|
153
|
+
#
|
154
|
+
# @api private
|
155
|
+
# @since 1.9.0
|
156
|
+
# rubocop:disable Layout/ClassStructure, Lint/IneffectiveAccessModifier
|
157
|
+
def self.swarm_loop(&main_loop_spawner)
|
158
|
+
# NOTE:
|
159
|
+
# This self.-related part of code is placed in the middle of class in order
|
160
|
+
# to provide better code readability (it is placed next to the method inside
|
161
|
+
# wich it should be called (see #swarm!)). That's why some rubocop cops are disabled.
|
162
|
+
|
163
|
+
main_loop = nil
|
164
|
+
|
165
|
+
loop do
|
166
|
+
command = Ractor.receive
|
167
|
+
|
168
|
+
case command
|
169
|
+
when :status
|
170
|
+
main_loop_alive = main_loop != nil && main_loop.alive?
|
171
|
+
main_loop_state =
|
172
|
+
if main_loop == nil
|
173
|
+
'non_initialized'
|
174
|
+
else
|
175
|
+
# NOTE: (full name resolution): ractor has no syntax-based constatnt context;
|
176
|
+
RedisQueuedLocks::Utilities.thread_state(main_loop)
|
177
|
+
end
|
178
|
+
Ractor.yield({ main_loop: { alive: main_loop_alive, state: main_loop_state } })
|
179
|
+
when :is_active
|
180
|
+
Ractor.yield(main_loop != nil && main_loop.alive?)
|
181
|
+
when :start
|
182
|
+
main_loop.kill if main_loop != nil
|
183
|
+
main_loop = yield # REFERENCE: `main_loop_spawner.call`
|
184
|
+
when :stop
|
185
|
+
main_loop.kill if main_loop != nil
|
186
|
+
when :kill
|
187
|
+
main_loop.kill if main_loop != nil
|
188
|
+
exit
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
# rubocop:enable Layout/ClassStructure, Lint/IneffectiveAccessModifier
|
193
|
+
|
194
|
+
# @return [Boolean]
|
195
|
+
#
|
196
|
+
# @api private
|
197
|
+
# @since 1.9.0
|
198
|
+
def idle?
|
199
|
+
swarm_element == nil
|
200
|
+
end
|
201
|
+
|
202
|
+
# @return [Boolean]
|
203
|
+
#
|
204
|
+
# @api private
|
205
|
+
# @since 1.9.0
|
206
|
+
def swarmed?
|
207
|
+
swarm_element != nil
|
208
|
+
end
|
209
|
+
|
210
|
+
# @return [Boolean]
|
211
|
+
#
|
212
|
+
# @api private
|
213
|
+
# @since 1.9.0
|
214
|
+
def swarmed__alive?
|
215
|
+
swarm_element != nil && ractor_alive?(swarm_element)
|
216
|
+
end
|
217
|
+
|
218
|
+
# @return [Boolean]
|
219
|
+
#
|
220
|
+
# @api private
|
221
|
+
# @since 1.9.0
|
222
|
+
def swarmed__dead?
|
223
|
+
swarm_element != nil && !ractor_alive?(swarm_element)
|
224
|
+
end
|
225
|
+
|
226
|
+
# @return [Boolean]
|
227
|
+
#
|
228
|
+
# @api private
|
229
|
+
# @since 1.9.0
|
230
|
+
def swarmed__running?
|
231
|
+
swarm_element != nil && ractor_alive?(swarm_element) && swarm_loop__is_active
|
232
|
+
end
|
233
|
+
|
234
|
+
# @return [Boolean]
|
235
|
+
#
|
236
|
+
# @api private
|
237
|
+
# @since 1.9.0
|
238
|
+
def swarmed__stopped?
|
239
|
+
swarm_element != nil && ractor_alive?(swarm_element) && !swarm_loop__is_active
|
240
|
+
end
|
241
|
+
|
242
|
+
# @return [Boolean]
|
243
|
+
#
|
244
|
+
# @api private
|
245
|
+
# @since 1.9.0
|
246
|
+
def swarm_loop__is_active
|
247
|
+
return if idle? || swarmed__dead?
|
248
|
+
sync.synchronize { swarm_element.send(:is_active).take }
|
249
|
+
end
|
250
|
+
|
251
|
+
# @return [Hash]
|
252
|
+
#
|
253
|
+
# @api private
|
254
|
+
# @since 1.9.0
|
255
|
+
def swarm_loop__status
|
256
|
+
return if idle? || swarmed__dead?
|
257
|
+
sync.synchronize { swarm_element.send(:status).take }
|
258
|
+
end
|
259
|
+
|
260
|
+
# @return [void]
|
261
|
+
#
|
262
|
+
# @api private
|
263
|
+
# @since 1.9.0
|
264
|
+
def swarm_loop__start
|
265
|
+
return if idle? || swarmed__dead?
|
266
|
+
sync.synchronize { swarm_element.send(:start) }
|
267
|
+
end
|
268
|
+
|
269
|
+
# @return [void]
|
270
|
+
#
|
271
|
+
# @api private
|
272
|
+
# @since 1.9.0
|
273
|
+
def swarm_loop__pause
|
274
|
+
return if idle? || swarmed__dead?
|
275
|
+
sync.synchronize { swarm_element.send(:stop) }
|
276
|
+
end
|
277
|
+
|
278
|
+
# @return [void]
|
279
|
+
#
|
280
|
+
# @api private
|
281
|
+
# @since 1.9.0
|
282
|
+
def swarm_loop__kill
|
283
|
+
return if idle? || swarmed__dead?
|
284
|
+
sync.synchronize { swarm_element.send(:kill) }
|
285
|
+
end
|
286
|
+
end
|
287
|
+
# rubocop:enable Metrics/ClassLength
|
@@ -0,0 +1,351 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# @api private
|
4
|
+
# @since 1.9.0
|
5
|
+
# rubocop:disable Metrics/ClassLength
|
6
|
+
class RedisQueuedLocks::Swarm::SwarmElement::Threaded
|
7
|
+
# @since 1.9.0
|
8
|
+
include RedisQueuedLocks::Utilities
|
9
|
+
|
10
|
+
# @return [RedisQueuedLocks::Client]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
# @since 1.9.0
|
14
|
+
attr_reader :rql_client
|
15
|
+
|
16
|
+
# @return [Thread,NilClass]
|
17
|
+
#
|
18
|
+
# @api private
|
19
|
+
# @since 1.9.0
|
20
|
+
attr_reader :swarm_element
|
21
|
+
|
22
|
+
# The main loop reference is only used inside the swarm element thread and
|
23
|
+
# swarm element termination. It should not be used everywhere else!
|
24
|
+
# This is strongly technical variable refenrece.
|
25
|
+
#
|
26
|
+
# @return [Thread,NilClass]
|
27
|
+
#
|
28
|
+
# @api private
|
29
|
+
# @since 1.9.0
|
30
|
+
attr_reader :main_loop
|
31
|
+
|
32
|
+
# @return [Thread::SizedQueue,NilClass]
|
33
|
+
#
|
34
|
+
# @api private
|
35
|
+
# @since 1.9.0
|
36
|
+
attr_reader :swarm_element_commands
|
37
|
+
|
38
|
+
# @return [Thread::SizedQueue]
|
39
|
+
#
|
40
|
+
# @api private
|
41
|
+
# @since 1.9.0
|
42
|
+
attr_reader :swarm_element_results
|
43
|
+
|
44
|
+
# @return [RedisQueuedLocks::Utilities::Lock]
|
45
|
+
#
|
46
|
+
# @api private
|
47
|
+
# @since 1.9.0
|
48
|
+
attr_reader :sync
|
49
|
+
|
50
|
+
# @param rql_client [RedisQueuedLocks::Client]
|
51
|
+
# @return [void]
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
# @since 1.9.0
|
55
|
+
def initialize(rql_client)
|
56
|
+
@rql_client = rql_client
|
57
|
+
@swarm_element = nil
|
58
|
+
@main_loop = nil # NOTE: strongly technical variable refenrece (see attr_reader docs)
|
59
|
+
@swarm_element_commands = nil
|
60
|
+
@swarm_element_results = nil
|
61
|
+
@sync = RedisQueuedLocks::Utilities::Lock.new
|
62
|
+
end
|
63
|
+
|
64
|
+
# @return [void]
|
65
|
+
#
|
66
|
+
# @api private
|
67
|
+
# @since 1.9.0
|
68
|
+
def try_swarm!
|
69
|
+
return unless enabled?
|
70
|
+
|
71
|
+
sync.synchronize do
|
72
|
+
swarm_element__termiante
|
73
|
+
swarm!
|
74
|
+
swarm_loop__start
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [void]
|
79
|
+
#
|
80
|
+
# @api private
|
81
|
+
# @since 19.0.0
|
82
|
+
def reswarm_if_dead!
|
83
|
+
return unless enabled?
|
84
|
+
|
85
|
+
sync.synchronize do
|
86
|
+
if swarmed__stopped?
|
87
|
+
swarm_loop__start
|
88
|
+
elsif swarmed__dead? || idle?
|
89
|
+
swarm!
|
90
|
+
swarm_loop__start
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @return [void]
|
96
|
+
#
|
97
|
+
# @api private
|
98
|
+
# @since 1.9.0
|
99
|
+
def try_kill!
|
100
|
+
sync.synchronize do
|
101
|
+
swarm_element__termiante
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [Boolean]
|
106
|
+
#
|
107
|
+
# @api private
|
108
|
+
# @since 1.9.0
|
109
|
+
def enabled?
|
110
|
+
# NOTE: provide an <is enabled> logic here by analyzing the redis queued locks config.
|
111
|
+
end
|
112
|
+
|
113
|
+
# @return [Hash<Symbol,Boolean|Hash<Symbol,String|Boolean>>]
|
114
|
+
# Format: {
|
115
|
+
# enabled: <Boolean>,
|
116
|
+
# thread: {
|
117
|
+
# running: <Boolean>,
|
118
|
+
# state: <String>,
|
119
|
+
# },
|
120
|
+
# main_loop: {
|
121
|
+
# running: <Boolean>,
|
122
|
+
# state: <String>
|
123
|
+
# }
|
124
|
+
# }
|
125
|
+
#
|
126
|
+
# @api private
|
127
|
+
# @since 1.9.0
|
128
|
+
# rubocop:disable Style/RedundantBegin
|
129
|
+
def status
|
130
|
+
sync.synchronize do
|
131
|
+
thread_running = swarmed__alive?
|
132
|
+
thread_state = swarmed? ? thread_state(swarm_element) : 'non_initialized'
|
133
|
+
|
134
|
+
main_loop_running = swarmed__running?
|
135
|
+
main_loop_state = begin
|
136
|
+
if main_loop_running
|
137
|
+
swarm_loop__status[:result][:main_loop][:state]
|
138
|
+
else
|
139
|
+
'non_initialized'
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
{
|
144
|
+
enabled: enabled?,
|
145
|
+
thread: {
|
146
|
+
running: thread_running,
|
147
|
+
state: thread_state
|
148
|
+
},
|
149
|
+
main_loop: {
|
150
|
+
running: main_loop_running,
|
151
|
+
state: main_loop_state
|
152
|
+
}
|
153
|
+
}
|
154
|
+
end
|
155
|
+
end
|
156
|
+
# rubocop:enable Style/RedundantBegin
|
157
|
+
|
158
|
+
private
|
159
|
+
|
160
|
+
# Swarm element lifecycle have the following flow:
|
161
|
+
# => 1) init (swarm!): create a thread, main loop is not started;
|
162
|
+
# => 2) start (swarm_loop__start): run the main lopp inside the created thread;
|
163
|
+
# => 3) stop (swarm_loop__stop): stop the main loop inside the created thread;
|
164
|
+
# => 4) terminate (swarm_element__termiante): kill the created thread;
|
165
|
+
# In addition you should implement `#spawn_main_loop!` method that will be the main logic
|
166
|
+
# of the your swarm element.
|
167
|
+
#
|
168
|
+
# @return [void]
|
169
|
+
#
|
170
|
+
# @api private
|
171
|
+
# @since 1.9.0
|
172
|
+
# rubocop:disable Metrics/MethodLength
|
173
|
+
def swarm!
|
174
|
+
# NOTE: kill the main loop at start to prevent any async-thread-race-based memory leaks;
|
175
|
+
main_loop&.kill
|
176
|
+
|
177
|
+
@swarm_element_commands = Thread::SizedQueue.new(1)
|
178
|
+
@swarm_element_results = Thread::SizedQueue.new(1)
|
179
|
+
|
180
|
+
@swarm_element = Thread.new do
|
181
|
+
loop do
|
182
|
+
command = swarm_element_commands.pop
|
183
|
+
|
184
|
+
case command
|
185
|
+
when :status
|
186
|
+
main_loop_alive = main_loop != nil && main_loop.alive?
|
187
|
+
main_loop_state = (main_loop == nil) ? 'non_initialized' : thread_state(main_loop)
|
188
|
+
swarm_element_results.push({
|
189
|
+
ok: true,
|
190
|
+
result: { main_loop: { alive: main_loop_alive, state: main_loop_state } }
|
191
|
+
})
|
192
|
+
when :is_active
|
193
|
+
is_active = main_loop != nil && main_loop.alive?
|
194
|
+
swarm_element_results.push({ ok: true, result: { is_active: } })
|
195
|
+
when :start
|
196
|
+
main_loop&.kill
|
197
|
+
@main_loop = spawn_main_loop!.tap { |thread| thread.abort_on_exception = false }
|
198
|
+
swarm_element_results.push({ ok: true, result: nil })
|
199
|
+
when :stop
|
200
|
+
main_loop&.kill
|
201
|
+
swarm_element_results.push({ ok: true, result: nil })
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
# rubocop:enable Metrics/MethodLength
|
207
|
+
|
208
|
+
# @return [Thread] Thread with #abort_onexception == false that wraps loop'ed logic;
|
209
|
+
#
|
210
|
+
# @api private
|
211
|
+
# @since 1.9.0
|
212
|
+
def spawn_main_loop!
|
213
|
+
# NOTE:
|
214
|
+
# - provide the swarm element looped logic here wrapped into the thread;
|
215
|
+
# - created thread will be reconfigured inside the swarm_element logic with a
|
216
|
+
# `abort_on_exception = false` (cuz the stauts of the thread is
|
217
|
+
# totally controlled by the @swarm_element's logic);
|
218
|
+
end
|
219
|
+
|
220
|
+
# @return [Boolean]
|
221
|
+
#
|
222
|
+
# @api private
|
223
|
+
# @since 1.9.0
|
224
|
+
def idle?
|
225
|
+
swarm_element == nil
|
226
|
+
end
|
227
|
+
|
228
|
+
# @return [Boolean]
|
229
|
+
#
|
230
|
+
# @api private
|
231
|
+
# @since 1.9.0
|
232
|
+
def swarmed?
|
233
|
+
swarm_element != nil
|
234
|
+
end
|
235
|
+
|
236
|
+
# @return [Boolean]
|
237
|
+
#
|
238
|
+
# @api private
|
239
|
+
# @since 1.9.0
|
240
|
+
def swarmed__alive?
|
241
|
+
swarmed? && swarm_element.alive? && !terminating?
|
242
|
+
end
|
243
|
+
|
244
|
+
# @return [Boolean]
|
245
|
+
#
|
246
|
+
# @api private
|
247
|
+
# @since 1.9.0
|
248
|
+
def swarmed__dead?
|
249
|
+
swarmed? && (!swarm_element.alive? || terminating?)
|
250
|
+
end
|
251
|
+
|
252
|
+
# @return [Boolean]
|
253
|
+
#
|
254
|
+
# @api private
|
255
|
+
# @since 1.9.0
|
256
|
+
def swarmed__running?
|
257
|
+
swarmed__alive? && !terminating? && (swarm_loop__is_active.yield_self do |result|
|
258
|
+
result && result[:ok] && result[:result][:is_active]
|
259
|
+
end)
|
260
|
+
end
|
261
|
+
|
262
|
+
# @return [Boolean,NilClass]
|
263
|
+
#
|
264
|
+
# @api private
|
265
|
+
# @since 1.9.0
|
266
|
+
# rubocop:disable Layout/MultilineOperationIndentation
|
267
|
+
def terminating?
|
268
|
+
swarm_element_commands == nil ||
|
269
|
+
swarm_element_commands&.closed? ||
|
270
|
+
swarm_element_results == nil ||
|
271
|
+
swarm_element_results&.closed?
|
272
|
+
end
|
273
|
+
# rubocop:enable Layout/MultilineOperationIndentation
|
274
|
+
|
275
|
+
# @return [Boolean]
|
276
|
+
#
|
277
|
+
# @api private
|
278
|
+
# @since 1.9.0
|
279
|
+
def swarmed__stopped?
|
280
|
+
swarmed__alive? && (terminating? || !(swarm_loop__is_active.yield_self do |result|
|
281
|
+
result && result[:ok] && result[:result][:is_active]
|
282
|
+
end))
|
283
|
+
end
|
284
|
+
|
285
|
+
# @return [Boolean,NilClass]
|
286
|
+
#
|
287
|
+
# @api private
|
288
|
+
# @since 1.9.0
|
289
|
+
def swarm_loop__is_active
|
290
|
+
return if idle? || swarmed__dead? || terminating?
|
291
|
+
sync.synchronize do
|
292
|
+
swarm_element_commands.push(:is_active)
|
293
|
+
swarm_element_results.pop
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
# @return [Hash,NilClass]
|
298
|
+
#
|
299
|
+
# @api private
|
300
|
+
# @since 1.9.0
|
301
|
+
def swarm_loop__status
|
302
|
+
return if idle? || swarmed__dead? || terminating?
|
303
|
+
sync.synchronize do
|
304
|
+
swarm_element_commands.push(:status)
|
305
|
+
swarm_element_results.pop
|
306
|
+
end
|
307
|
+
end
|
308
|
+
|
309
|
+
# @return [void]
|
310
|
+
#
|
311
|
+
# @api private
|
312
|
+
# @since 1.9.0
|
313
|
+
def swarm_loop__start
|
314
|
+
return if idle? || swarmed__dead? || terminating?
|
315
|
+
sync.synchronize do
|
316
|
+
swarm_element_commands.push(:start)
|
317
|
+
swarm_element_results.pop
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# @return [void]
|
322
|
+
#
|
323
|
+
# @api private
|
324
|
+
# @since 1.9.0
|
325
|
+
def swarm_loop__stop
|
326
|
+
return if idle? || swarmed__dead? || terminating?
|
327
|
+
sync.synchronize do
|
328
|
+
swarm_element_commands.push(:stop)
|
329
|
+
swarm_element_results.pop
|
330
|
+
end
|
331
|
+
end
|
332
|
+
|
333
|
+
# @return [void]
|
334
|
+
#
|
335
|
+
# @api private
|
336
|
+
# @since 1.9.0
|
337
|
+
def swarm_element__termiante
|
338
|
+
return if idle? || swarmed__dead?
|
339
|
+
sync.synchronize do
|
340
|
+
main_loop&.kill
|
341
|
+
swarm_element.kill
|
342
|
+
swarm_element_commands.close
|
343
|
+
swarm_element_results.close
|
344
|
+
swarm_element_commands.clear
|
345
|
+
swarm_element_results.clear
|
346
|
+
@swarm_element_commands = nil
|
347
|
+
@swarm_element_results = nil
|
348
|
+
end
|
349
|
+
end
|
350
|
+
end
|
351
|
+
# rubocop:enable Metrics/ClassLength
|