RubyThread.java

  1. /*
  2.  * Copyright (c) 2013, 2014 Oracle and/or its affiliates. All rights reserved. This
  3.  * code is released under a tri EPL/GPL/LGPL license. You can use it,
  4.  * redistribute it and/or modify it under the terms of the:
  5.  *
  6.  * Eclipse Public License version 1.0
  7.  * GNU General Public License version 2
  8.  * GNU Lesser General Public License version 2.1
  9.  */
  10. package org.jruby.truffle.runtime.core;

  11. import org.jruby.RubyThread.Status;
  12. import org.jruby.truffle.nodes.RubyNode;
  13. import org.jruby.truffle.nodes.objects.Allocator;
  14. import org.jruby.truffle.runtime.RubyContext;
  15. import org.jruby.truffle.runtime.control.RaiseException;
  16. import org.jruby.truffle.runtime.control.ReturnException;
  17. import org.jruby.truffle.runtime.control.ThreadExitException;
  18. import org.jruby.truffle.runtime.subsystems.ThreadManager;

  19. import java.util.concurrent.CountDownLatch;

  20. /**
  21.  * Represents the Ruby {@code Thread} class. Implemented using Java threads, but note that there is
  22.  * not a one-to-one mapping between Ruby threads and Java threads - specifically in combination with
  23.  * fibers as they are currently implemented as their own Java threads.
  24.  */
  25. public class RubyThread extends RubyBasicObject {

  26.     public Object getValue() {
  27.         return value;
  28.     }

  29.     public RubyException getException() {
  30.         return exception;
  31.     }

  32.     public void shutdown() {
  33.     }

  34.     private final ThreadManager manager;

  35.     private final CountDownLatch finished = new CountDownLatch(1);

  36.     private Status status = Status.RUN;

  37.     private RubyException exception;
  38.     private Object value;

  39.     private RubyBasicObject threadLocals;

  40.     public RubyThread(RubyClass rubyClass, ThreadManager manager) {
  41.         super(rubyClass);
  42.         this.manager = manager;
  43.         threadLocals = new RubyBasicObject(rubyClass.getContext().getCoreLibrary().getObjectClass());
  44.     }

  45.     public void initialize(RubyContext context, RubyNode currentNode, RubyProc block) {
  46.         final RubyProc finalBlock = block;

  47.         initialize(context, currentNode, new Runnable() {

  48.             @Override
  49.             public void run() {
  50.                 value = finalBlock.rootCall();
  51.             }

  52.         });
  53.     }

  54.     public void initialize(final RubyContext context, final RubyNode currentNode, Runnable runnable) {
  55.         final RubyThread finalThread = this;
  56.         final Runnable finalRunnable = runnable;

  57.         new Thread(new Runnable() {

  58.             @Override
  59.             public void run() {
  60.                 finalThread.manager.registerThread(finalThread);
  61.                 finalThread.manager.enterGlobalLock(finalThread);
  62.                 context.getSafepointManager().enterThread();

  63.                 try {
  64.                     finalRunnable.run();
  65.                 } catch (ThreadExitException e) {
  66.                     return;
  67.                 } catch (RaiseException e) {
  68.                     exception = e.getRubyException();
  69.                 } catch (ReturnException e) {
  70.                     exception = getContext().getCoreLibrary().unexpectedReturn(currentNode);
  71.                 } finally {
  72.                     finalThread.manager.leaveGlobalLock();
  73.                     finalThread.manager.unregisterThread(finalThread);
  74.                     finalThread.finished.countDown();
  75.                     context.getSafepointManager().leaveThread();
  76.                     status = Status.DEAD;
  77.                 }
  78.             }

  79.         }).start();
  80.     }

  81.     public void join() {
  82.         final RubyThread runningThread = getContext().getThreadManager().leaveGlobalLock();

  83.         try {
  84.             while (true) {
  85.                 try {
  86.                     finished.await();
  87.                     break;
  88.                 } catch (InterruptedException e) {
  89.                     // Await again
  90.                 }
  91.             }
  92.         } finally {
  93.             getContext().getThreadManager().enterGlobalLock(runningThread);
  94.         }

  95.         if (exception != null) {
  96.             throw new RaiseException(exception);
  97.         }
  98.     }

  99.     public Status getStatus() {
  100.         return status;
  101.     }

  102.     public RubyBasicObject getThreadLocals() {
  103.         return threadLocals;
  104.     }

  105.     public static class ThreadAllocator implements Allocator {

  106.         @Override
  107.         public RubyBasicObject allocate(RubyContext context, RubyClass rubyClass, RubyNode currentNode) {
  108.             return new RubyThread(rubyClass, context.getThreadManager());
  109.         }

  110.     }

  111. }