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; }
@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(); } }
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; }
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; }
@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; }
@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(); }
@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"); } }
public void pollThreadEvents() { thread.pollThreadEvents(this); }
public IRubyObject setErrorInfo(IRubyObject errorInfo) { thread.setErrorInfo(errorInfo); return errorInfo; }
public IRubyObject getErrorInfo() { return thread.getErrorInfo(); }
@Override protected void finalize() throws Throwable { thread.dispose(); }
@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; } }