2828
2929package org .jruby .ext .socket ;
3030
31+ import org .jruby .FiberScheduler ;
3132import org .jruby .Ruby ;
3233import org .jruby .RubyBasicObject ;
3334import org .jruby .RubyClass ;
@@ -145,6 +146,9 @@ public IRubyObject accept(ThreadContext context) {
145146 RubyTCPSocket socket = new RubyTCPSocket (context .runtime , Access .getClass (context , "TCPSocket" ));
146147
147148 try {
149+ IRubyObject scheduler = FiberScheduler .current (context );
150+ if (scheduler != null ) return initAcceptedSocket (context , socket , schedulerAccept (context , scheduler ));
151+
148152 RubyThread thread = context .getThread ();
149153
150154 while (true ) {
@@ -160,16 +164,8 @@ public IRubyObject accept(ThreadContext context) {
160164
161165 connected .finishConnect ();
162166
163- // Force the client socket to be blocking
164- synchronized (connected .blockingLock ()) {
165- connected .configureBlocking (false );
166- connected .configureBlocking (true );
167- }
168-
169167 // otherwise one key has been selected (ours) so we get the channel and hand it off
170- socket .initSocket (newChannelFD (context .runtime , connected ));
171-
172- return socket ;
168+ return initAcceptedSocket (context , socket , connected );
173169 }
174170 }
175171
@@ -178,6 +174,46 @@ public IRubyObject accept(ThreadContext context) {
178174 }
179175 }
180176
177+ private static RubyTCPSocket initAcceptedSocket (ThreadContext context , RubyTCPSocket socket , SocketChannel connected ) throws IOException {
178+ // Force the client socket to be blocking
179+ synchronized (connected .blockingLock ()) {
180+ connected .configureBlocking (false );
181+ connected .configureBlocking (true );
182+ }
183+
184+ socket .initSocket (newChannelFD (context .runtime , connected ));
185+
186+ return socket ;
187+ }
188+
189+ // MRI: rsock_s_accept, which retries a nonblocking accept(2) whenever io_wait reports the server readable
190+ private SocketChannel schedulerAccept (ThreadContext context , IRubyObject scheduler ) throws IOException {
191+ while (true ) {
192+ ServerSocketChannel ssc = getServerSocketChannel ();
193+ SocketChannel connected ;
194+
195+ synchronized (ssc .blockingLock ()) {
196+ boolean oldBlocking = ssc .isBlocking ();
197+
198+ try {
199+ ssc .configureBlocking (false );
200+ connected = ssc .accept ();
201+ } finally {
202+ try {ssc .configureBlocking (oldBlocking );} catch (IOException ioe ) {}
203+ }
204+ }
205+
206+ if (connected != null ) {
207+ connected .finishConnect ();
208+
209+ return connected ;
210+ }
211+
212+ // outside the blocking lock, which a sibling fiber may need to accept on this server
213+ schedulerWaitAcceptable (context , scheduler );
214+ }
215+ }
216+
181217 @ JRubyMethod (name = "accept_nonblock" )
182218 public IRubyObject accept_nonblock (ThreadContext context ) {
183219 return accept_nonblock (context , context .runtime , true );
@@ -235,6 +271,9 @@ public IRubyObject accept_nonblock(ThreadContext context, Ruby runtime, boolean
235271 @ JRubyMethod (name = "sysaccept" )
236272 public IRubyObject sysaccept (ThreadContext context ) {
237273 try {
274+ IRubyObject scheduler = FiberScheduler .current (context );
275+ if (scheduler != null ) return asFixnum (context , newChannelFD (context .runtime , schedulerAccept (context , scheduler )).bestFileno ());
276+
238277 RubyThread thread = context .getThread ();
239278
240279 while (true ) {
0 commit comments