New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Puma listen thread can fail with infinite loop. #2699
Comments
I just whipped up a quick PR to try and fix this, but we should probably write some tests too. |
Need more coffee, I'll see about a test... Thanks. |
I'm just seeing the infinite loop when Puma is shutdown. Do you see the same? Using a rackup of: require 'objspace'
run lambda { |env|
ios = ObjectSpace.each_object(::TCPServer).to_a.tap { |a| a.each(&:close) }
[200, [], [ios.inspect]]
} curl connects the first time, and then freezes. Or, the first connection deletes the listener sockets, but Puma keeps running, and only detects the closure when it's shutdown. Trying to see if there's a simple way to detect the closure while it's running... |
I'm not sure the exact sequence of events but it might depend on how puma is started. |
I started the server, I think using both single (no workers) and clustered (workers), and did requests with curl. The first request closed the listener sockets, the second hung, as there was nothing to accept the connection. When I stopped the server with Ctrl-C, I then got the loop. I'd like to know how/why the production system shut the listener socket... Regardless, I think we may need another thread to check whether any listener sockets have closed, then a means of re-opening them, which needs to work a bit differently for single and clustered. Or, since it's unlikely that Puma closed the listeners, we consider it an external issue. Don't know. Then again, I have stated something like 'nothing should stop Puma' in the past... |
We suspect thread corruption within a C extension but we don't know for sure yet. It's just one theory. Another is Ruby code doing something wonky with I don't mind if "nothing stops puma" but my gut feeling is we should be very careful about having a catch all exception handler in a non-terminating loop. We can try to solve both problems, but my main concern is the latter. |
* More elaborate exception handling. Fixes #2699. * Add TestIntegrationSingle#test_closed_listener Co-authored-by: MSP-Greg <Greg.mpls@gmail.com>
* More elaborate exception handling. Fixes puma#2699. * Add TestIntegrationSingle#test_closed_listener Co-authored-by: MSP-Greg <Greg.mpls@gmail.com>
The following middleware can cause Puma to enter an infinite loop:
This probably seems odd, but we are actually seeing some variant of this in a production system. We don't know who is calling close (suspect threading bug) but it causes an EBADF error in the list loop, followed by infinite
IOError
s:At the very least, we should probably handle this more gracefully (i.e. exit the loop as soon as the listening socket becomes invalid for any reason, rather than trying to select/accept again).
The text was updated successfully, but these errors were encountered: