Example #1
0
  public static RubyClass createThreadClass(Ruby runtime) {
    // FIXME: In order for Thread to play well with the standard 'new' behavior,
    // it must provide an allocator that can create empty object instances which
    // initialize then fills with appropriate data.
    RubyClass threadClass =
        runtime.defineClass(
            "Thread", runtime.getObject(), ObjectAllocator.NOT_ALLOCATABLE_ALLOCATOR);
    runtime.setThread(threadClass);

    threadClass.index = ClassIndex.THREAD;
    threadClass.setReifiedClass(RubyThread.class);

    threadClass.defineAnnotatedMethods(RubyThread.class);

    RubyThread rubyThread = new RubyThread(runtime, threadClass);
    // TODO: need to isolate the "current" thread from class creation
    rubyThread.threadImpl = new NativeThread(rubyThread, Thread.currentThread());
    runtime.getThreadService().setMainThread(Thread.currentThread(), rubyThread);

    // set to default thread group
    runtime.getDefaultThreadGroup().addDirectly(rubyThread);

    threadClass.setMarshal(ObjectMarshal.NOT_MARSHALABLE_MARSHAL);

    return threadClass;
  }
Example #2
0
  @JRubyMethod(meta = true)
  public static IRubyObject exit(IRubyObject receiver, Block block) {
    RubyThread rubyThread =
        receiver.getRuntime().getThreadService().getCurrentContext().getThread();

    synchronized (rubyThread) {
      rubyThread.status.set(Status.ABORTING);
      rubyThread.mail = null;
      receiver.getRuntime().getThreadService().setCritical(false);
      throw new ThreadKill();
    }
  }
Example #3
0
  private static RubyThread startThread(
      final IRubyObject recv, final IRubyObject[] args, boolean callInit, Block block) {
    RubyThread rubyThread = new RubyThread(recv.getRuntime(), (RubyClass) recv);

    if (callInit) {
      rubyThread.callInit(args, block);
    } else {
      // for Thread::start, which does not call the subclass's initialize
      rubyThread.initialize(recv.getRuntime().getCurrentContext(), args, block);
    }

    return rubyThread;
  }
Example #4
0
  private static RubyThread adoptThread(final IRubyObject recv, Thread t, Block block) {
    final Ruby runtime = recv.getRuntime();
    final RubyThread rubyThread = new RubyThread(runtime, (RubyClass) recv);

    rubyThread.threadImpl = new NativeThread(rubyThread, t);
    ThreadContext context = runtime.getThreadService().registerNewThread(rubyThread);
    runtime.getThreadService().associateThread(t, rubyThread);

    context.preAdoptThread();

    // set to default thread group
    runtime.getDefaultThreadGroup().addDirectly(rubyThread);

    return rubyThread;
  }
Example #5
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;
  }
Example #6
0
  @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();
  }
Example #7
0
  @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");
    }
  }
Example #8
0
 public void pollThreadEvents() {
   thread.pollThreadEvents(this);
 }
Example #9
0
 public IRubyObject setErrorInfo(IRubyObject errorInfo) {
   thread.setErrorInfo(errorInfo);
   return errorInfo;
 }
Example #10
0
 public IRubyObject getErrorInfo() {
   return thread.getErrorInfo();
 }
Example #11
0
 @Override
 protected void finalize() throws Throwable {
   thread.dispose();
 }
Example #12
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;
    }
  }