Skip to content

Commit aab64f9

Browse files
committed
🧵 Fix deadlock in #disconnect
`#disconnect` could deadlock when called inside the mutex, because the receiver thread could be signaling a condition variable and it would thus be unable to resume until the current thread releases its lock. Also, `#disconnect` shouldn't raise `IO#shutdown` exceptions on the receiver thread prior to closing the socket, when it is being called from the receiver thread. The exception is still raised, _after_ the socket is closed.
1 parent 49a44d6 commit aab64f9

1 file changed

Lines changed: 6 additions & 4 deletions

File tree

lib/net/imap.rb

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1190,22 +1190,24 @@ def tls_verified?; @tls_verified end
11901190

11911191
# Disconnects from the server.
11921192
#
1193-
# Waits for receiver thread to close before returning. Slow or stuck
1194-
# response handlers can cause #disconnect to hang until they complete.
1193+
# Waits for receiver thread to close before returning, except when called
1194+
# from inside the connection mutex such as from a response handler. Slow or
1195+
# stuck response handlers can cause #disconnect to hang until they complete.
11951196
#
11961197
# Related: #logout, #logout!
11971198
def disconnect
11981199
in_logout_state = try_state_logout?
11991200
return if disconnected?
1201+
in_receiver_thread = Thread.current == @receiver_thread
12001202
begin
12011203
@sock.to_io.shutdown
12021204
rescue Errno::ENOTCONN
12031205
# ignore `Errno::ENOTCONN: Socket is not connected' on some platforms.
12041206
rescue Exception => e
1205-
@receiver_thread.raise(e)
1207+
@receiver_thread.raise(e) unless in_receiver_thread
12061208
end
12071209
@sock.close
1208-
@receiver_thread.join
1210+
@receiver_thread.join unless mon_owned? || in_receiver_thread
12091211
raise e if e
12101212
ensure
12111213
# Try again after shutting down the receiver thread. With no reciever

0 commit comments

Comments
 (0)