-
-
Notifications
You must be signed in to change notification settings - Fork 925
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
ServerSocket still relevant #4748
Comments
The implementation of Socket in JRuby is a bit troublesome because the JDK separates socket types by class. So if you want to create a server, you need to construct a server socket object. In Ruby, however, you can create a socket object and later decide whether it's a server or a client. JRuby sacrifices that ability of Ruby by only supporting client connections in the bare Socket class and mentioning the ServerSocket class as an alternative. Unfortunately since this class doesn't exist in regular Ruby, nobody knows about it and nobody uses it. The async-io library could be modified to use a specific server socket type (like TCPServer), perhaps? I did have an experiment on a branch where I delayed the creation of the Java socket, but it's really pretty ugly; I have to simulate a bunch of intermediate steps like "bind" and all the errors that go with them, since I can't actually initiate a socket until later. There is of course the possibility that we could improve our Socket impl to use native functions, but native socket functions are notoriously troublesome to bind through FFI, since they have various struct shapes across platforms and libraries. |
I played around with this a bit more. It's troublesome.
This isn't really a good option. I'd really have to implement the ServerSocket / ClientSocket split into the core of the io abstraction in order to make it work. It's probably the only solution that's going to work well, but the problem is core Ruby doesn't make this split, while JRuby does. It's a bit of a mess to be honest. The only solution I can really see working, is to actually re-implement the socket API from the ground up.
I tried this too. It was hard work, but it did work for the most part.
Honestly, I think this is the best route if you want to make things just work without additional work for the user. Or, the above, where you basically cache all the method calls until the actual socket is allocated. The problem is, it's not clear whether you have a server socket or client socket. That's because a user might call bind (server) or bind & connect (bind to local address for connection). So, it's hard to detect the exact sequence. The real issue is Java has made a decision about how sockets should work without actually providing the real basic API level functions. Are there any external libraries you could depend on to provide a proper socket implementation? |
Here is the hack which for the most part worked, but failed with UDP sockets. |
I think that the cross-interpreter costs w.r.t. how the APIs work is stupidly complex for typical developers to manage which is unfortunate. I could modify |
The long term answer is that we need to start moving toward all-native Socket. There's a solid start on this in the rubysl-socket library, but it has a number of dependencies on Rubinius or on having an initial build step per-platform, which we can't provide. The TruffleRuby folks may have a modified version that we could drop in. I have attempted at various times to make Socket defer the decision whether to use a Java TCPSocket or TCPServer, but it's very hacky and is incompatible with code that attempts to make use of the file descriptor between the time the socket is created and bound. @ioquatix You might have a look at https://github.com/rubysl/rubysl-socket or at the modified version in TruffleRuby at https://github.com/oracle/truffleruby/tree/master/lib/truffle/socket and see how much work would be needed to get just the basic Socket class working natively. |
FWIW, the version in TruffleRuby is more complete and has less bugs than rubysl-socket (BTW that link seems dead). |
@ioquatix We have never made a move to using native calls for all socket support because none of the available options work without a build step. JRuby runs out of the box on any system with a JDK because we rely on so many built-in JDK classes (e.g. the socket subsystem) and requiring a build step for native sockets is not in our plan right now. The plan long term would be to support fully-native socket support, pregenerated for as many platforms as possible, with a fallback to our non-native sockets. Unfortunately we don't have many resources to work on that these days. |
rubysl-socket doesn't require any build step, isn't it? It's just FFI. |
@eregon Last time I looked it required a bunch of properties to be set for the shape and sizing of the struct, which are generated at build time on Rubinius (and presumably on TruffleRuby). In any case the structs differ across platforms, so we'd need to have predefined values for any platforms we want to support. I did not see that in rubysl-socket nor in TruffleRuby socket last time I looked. |
@eregon Example: https://github.com/oracle/truffleruby/blob/master/lib/truffle/socket/socket.rb#L142-L150 We do not have these values set and they would be per-platform, so I assumed TruffleRuby generates them when built for each platform. JRuby has no per-platform build step (outside of jffi and) so we can't provide these values. |
I think those values could be stored in jffi then. |
@eregon Ah there's a bit of magic I did not know about. It would be reasonable for us to generate these configs, and when a platform's configs are not present we fall back on the JDK-based sockets. Just need to do something about all those pesky Truffle/Rubinius primitives. |
There are no primitives used in lib/truffle/socket, only FFI. |
I was referring to things that I believe were primitives in rbx. Perhaps they're not "primitives" in the same sense in TruffleRuby, but they don't exist as standard APIs nor as bound FFI endpoints: https://github.com/oracle/truffleruby/blob/master/lib/truffle/socket/basic_socket.rb#L76 Granted, most of this code was written just atop FFI originally, and it seems like that's still the case in your improved version. Hopefully we'll have some cycles free to attempt using it in JRuby soon. |
Of these 3 only the first one is not defined directly there, but it's still pure Ruby: The other two are just defined in |
Longer-term I think it could be cool to make |
For clarification, TruffleRuby has primitives, they look like |
I ran into this issue
https://travis-ci.org/socketry/async-http/jobs/267440658#L617-L618
looked at https://github.com/jruby/jruby/wiki/ServerSocket
I thought JRuby uses native UNIX functions where possible?
Just wondering what is considered best practice here.
The text was updated successfully, but these errors were encountered: