Пример #1
0
 public void executeBlockingTask(BlockingTask task) throws InterruptedException {
   enterSleep();
   try {
     currentBlockingTask = task;
     pollThreadEvents();
     task.run();
   } finally {
     exitSleep();
     currentBlockingTask = null;
     pollThreadEvents();
   }
 }
Пример #2
0
  /**
   * 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;
  }
Пример #3
0
  @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;
  }
Пример #4
0
  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);
    }
  }
Пример #5
0
 public void pollThreadEvents() {
   thread.pollThreadEvents(this);
 }
Пример #6
0
  @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;
    }
  }
Пример #7
0
 public void pollThreadEvents() {
   pollThreadEvents(getRuntime().getCurrentContext());
 }
Пример #8
0
 public void afterBlockingCall() {
   exitSleep();
   pollThreadEvents();
 }
Пример #9
0
 public void beforeBlockingCall() {
   pollThreadEvents();
   enterSleep();
 }
Пример #10
0
  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;
    }
  }