Skip to content

Commit 1c15dcd

Browse files
committed
Close other client sockets in application forks
All client unix sockets are initiated at the spring application process before forking to serve each client. A reference to the client socket remains active by the thread in the wait method waiting for the client's fork to exit. This is problematic in cases with more than one parallel clients because the client socket for the first client is also present in the fork for the second client due to fd inheritance from the spring application parent process. With the first client's socket being present in the second client's fork, the first client cannot exit gracefully because a reference to its socket remains open in the fork for the second client leading to rails console hanging for the first client until the second client gets terminated. The problem can be reproduced by opening 2 rails consoles on spring & attempting to exit the first console while the second is still active.
1 parent 577cf01 commit 1c15dcd

File tree

2 files changed

+9
-0
lines changed

2 files changed

+9
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
## Next Release
22

3+
* Fix bug which makes rails consoles to hang at exit when multiple of them are open (#647)
4+
35
## 2.1.1
46

57
* Avoid -I rubylibdir with default-gem bundler

lib/spring/application.rb

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ def initialize(manager, original_env, spring_env = Env.new)
1212
@spring_env = spring_env
1313
@mutex = Mutex.new
1414
@waiting = Set.new
15+
@clients = Set.new
1516
@preloaded = false
1617
@state = :initialized
1718
@interrupt = IO.pipe
@@ -151,6 +152,8 @@ def serve(client)
151152
log "got client"
152153
manager.puts
153154

155+
@clients << client
156+
154157
_stdout, stderr, _stdin = streams = 3.times.map { client.recv_io }
155158
[STDOUT, STDERR, STDIN].zip(streams).each { |a, b| a.reopen(b) }
156159

@@ -167,6 +170,10 @@ def serve(client)
167170
end
168171

169172
pid = fork {
173+
# Make sure to close other clients otherwise their graceful termination
174+
# will be impossible due to reference from this fork.
175+
@clients.select { |c| c != client }.each(&:close)
176+
170177
Process.setsid
171178
IGNORE_SIGNALS.each { |sig| trap(sig, "DEFAULT") }
172179
trap("TERM", "DEFAULT")

0 commit comments

Comments
 (0)