Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Fixes for test_thread in thread pooling mode

* Allow setting priorities for pooled threads
* Aggregate "dispose" functionality for threads
* Reset pooled thread priorities to initial
* Remove undefined-behavior "dead thread" priority test
* Make Thread.current keys test permissive about extra keys
  • Loading branch information...
commit affd4d8a90b84e4d0adb47228b30ff2305715dc6 1 parent 3676f3f
Charles Oliver Nutter headius authored
53 src/org/jruby/RubyThread.java
@@ -67,6 +67,8 @@
67 67 import org.jruby.runtime.ClassIndex;
68 68 import org.jruby.runtime.ObjectMarshal;
69 69 import static org.jruby.runtime.Visibility.*;
  70 +
  71 +import org.jruby.util.cli.Options;
70 72 import org.jruby.util.io.BlockingIO;
71 73 import org.jruby.util.io.SelectorFactory;
72 74 import org.jruby.util.log.Logger;
@@ -138,6 +140,12 @@
138 140 /** The list of locks this thread currently holds, so they can be released on exit */
139 141 private final List<Lock> heldLocks = new ArrayList<Lock>();
140 142
  143 + /** Whether or not this thread has been disposed of */
  144 + private volatile boolean disposed = false;
  145 +
  146 + /** The thread's initial priority, for use in thread pooled mode */
  147 + private int initialPriority;
  148 +
141 149 protected RubyThread(Ruby runtime, RubyClass type) {
142 150 super(runtime, type);
143 151
@@ -207,11 +215,46 @@ public ThreadContext getContext() {
207 215 public Thread getNativeThread() {
208 216 return threadImpl.nativeThread();
209 217 }
  218 +
210 219 /**
211   - * Dispose of the current thread by removing it from its parent ThreadGroup.
  220 + * Perform pre-execution tasks once the native thread is running, but we
  221 + * have not yet called the Ruby code for the thread.
212 222 */
213   - public void dispose() {
214   - threadGroup.remove(this);
  223 + public void beforeStart() {
  224 + // store initial priority, for restoring pooled threads to normal
  225 + initialPriority = threadImpl.getPriority();
  226 +
  227 + // set to "normal" priority
  228 + threadImpl.setPriority(Thread.NORM_PRIORITY);
  229 + }
  230 +
  231 + /**
  232 + * Dispose of the current thread by tidying up connections to other stuff
  233 + */
  234 + public synchronized void dispose() {
  235 + if (!disposed) {
  236 + disposed = true;
  237 +
  238 + // remove from parent thread group
  239 + threadGroup.remove(this);
  240 +
  241 + // unlock all locked locks
  242 + unlockAll();
  243 +
  244 + // clear all thread locals
  245 + clearThreadLocals();
  246 +
  247 + // reset thread priority to initial if pooling
  248 + if (Options.THREADPOOL_ENABLED.load()) {
  249 + threadImpl.setPriority(initialPriority);
  250 + }
  251 +
  252 + // mark thread as DEAD
  253 + beDead();
  254 +
  255 + // unregister from runtime's ThreadService
  256 + getRuntime().getThreadService().unregisterThread(this);
  257 + }
215 258 }
216 259
217 260 public static RubyClass createThreadClass(Ruby runtime) {
@@ -459,6 +502,10 @@ private IRubyObject getSymbolKey(IRubyObject originalKey) {
459 502 return threadLocalVariables;
460 503 }
461 504
  505 + private void clearThreadLocals() {
  506 + threadLocalVariables = null;
  507 + }
  508 +
462 509 public final Map<Object, IRubyObject> getContextVariables() {
463 510 return contextVariables;
464 511 }
21 src/org/jruby/internal/runtime/FutureThread.java
@@ -151,20 +151,29 @@ public void join(long millis) throws InterruptedException, ExecutionException {
151 151 }
152 152
153 153 /**
154   - * Jobs from the thread pool do not support setting priorities and always returns
155   - * current priority.
156   - *
  154 + * The current priority of the thread associated with this future.
  155 + *
157 156 * @return the current priority of the thread in which we this is running
158 157 */
159 158 public int getPriority() {
160   - return 1;
  159 + if (nativeThread == null) {
  160 + return 1;
  161 + }
  162 +
  163 + return nativeThread.getPriority();
161 164 }
162 165
163 166 /**
164   - * Jobs from the thread pool do not support setting priorities and always returns
165   - * current priority.
  167 + * Set the priority of the thread associated with this future.
  168 + *
  169 + * @param priority the new priority
166 170 */
167 171 public void setPriority(int priority) {
  172 + if (nativeThread == null) {
  173 + return;
  174 + }
  175 +
  176 + nativeThread.setPriority(priority);
168 177 }
169 178
170 179 public boolean isCurrent() {
8 src/org/jruby/internal/runtime/RubyRunnable.java
@@ -92,6 +92,7 @@ public void run() {
92 92 }
93 93
94 94 context.preRunThread(currentFrames);
  95 + rubyThread.beforeStart();
95 96
96 97 // uber-ThreadKill catcher, since it should always just mean "be dead"
97 98 try {
@@ -111,13 +112,8 @@ public void run() {
111 112 // Someone called exit!, so we need to kill the main thread
112 113 runtime.getThreadService().getMainThread().kill();
113 114 } finally {
114   - rubyThread.unlockAll();
115 115 runtime.getThreadService().setCritical(false);
116   - rubyThread.beDead();
117   -
118   - runtime.getThreadService().unregisterThread(rubyThread);
119   -
120   - ((RubyThreadGroup)rubyThread.group()).remove(rubyThread);
  116 + rubyThread.dispose();
121 117
122 118 // restore context classloader, in case we're using a thread pool
123 119 try {
21 test/test_thread.rb
@@ -45,9 +45,7 @@ def test_thread_local_variables
45 45 assert(Thread.current.key?(:x))
46 46 Thread.current["y"] = 2
47 47 assert(Thread.current.key?("y"))
48   - unless RUBY_VERSION =~ /1\.9/ # JRUBY-6485
49   - assert_equal([:x, :y], Thread.current.keys.sort {|x, y| x.to_s <=> y.to_s})
50   - end
  48 + assert_equal([:x, :y], Thread.current.keys.sort {|x, y| x.to_s <=> y.to_s} & [:x, :y])
51 49 assert_raises(TypeError) { Thread.current[Object.new] }
52 50 assert_raises(TypeError) { Thread.current[Object.new] = 1 }
53 51 assert_raises(ArgumentError) { Thread.current[1] }
@@ -156,12 +154,17 @@ def test_thread_subclass_zsuper
156 154 assert_equal(2, x.value)
157 155 end
158 156
159   - def test_dead_thread_priority
160   - x = Thread.new {}
161   - 1 while x.alive?
162   - x.priority = 5
163   - assert_equal(5, x.priority)
164   - end
  157 + # Because a Ruby thread may use a pooled thread, we will
  158 + # not preserve priorities set into dead threads. Because
  159 + # this is a meaningless feature, anyway, I remove it here
  160 + # and consider this behavior undefined. CON@20120306
  161 +
  162 + # def test_dead_thread_priority
  163 + # x = Thread.new {}
  164 + # 1 while x.alive?
  165 + # x.priority = 5
  166 + # assert_equal(5, x.priority)
  167 + # end
165 168
166 169 def test_join_returns_thread
167 170 x = Thread.new {}

0 comments on commit affd4d8

Please sign in to comment.
Something went wrong with that request. Please try again.