public void executeBlockingTask(BlockingTask task) throws InterruptedException { enterSleep(); try { currentBlockingTask = task; pollThreadEvents(); task.run(); } finally { exitSleep(); currentBlockingTask = null; pollThreadEvents(); } }
/** * We can never be sure if a wait will finish because of a Java "spurious wakeup". So if we * explicitly wakeup and we wait less than requested amount we will return false. We will return * true if we sleep right amount or less than right amount via spurious wakeup. */ public synchronized boolean sleep(long millis) throws InterruptedException { assert this == getRuntime().getCurrentContext().getThread(); boolean result = true; synchronized (this) { pollThreadEvents(); try { status.set(Status.SLEEP); if (millis == -1) { wait(); } else { wait(millis); } } finally { result = (status.get() != Status.RUN); pollThreadEvents(); status.set(Status.RUN); } } return result; }
@JRubyMethod(name = {"kill", "exit", "terminate"}) public IRubyObject kill() { // need to reexamine this RubyThread currentThread = getRuntime().getCurrentContext().getThread(); // If the killee thread is the same as the killer thread, just die if (currentThread == this) throwThreadKill(); debug(this, "trying to kill"); currentThread.pollThreadEvents(); getRuntime() .getThreadService() .deliverEvent(new ThreadService.Event(currentThread, this, ThreadService.Event.Type.KILL)); debug(this, "succeeded with kill"); return this; }
public boolean waitForIO(ThreadContext context, RubyIO io, int ops) { Channel channel = io.getChannel(); if (!(channel instanceof SelectableChannel)) { return true; } try { io.addBlockingThread(this); blockingIO = BlockingIO.newCondition(channel, ops); boolean ready = blockingIO.await(); // check for thread events, in case we've been woken up to die pollThreadEvents(); return ready; } catch (IOException ioe) { throw context.runtime.newRuntimeError("Error with selector: " + ioe); } catch (InterruptedException ex) { // FIXME: not correct exception throw context.runtime.newRuntimeError("Interrupted"); } finally { blockingIO = null; io.removeBlockingThread(this); } }
public void pollThreadEvents() { thread.pollThreadEvents(this); }
@JRubyMethod(name = "join", optional = 1) public IRubyObject join(IRubyObject[] args) { Ruby runtime = getRuntime(); long timeoutMillis = Long.MAX_VALUE; if (args.length > 0 && !args[0].isNil()) { if (args.length > 1) { throw getRuntime().newArgumentError(args.length, 1); } // MRI behavior: value given in seconds; converted to Float; less // than or equal to zero returns immediately; returns nil timeoutMillis = (long) (1000.0D * args[0].convertToFloat().getValue()); if (timeoutMillis <= 0) { // TODO: not sure that we should skip calling join() altogether. // Thread.join() has some implications for Java Memory Model, etc. if (threadImpl.isAlive()) { return getRuntime().getNil(); } else { return this; } } } if (isCurrent()) { throw getRuntime().newThreadError("thread " + identityString() + " tried to join itself"); } try { if (runtime.getThreadService().getCritical()) { // If the target thread is sleeping or stopped, wake it synchronized (this) { notify(); } // interrupt the target thread in case it's blocking or waiting // WARNING: We no longer interrupt the target thread, since this usually means // interrupting IO and with NIO that means the channel is no longer usable. // We either need a new way to handle waking a target thread that's waiting // on IO, or we need to accept that we can't wake such threads and must wait // for them to complete their operation. // threadImpl.interrupt(); } RubyThread currentThread = getRuntime().getCurrentContext().getThread(); final long timeToWait = Math.min(timeoutMillis, 200); // We need this loop in order to be able to "unblock" the // join call without actually calling interrupt. long start = System.currentTimeMillis(); while (true) { currentThread.pollThreadEvents(); threadImpl.join(timeToWait); if (!threadImpl.isAlive()) { break; } if (System.currentTimeMillis() - start > timeoutMillis) { break; } } } catch (InterruptedException ie) { ie.printStackTrace(); assert false : ie; } catch (ExecutionException ie) { ie.printStackTrace(); assert false : ie; } if (exitingException != null) { // Set $! in the current thread before exiting getRuntime().getGlobalVariables().set("$!", (IRubyObject) exitingException.getException()); throw exitingException; } if (threadImpl.isAlive()) { return getRuntime().getNil(); } else { return this; } }
public void pollThreadEvents() { pollThreadEvents(getRuntime().getCurrentContext()); }
public void afterBlockingCall() { exitSleep(); pollThreadEvents(); }
public void beforeBlockingCall() { pollThreadEvents(); enterSleep(); }
public boolean select(Channel channel, RubyIO io, int ops, long timeout) { if (channel instanceof SelectableChannel) { SelectableChannel selectable = (SelectableChannel) channel; synchronized (selectable.blockingLock()) { boolean oldBlocking = selectable.isBlocking(); SelectionKey key = null; try { selectable.configureBlocking(false); if (io != null) io.addBlockingThread(this); currentSelector = getRuntime().getSelectorPool().get(selectable.provider()); key = selectable.register(currentSelector, ops); beforeBlockingCall(); int result; if (timeout < 0) { result = currentSelector.select(); } else if (timeout == 0) { result = currentSelector.selectNow(); } else { result = currentSelector.select(timeout); } // check for thread events, in case we've been woken up to die pollThreadEvents(); if (result == 1) { Set<SelectionKey> keySet = currentSelector.selectedKeys(); if (keySet.iterator().next() == key) { return true; } } return false; } catch (IOException ioe) { throw getRuntime().newIOErrorFromException(ioe); } finally { // Note: I don't like ignoring these exceptions, but it's // unclear how likely they are to happen or what damage we // might do by ignoring them. Note that the pieces are separate // so that we can ensure one failing does not affect the others // running. // clean up the key in the selector try { if (key != null) key.cancel(); if (currentSelector != null) currentSelector.selectNow(); } catch (Exception e) { // ignore } // shut down and null out the selector try { if (currentSelector != null) { getRuntime().getSelectorPool().put(currentSelector); } } catch (Exception e) { // ignore } finally { currentSelector = null; } // remove this thread as a blocker against the given IO if (io != null) io.removeBlockingThread(this); // go back to previous blocking state on the selectable try { selectable.configureBlocking(oldBlocking); } catch (Exception e) { // ignore } // clear thread state from blocking call afterBlockingCall(); } } } else { // can't select, just have to do a blocking call return true; } }