public void exceptionRaised(RaiseException exception) { assert isCurrent(); RubyException rubyException = exception.getException(); Ruby runtime = rubyException.getRuntime(); if (runtime.getSystemExit().isInstance(rubyException)) { runtime .getThreadService() .getMainThread() .raise(new IRubyObject[] {rubyException}, Block.NULL_BLOCK); } else if (abortOnException(runtime)) { RubyException systemExit; if (!runtime.is1_9()) { runtime.printError(rubyException); systemExit = RubySystemExit.newInstance(runtime, 1); systemExit.message = rubyException.message; systemExit.set_backtrace(rubyException.backtrace()); } else { systemExit = rubyException; } runtime .getThreadService() .getMainThread() .raise(new IRubyObject[] {systemExit}, Block.NULL_BLOCK); return; } else if (runtime.getDebug().isTrue()) { runtime.printError(exception.getException()); } exitingException = exception; }
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(rest = true, visibility = PRIVATE) public IRubyObject initialize(ThreadContext context, IRubyObject[] args, Block block) { Ruby runtime = getRuntime(); if (!block.isGiven()) throw runtime.newThreadError("must be called with a block"); try { RubyRunnable runnable = new RubyRunnable(this, args, context.getFrames(0), block); if (RubyInstanceConfig.POOLING_ENABLED) { FutureThread futureThread = new FutureThread(this, runnable); threadImpl = futureThread; addToCorrectThreadGroup(context); threadImpl.start(); // JRUBY-2380, associate future early so it shows up in Thread.list right away, in case it // doesn't run immediately runtime.getThreadService().associateThread(futureThread.getFuture(), this); } else { Thread thread = new Thread(runnable); thread.setDaemon(true); thread.setName( "Ruby" + thread.getName() + ": " + context.getFile() + ":" + (context.getLine() + 1)); threadImpl = new NativeThread(this, thread); addToCorrectThreadGroup(context); // JRUBY-2380, associate thread early so it shows up in Thread.list right away, in case it // doesn't run immediately runtime.getThreadService().associateThread(thread, this); threadImpl.start(); } // We yield here to hopefully permit the target thread to schedule // MRI immediately schedules it, so this is close but not exact Thread.yield(); return this; } catch (OutOfMemoryError oome) { if (oome.getMessage().equals("unable to create new native thread")) { throw runtime.newThreadError(oome.getMessage()); } throw oome; } catch (SecurityException ex) { throw runtime.newThreadError(ex.getMessage()); } }
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; }
private IRubyObject startWith(ThreadedRunnable runnable) throws RaiseException, OutOfMemoryError { Ruby runtime = getRuntime(); ThreadContext context = runtime.getCurrentContext(); try { if (RubyInstanceConfig.POOLING_ENABLED) { FutureThread futureThread = new FutureThread(this, runnable); threadImpl = futureThread; addToCorrectThreadGroup(context); threadImpl.start(); // JRUBY-2380, associate future early so it shows up in Thread.list right away, in case it // doesn't run immediately runtime.getThreadService().associateThread(futureThread.getFuture(), this); } else { Thread thread = new Thread(runnable); thread.setDaemon(true); thread.setName( "Ruby" + thread.getName() + ": " + context.getFile() + ":" + (context.getLine() + 1)); threadImpl = new NativeThread(this, thread); addToCorrectThreadGroup(context); // JRUBY-2380, associate thread early so it shows up in Thread.list right away, in case it // doesn't run immediately runtime.getThreadService().associateThread(thread, this); threadImpl.start(); } // We yield here to hopefully permit the target thread to schedule // MRI immediately schedules it, so this is close but not exact Thread.yield(); return this; } catch (OutOfMemoryError oome) { if (oome.getMessage().equals("unable to create new native thread")) { throw runtime.newThreadError(oome.getMessage()); } throw oome; } catch (SecurityException ex) { throw runtime.newThreadError(ex.getMessage()); } }
@JRubyMethod(name = "pass", meta = true) public static IRubyObject pass(IRubyObject recv) { Ruby runtime = recv.getRuntime(); ThreadService ts = runtime.getThreadService(); boolean critical = ts.getCritical(); ts.setCritical(false); Thread.yield(); ts.setCritical(critical); return recv.getRuntime().getNil(); }
/** * For handling all non-Ruby exceptions bubbling out of threads * * @param exception */ @SuppressWarnings("deprecation") public void exceptionRaised(Throwable exception) { if (exception instanceof RaiseException) { exceptionRaised((RaiseException) exception); return; } assert isCurrent(); Ruby runtime = getRuntime(); if (abortOnException(runtime) && exception instanceof Error) { // re-propagate on main thread runtime.getThreadService().getMainThread().getNativeThread().stop(exception); } else { // just rethrow on this thread, let system handlers report it UnsafeFactory.getUnsafe().throwException(exception); } }
@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 = "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; } }