Skip to content

Commit 8640a42

Browse files
authored
Add interruptible sleep to avoid deadlocking (#53)
* Add interruptible sleep to avoid deadlocking Stolen from rosa in SolidQueue * use permit_concurrent_loads just in case
1 parent 869bbd2 commit 8640a42

File tree

1 file changed

+40
-3
lines changed

1 file changed

+40
-3
lines changed

lib/action_cable/subscription_adapter/solid_cable.rb

+40-3
Original file line numberDiff line numberDiff line change
@@ -50,16 +50,22 @@ def initialize(event_loop)
5050
end
5151

5252
def listen
53-
while running?
53+
loop do
54+
break unless running?
55+
5456
with_polling_volume { broadcast_messages }
5557

56-
sleep ::SolidCable.polling_interval
58+
interruptible_sleep ::SolidCable.polling_interval
5759
end
5860
end
5961

6062
def shutdown
6163
self.running = false
62-
Thread.pass while thread.alive?
64+
wake_up
65+
66+
ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
67+
thread&.join
68+
end
6369
end
6470

6571
def add_channel(channel, on_success)
@@ -112,6 +118,37 @@ def with_polling_volume
112118
yield
113119
end
114120
end
121+
122+
def wake_up
123+
interrupt
124+
end
125+
126+
SELF_PIPE_BLOCK_SIZE = 11
127+
128+
def interrupt
129+
self_pipe[:writer].write_nonblock(".")
130+
rescue Errno::EAGAIN, Errno::EINTR
131+
# Ignore writes that would block and retry
132+
# if another signal arrived while writing
133+
retry
134+
end
135+
136+
def interruptible_sleep(time)
137+
if time > 0 && self_pipe[:reader].wait_readable(time)
138+
loop { self_pipe[:reader].read_nonblock(SELF_PIPE_BLOCK_SIZE) }
139+
end
140+
rescue Errno::EAGAIN, Errno::EINTR
141+
end
142+
143+
# Self-pipe for signal-handling (http://cr.yp.to/docs/selfpipe.html)
144+
def self_pipe
145+
@self_pipe ||= create_self_pipe
146+
end
147+
148+
def create_self_pipe
149+
reader, writer = IO.pipe
150+
{ reader: reader, writer: writer }
151+
end
115152
end
116153
end
117154
end

0 commit comments

Comments
 (0)