private ByteList doReceive(ThreadContext context, int length) { Ruby runtime = context.runtime; ByteBuffer buf = ByteBuffer.allocate(length); try { context.getThread().beforeBlockingCall(); int read = openFile.getMainStreamSafe().getDescriptor().read(buf); if (read == 0) return null; return new ByteList(buf.array(), 0, buf.position()); } catch (BadDescriptorException e) { throw runtime.newIOError("bad descriptor"); } catch (IOException e) { // All errors to sysread should be SystemCallErrors, but on a closed stream // Ruby returns an IOError. Java throws same exception for all errors so // we resort to this hack... if ("Socket not open".equals(e.getMessage())) { throw runtime.newIOError(e.getMessage()); } throw runtime.newSystemCallError(e.getMessage()); } finally { context.getThread().afterBlockingCall(); } }
@JRubyMethod public IRubyObject lock(ThreadContext context) { try { context.getThread().enterSleep(); try { checkRelocking(context); context.getThread().lockInterruptibly(lock); } catch (InterruptedException ex) { context.pollThreadEvents(); throw context.getRuntime().newConcurrencyError(ex.getLocalizedMessage()); } } finally { context.getThread().exitSleep(); } return this; }
@JRubyMethod public RubyBoolean try_lock(ThreadContext context) { if (lock.isHeldByCurrentThread()) { return context.runtime.getFalse(); } return context.runtime.newBoolean(context.getThread().tryLock(lock)); }
@JRubyMethod(module = true) public static IRubyObject timeout( final ThreadContext context, IRubyObject timeout, IRubyObject seconds, IRubyObject exceptionType, Block block) { // No seconds, just yield if (seconds.isNil() || Helpers.invoke(context, seconds, "zero?").isTrue()) { return block.yieldSpecific(context); } final Ruby runtime = context.runtime; // No timeout in critical section if (runtime.getThreadService().getCritical()) { return raiseBecauseCritical(context); } final RubyThread currentThread = context.getThread(); final AtomicBoolean latch = new AtomicBoolean(false); IRubyObject id = new RubyObject(runtime, runtime.getObject()); RubyClass anonException = (RubyClass) runtime.getClassFromPath("Timeout::AnonymousException"); Runnable timeoutRunnable = exceptionType.isNil() ? prepareRunnable(currentThread, runtime, latch, id) : prepareRunnableWithException(currentThread, exceptionType, runtime, latch); Future timeoutFuture = null; try { try { timeoutFuture = timeoutExecutor.schedule( timeoutRunnable, (long) (seconds.convertToFloat().getDoubleValue() * 1000000), TimeUnit.MICROSECONDS); return block.yield(context, seconds); } finally { killTimeoutThread(context, timeoutFuture, latch); } } catch (RaiseException re) { // if it's the exception we're expecting if (re.getException().getMetaClass() == anonException) { // and we were not given a specific exception if (exceptionType.isNil()) { // and it's the exception intended for us if (re.getException().getInternalVariable("__identifier__") == id) { return raiseTimeoutError(context, re); } } } // otherwise, rethrow throw re; } }
private void addToCorrectThreadGroup(ThreadContext context) { // JRUBY-3568, inherit threadgroup or use default IRubyObject group = context.getThread().group(); if (!group.isNil()) { ((RubyThreadGroup) group).addDirectly(this); } else { context.runtime.getDefaultThreadGroup().addDirectly(this); } }
protected IRubyObject resumeOrTransfer(ThreadContext context, IRubyObject arg, boolean transfer) { CoroutineFiber current = (CoroutineFiber) context.getFiber(); slot = arg; try { switch (state) { case SUSPENDED_YIELD: if (transfer) { current.state = CoroutineFiberState.SUSPENDED_TRANSFER; } else { current.state = CoroutineFiberState.SUSPENDED_RESUME; lastFiber = (CoroutineFiber) context.getFiber(); } state = CoroutineFiberState.RUNNING; context.getRuntime().getThreadService().setCurrentContext(context); context.setThread(context.getThread()); Coroutine.yieldTo(coro); break; case SUSPENDED_TRANSFER: if (!transfer) { throw context.getRuntime().newFiberError("double resume"); } current.state = CoroutineFiberState.SUSPENDED_TRANSFER; state = CoroutineFiberState.RUNNING; context.getRuntime().getThreadService().setCurrentContext(context); context.setThread(context.getThread()); Coroutine.yieldTo(coro); break; case FINISHED: throw context.getRuntime().newFiberError("dead fiber called"); default: throw context.getRuntime().newFiberError("fiber in an invalid state: " + state); } } catch (OutOfMemoryError oome) { if (oome.getMessage().equals("unable to create new native thread")) { throw context.runtime.newThreadError("too many threads, can't create a new Fiber"); } throw oome; } // back from fiber, poll events and proceed out of resume context.pollThreadEvents(); return slot; }
@JRubyMethod(compat = CompatVersion.RUBY1_9) public IRubyObject sleep(ThreadContext context, IRubyObject timeout) { long beg = System.currentTimeMillis(); try { unlock(context); context.getThread().sleep((long) (timeout.convertToFloat().getDoubleValue() * 1000)); } catch (InterruptedException ex) { // ignore interrupted } finally { lock(context); } return context.runtime.newFixnum((System.currentTimeMillis() - beg) / 1000); }
@JRubyMethod public synchronized IRubyObject unlock(ThreadContext context) { Ruby runtime = context.getRuntime(); if (!lock.isLocked()) { throw runtime.newThreadError("Mutex is not locked"); } if (!lock.isHeldByCurrentThread()) { throw runtime.newThreadError("Mutex is not owned by calling thread"); } boolean hasQueued = lock.hasQueuedThreads(); context.getThread().unlock(lock); return hasQueued ? context.nil : this; }
protected void initFiber(ThreadContext context) { final Ruby runtime = context.runtime; this.slot = runtime.getNil(); this.context = root ? context : ThreadContext.newContext(runtime); this.context.setFiber(this); this.context.setThread(context.getThread()); this.state = CoroutineFiberState.SUSPENDED_YIELD; if (coro == null) { coro = new Coroutine() { @Override protected void run() { try { // first resume, dive into the block slot = block.yieldArray(CoroutineFiber.this.context, slot, null, null); } catch (JumpException.RetryJump rtry) { // FIXME: technically this should happen before the block is executed parent.raise( new IRubyObject[] {runtime.newSyntaxError("Invalid retry").getException()}, Block.NULL_BLOCK); } catch (JumpException.BreakJump brk) { parent.raise( new IRubyObject[] { runtime .newLocalJumpError( Reason.BREAK, runtime.getNil(), "break from proc-closure") .getException() }, Block.NULL_BLOCK); } catch (JumpException.ReturnJump ret) { parent.raise( new IRubyObject[] { runtime .newLocalJumpError(Reason.RETURN, runtime.getNil(), "unexpected return") .getException() }, Block.NULL_BLOCK); } catch (RaiseException re) { // re-raise exception in parent thread parent.raise(new IRubyObject[] {re.getException()}, Block.NULL_BLOCK); } finally { state = CoroutineFiberState.FINISHED; } } }; } }
@JRubyMethod(rest = true) public IRubyObject recv(ThreadContext context, IRubyObject[] args) { OpenFile openFile = getOpenFileChecked(); try { context.getThread().beforeBlockingCall(); return RubyString.newString( context.getRuntime(), openFile.getMainStream().read(RubyNumeric.fix2int(args[0]))); } catch (BadDescriptorException e) { throw context.getRuntime().newErrnoEBADFError(); } catch (EOFException e) { // recv returns nil on EOF return context.getRuntime().getNil(); } catch (IOException e) { // All errors to sysread should be SystemCallErrors, but on a closed stream // Ruby returns an IOError. Java throws same exception for all errors so // we resort to this hack... if ("Socket not open".equals(e.getMessage())) { throw context.getRuntime().newIOError(e.getMessage()); } throw context.getRuntime().newSystemCallError(e.getMessage()); } finally { context.getThread().afterBlockingCall(); } }
/* Run the selector */ private int doSelect(Ruby runtime, ThreadContext context, IRubyObject timeout) { int result; cancelKeys(); try { context.getThread().beforeBlockingCall(); if (timeout.isNil()) { result = this.selector.select(); } else { double t = RubyNumeric.num2dbl(timeout); if (t == 0) { result = this.selector.selectNow(); } else if (t < 0) { throw runtime.newArgumentError("time interval must be positive"); } else { result = this.selector.select((long) (t * 1000)); } } context.getThread().afterBlockingCall(); return result; } catch (IOException ie) { throw runtime.newIOError(ie.getLocalizedMessage()); } }
@JRubyMethod(name = "accept_nonblock") public IRubyObject accept_nonblock(ThreadContext context) { Ruby runtime = context.runtime; RubyTCPSocket socket = new RubyTCPSocket(runtime, runtime.getClass("TCPSocket")); Selector selector = null; synchronized (ssc.blockingLock()) { boolean oldBlocking = ssc.isBlocking(); try { ssc.configureBlocking(false); selector = SelectorFactory.openWithRetryFrom(runtime, SelectorProvider.provider()); boolean ready = context.getThread().select(this, SelectionKey.OP_ACCEPT, 0); if (!ready) { // no connection immediately accepted, let them try again throw runtime.newErrnoEAGAINError("Resource temporarily unavailable"); } else { // otherwise one key has been selected (ours) so we get the channel and hand it off socket.initSocket( context.runtime, new ChannelDescriptor(ssc.accept(), newModeFlags(runtime, ModeFlags.RDWR))); return socket; } } catch (IOException e) { throw SocketUtils.sockerr(context.runtime, "problem when accepting"); } finally { try { if (selector != null) selector.close(); } catch (Exception e) { } try { ssc.configureBlocking(oldBlocking); } catch (IOException ioe) { } } } }
@JRubyMethod(module = true) public static IRubyObject timeout( final ThreadContext context, IRubyObject timeout, IRubyObject seconds, Block block) { // No seconds, just yield if (seconds.isNil() || Helpers.invoke(context, seconds, "zero?").isTrue()) { return block.yieldSpecific(context); } final Ruby runtime = context.runtime; // No timeout in critical section if (runtime.getThreadService().getCritical()) { return raiseBecauseCritical(context); } final RubyThread currentThread = context.getThread(); final AtomicBoolean latch = new AtomicBoolean(false); IRubyObject id = new RubyObject(runtime, runtime.getObject()); Runnable timeoutRunnable = prepareRunnable(currentThread, runtime, latch, id); Future timeoutFuture = null; try { try { timeoutFuture = timeoutExecutor.schedule( timeoutRunnable, (long) (seconds.convertToFloat().getDoubleValue() * 1000000), TimeUnit.MICROSECONDS); return block.yield(context, seconds); } finally { killTimeoutThread(context, timeoutFuture, latch); } } catch (RaiseException re) { if (re.getException().getInternalVariable("__identifier__") == id) { return raiseTimeoutError(context, re); } else { throw re; } } }
private synchronized IRubyObject pop(ThreadContext context, boolean should_block) { checkShutdown(context); if (!should_block && entries.size() == 0) { throw new RaiseException( context.runtime, context.runtime.getThreadError(), "queue empty", false); } numWaiting++; try { while (java_length() == 0) { try { context.getThread().wait_timeout(this, null); } catch (InterruptedException e) { } checkShutdown(context); } } finally { numWaiting--; } return (IRubyObject) entries.removeFirst(); }
@JRubyMethod(name = "stop", meta = true) public static IRubyObject stop(ThreadContext context, IRubyObject receiver) { RubyThread rubyThread = context.getThread(); synchronized (rubyThread) { rubyThread.checkMail(context); try { // attempt to decriticalize all if we're the critical thread receiver.getRuntime().getThreadService().setCritical(false); rubyThread.status.set(Status.SLEEP); rubyThread.wait(); } catch (InterruptedException ie) { rubyThread.checkMail(context); rubyThread.status.set(Status.RUN); } } return receiver.getRuntime().getNil(); }
public IRubyObject yield(ThreadContext context, IRubyObject arg) { assert !root; if (lastFiber.state != CoroutineFiberState.SUSPENDED_RESUME) { if (lastFiber.state == CoroutineFiberState.SUSPENDED_TRANSFER) { throw context.getRuntime().newFiberError("a Fiber that was transferred to cannot yield"); } throw context .getRuntime() .newFiberError("invalid state of last Fiber at yield: " + lastFiber.state); } slot = arg; state = CoroutineFiberState.SUSPENDED_YIELD; lastFiber.state = CoroutineFiberState.RUNNING; context.getRuntime().getThreadService().setCurrentContext(lastFiber.context); Object o = lastFiber.context; lastFiber.context.setThread(context.getThread()); Coroutine.yieldTo(lastFiber.coro); // back from fiber, poll events and proceed out of resume context.pollThreadEvents(); return slot; }
@JRubyMethod(optional = 3) public IRubyObject raise(IRubyObject[] args, Block block) { Ruby runtime = getRuntime(); ThreadContext context = runtime.getCurrentContext(); if (this == context.getThread()) { return RubyKernel.raise(context, runtime.getKernel(), args, block); } debug(this, "before raising"); RubyThread currentThread = getRuntime().getCurrentContext().getThread(); debug(this, "raising"); IRubyObject exception = prepareRaiseException(runtime, args, block); runtime .getThreadService() .deliverEvent( new ThreadService.Event( currentThread, this, ThreadService.Event.Type.RAISE, exception)); return this; }
@JRubyMethod(name = "accept") public IRubyObject accept(ThreadContext context) { Ruby runtime = context.runtime; RubyTCPSocket socket = new RubyTCPSocket(runtime, runtime.getClass("TCPSocket")); try { RubyThread thread = context.getThread(); while (true) { boolean ready = thread.select(this, SelectionKey.OP_ACCEPT); if (!ready) { // we were woken up without being selected...poll for thread events and go back to sleep context.pollThreadEvents(); } else { SocketChannel connected = ssc.accept(); if (connected == null) continue; connected.finishConnect(); // Force the client socket to be blocking synchronized (connected.blockingLock()) { connected.configureBlocking(false); connected.configureBlocking(true); } // otherwise one key has been selected (ours) so we get the channel and hand it off socket.initSocket( runtime, new ChannelDescriptor(connected, newModeFlags(runtime, ModeFlags.RDWR))); return socket; } } } catch (IOException e) { throw SocketUtils.sockerr(runtime, "problem when accepting"); } }
@JRubyMethod(name = "accept") public IRubyObject accept(ThreadContext context) { RubyTCPSocket socket = new RubyTCPSocket(context.getRuntime(), context.getRuntime().fastGetClass("TCPSocket")); try { while (true) { boolean ready = context.getThread().select(this, SelectionKey.OP_ACCEPT); if (!ready) { // we were woken up without being selected...poll for thread events and go back to sleep context.pollThreadEvents(); } else { try { SocketChannel connected = ssc.accept(); connected.finishConnect(); // // Force the client socket to be blocking // synchronized (connected.blockingLock()) { connected.configureBlocking(false); connected.configureBlocking(true); } // otherwise one key has been selected (ours) so we get the channel and hand it off socket.initSocket( context.getRuntime(), new ChannelDescriptor(connected, new ModeFlags(ModeFlags.RDWR))); } catch (InvalidValueException ex) { throw context.getRuntime().newErrnoEINVALError(); } return socket; } } } catch (IOException e) { throw sockerr(context.getRuntime(), "problem when accepting"); } }