-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
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
option to not delay TCPServer socket creation #24115
Conversation
This change adds an option to TCPServer socket constructor to not delay creation of the socket. This will allow TCP servers to set socket options like `SO_REUSEPORT` before the socket is bound and listened on. With early initialization: ``` tan@tanlt ~/temp/soreuseport $ julia ./soreuseport.jl false & [1] 23061 tan@tanlt ~/temp/soreuseport $ creating socket bound to 8010... has reuseport tcp_reuseport returned 0 listening... accepting... tan@tanlt ~/temp/soreuseport $ julia ./soreuseport.jl false creating socket bound to 8010... has reuseport tcp_reuseport returned 0 listening... accepting... ``` But that is not possible with delayed initialization: ``` tan@tanlt ~/temp/soreuseport $ julia ./soreuseport.jl true & [1] 22977 tan@tanlt ~/temp/soreuseport $ creating socket bound to 8010... has reuseport tcp_reuseport returned -1 listening... accepting... tan@tanlt ~/temp/soreuseport $ julia ./soreuseport.jl true creating socket bound to 8010... has reuseport tcp_reuseport returned -1 listening... ERROR: LoadError: listen: address already in use (EADDRINUSE) Stacktrace: [1] uv_error at ./libuv.jl:68 [inlined] [2] #listen#369 at ./stream.jl:933 [inlined] [3] listen at ./stream.jl:933 [inlined] [4] run_server(::Bool) at /home/tan/temp/soreuseport/soreuseport.jl:13 [5] include_relative(::Module, ::String) at ./loading.jl:533 [6] include(::Module, ::String) at ./sysimg.jl:14 [7] process_options(::Base.JLOptions) at ./client.jl:325 [8] _start() at ./client.jl:391 in expression starting at /home/tan/temp/soreuseport/soreuseport.jl:22 ``` My `soreuseport.jl` is: ``` function run_server(delay) println("creating socket bound to 8010...") s = Base.TCPServer(; delay=delay) if ccall(:jl_has_so_reuseport, Int32, ()) == 1 println("has reuseport") rc = ccall(:jl_tcp_reuseport, Int32, (Ptr{Void},), s.handle) println("tcp_reuseport returned ", rc) else println("no reuseport") end bind(s, Base.InetAddr(ip"0.0.0.0", 8010)) println("listening...") listen(s) println("accepting...") s1 = accept(s) println("accepted, closing...") close(s1) close(s) println("done") end run_server(parse(Bool, ARGS[1])) ```
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM. Needs docs and ideally a test even if only to test that the keyword is accepted, if not that it does something in particular.
if ccall(:jl_has_so_reuseport, Int32, ()) == 1 | ||
@test 0 == ccall(:jl_tcp_reuseport, Int32, (Ptr{Void},), s.handle) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe test the positive case as well? (Unless that's already done somewhere else)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes, that is tested elsewhere as that's the default.
base/socket.jl
Outdated
|
||
# Keyword arg "delay": if true, libuv delays creation of socket fd till bind. | ||
# It can be set to false if there is a need to set socket options before | ||
# further calls to `bind` and `listen`, e.g. `SO_REUSEPORT`. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I meant as part of the doc string for the TCPServer
constructor, although a comment is better than nothing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TCPServer is not exported, so I think this should be okay for now.
Can this be merged now? |
Bump, anything preventing this from being merged? |
@ararslan Any chance getting this into 0.6.2 please? |
Yes, will do. |
A new keyword argument is a feature, not a bugfix. |
|
So? Shouldn't encourage writing code that works on 0.6.2 that wouldn't work on 0.6.1 unless there's a legitimate bug involved. |
So this feature is needed to run a Julia web server with real world load. Currently, a Julia web server can effectively only serve one connection at a time (even though the webserver is written with The actual underlying feature ( I think we can safely say that a new keyword argument is pretty low risk, there are bugfixes with much higher risk (as we've seen recently). Hence I would argue for its inclusion. |
Could we just change the incorrect initialization order and consider that a bugfix? |
Or carry a local copy of the modified version of the constructor, which should work correctly even on 0.6.0 or 0.6.1. |
To answer Stefan's question, I consider the keyword argument as the proper fix. The bug was that Julia allowed only one way of constructing server socket (delayed), where no options could be set until it is listened on. While delayed creation is useful to avoid locking resources until use, we also need early creation to be able to set certain options. Without the keyword we will have to make all sockets initialize early, which I think is worse. |
I would however argue for backporting this. It would allow packages that are willing to depend on this internal API in There is no impact on packages that choose to be compatible across |
There shouldn't be such a thing as an API that only exists in 0.6.2, internal or otherwise. Feature freeze for release-0.6 was in February. |
This change adds an option to TCPServer socket constructor to not delay creation of the socket. This will allow TCP servers to set socket options like
SO_REUSEPORT
before the socket is bound and listened on.With early initialization:
But that is not possible with delayed initialization:
My
soreuseport.jl
is: