Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: jruby/jruby
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: f538f18ec88f
Choose a base ref
...
head repository: jruby/jruby
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: 16781cf7c907
Choose a head ref
  • 2 commits
  • 1 file changed
  • 1 contributor

Commits on Jan 1, 2016

  1. Implement TCPServer#listen.

    The JDK socket classes do both bind and listen at once and do not
    expose a way to change the backlog after that. To work around
    this, I reflectively attempt to access the class in which they
    bind the native listen(2) function. If I'm able to use it, we will
    set the backlog. If I'm not, TCPServer#listen remains mostly a
    no-op.
    
    Note that a strategy like RubySocket, where we defer the creation
    of the actual socket, might work here...but that has not yet been
    proven out, and subsequent calls to listen would not work.
    headius committed Jan 1, 2016
    Copy the full SHA
    dfee5d3 View commit details
  2. Copy the full SHA
    16781cf View commit details
Showing with 37 additions and 2 deletions.
  1. +37 −2 core/src/main/java/org/jruby/ext/socket/RubyTCPServer.java
39 changes: 37 additions & 2 deletions core/src/main/java/org/jruby/ext/socket/RubyTCPServer.java
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@
package org.jruby.ext.socket;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.BindException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
@@ -214,7 +216,7 @@ public IRubyObject accept_nonblock(ThreadContext context, Ruby runtime, boolean

if (!ready) {
// no connection immediately accepted, let them try again
throw runtime.newErrnoEAGAINError("Resource temporarily unavailable");
throw runtime.newErrnoEAGAINReadableError("Resource temporarily unavailable");

} else {
// otherwise one key has been selected (ours) so we get the channel and hand it off
@@ -266,8 +268,41 @@ public IRubyObject sysaccept(ThreadContext context) {
}
}

private static volatile Method listenFunction;
static {
Method listen = null;
try {
listen = Class.forName("sun.nio.ch.Net").getDeclaredMethod("listen", int.class);
listen.setAccessible(true);
} catch (Exception e) {
// can't use listen, backlog will be a no-op
}
listenFunction = listen;
}

@JRubyMethod(name = "listen", required = 1)
public IRubyObject listen(ThreadContext context, IRubyObject backlog) {
public IRubyObject listen(ThreadContext context, IRubyObject _backlog) {
int backlog = _backlog.convertToInteger().getIntValue();

if (listenFunction != null) {
int ret = 0;
try {
listenFunction.invoke(FilenoUtil.getDescriptorFromChannel(getServerSocketChannel()), backlog);
} catch (InvocationTargetException ite) {
// We should only get IOException from this, or else something else is wrong
if (ite.getTargetException() instanceof IOException) {
IOException ioe = (IOException) ite.getTargetException();
throw context.runtime.newIOErrorFromException(ioe);
}

// give up attempting to use this function
listenFunction = null;
} catch (IllegalAccessException iae) {
// give up attempting to use this function
listenFunction = null;
}
}

return RubyFixnum.zero(context.runtime);
}