src/share/vm/utilities/taskqueue.hpp

Thu, 20 Nov 2008 16:56:09 -0800

author
ysr
date
Thu, 20 Nov 2008 16:56:09 -0800
changeset 888
c96030fff130
parent 810
81cd571500b0
child 976
23673011938d
permissions
-rw-r--r--

6684579: SoftReference processing can be made more efficient
Summary: For current soft-ref clearing policies, we can decide at marking time if a soft-reference will definitely not be cleared, postponing the decision of whether it will definitely be cleared to the final reference processing phase. This can be especially beneficial in the case of concurrent collectors where the marking is usually concurrent but reference processing is usually not.
Reviewed-by: jmasa

duke@435 1 /*
xdono@631 2 * Copyright 2001-2008 Sun Microsystems, Inc. All Rights Reserved.
duke@435 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@435 4 *
duke@435 5 * This code is free software; you can redistribute it and/or modify it
duke@435 6 * under the terms of the GNU General Public License version 2 only, as
duke@435 7 * published by the Free Software Foundation.
duke@435 8 *
duke@435 9 * This code is distributed in the hope that it will be useful, but WITHOUT
duke@435 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@435 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@435 12 * version 2 for more details (a copy is included in the LICENSE file that
duke@435 13 * accompanied this code).
duke@435 14 *
duke@435 15 * You should have received a copy of the GNU General Public License version
duke@435 16 * 2 along with this work; if not, write to the Free Software Foundation,
duke@435 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@435 18 *
duke@435 19 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
duke@435 20 * CA 95054 USA or visit www.sun.com if you need additional information or
duke@435 21 * have any questions.
duke@435 22 *
duke@435 23 */
duke@435 24
duke@435 25 class TaskQueueSuper: public CHeapObj {
duke@435 26 protected:
duke@435 27 // The first free element after the last one pushed (mod _n).
duke@435 28 // (For now we'll assume only 32-bit CAS).
duke@435 29 volatile juint _bottom;
duke@435 30
duke@435 31 // log2 of the size of the queue.
duke@435 32 enum SomeProtectedConstants {
duke@435 33 Log_n = 14
duke@435 34 };
duke@435 35
duke@435 36 // Size of the queue.
duke@435 37 juint n() { return (1 << Log_n); }
duke@435 38 // For computing "x mod n" efficiently.
duke@435 39 juint n_mod_mask() { return n() - 1; }
duke@435 40
duke@435 41 struct Age {
duke@435 42 jushort _top;
duke@435 43 jushort _tag;
duke@435 44
duke@435 45 jushort tag() const { return _tag; }
duke@435 46 jushort top() const { return _top; }
duke@435 47
duke@435 48 Age() { _tag = 0; _top = 0; }
duke@435 49
duke@435 50 friend bool operator ==(const Age& a1, const Age& a2) {
duke@435 51 return a1.tag() == a2.tag() && a1.top() == a2.top();
duke@435 52 }
duke@435 53
duke@435 54 };
duke@435 55 Age _age;
duke@435 56 // These make sure we do single atomic reads and writes.
duke@435 57 Age get_age() {
duke@435 58 jint res = *(volatile jint*)(&_age);
duke@435 59 return *(Age*)(&res);
duke@435 60 }
duke@435 61 void set_age(Age a) {
duke@435 62 *(volatile jint*)(&_age) = *(int*)(&a);
duke@435 63 }
duke@435 64
duke@435 65 jushort get_top() {
duke@435 66 return get_age().top();
duke@435 67 }
duke@435 68
duke@435 69 // These both operate mod _n.
duke@435 70 juint increment_index(juint ind) {
duke@435 71 return (ind + 1) & n_mod_mask();
duke@435 72 }
duke@435 73 juint decrement_index(juint ind) {
duke@435 74 return (ind - 1) & n_mod_mask();
duke@435 75 }
duke@435 76
duke@435 77 // Returns a number in the range [0.._n). If the result is "n-1", it
duke@435 78 // should be interpreted as 0.
duke@435 79 juint dirty_size(juint bot, juint top) {
duke@435 80 return ((jint)bot - (jint)top) & n_mod_mask();
duke@435 81 }
duke@435 82
duke@435 83 // Returns the size corresponding to the given "bot" and "top".
duke@435 84 juint size(juint bot, juint top) {
duke@435 85 juint sz = dirty_size(bot, top);
duke@435 86 // Has the queue "wrapped", so that bottom is less than top?
duke@435 87 // There's a complicated special case here. A pair of threads could
duke@435 88 // perform pop_local and pop_global operations concurrently, starting
duke@435 89 // from a state in which _bottom == _top+1. The pop_local could
duke@435 90 // succeed in decrementing _bottom, and the pop_global in incrementing
duke@435 91 // _top (in which case the pop_global will be awarded the contested
duke@435 92 // queue element.) The resulting state must be interpreted as an empty
duke@435 93 // queue. (We only need to worry about one such event: only the queue
duke@435 94 // owner performs pop_local's, and several concurrent threads
duke@435 95 // attempting to perform the pop_global will all perform the same CAS,
duke@435 96 // and only one can succeed. Any stealing thread that reads after
duke@435 97 // either the increment or decrement will seen an empty queue, and will
duke@435 98 // not join the competitors. The "sz == -1 || sz == _n-1" state will
duke@435 99 // not be modified by concurrent queues, so the owner thread can reset
duke@435 100 // the state to _bottom == top so subsequent pushes will be performed
duke@435 101 // normally.
duke@435 102 if (sz == (n()-1)) return 0;
duke@435 103 else return sz;
duke@435 104 }
duke@435 105
duke@435 106 public:
duke@435 107 TaskQueueSuper() : _bottom(0), _age() {}
duke@435 108
duke@435 109 // Return "true" if the TaskQueue contains any tasks.
duke@435 110 bool peek();
duke@435 111
duke@435 112 // Return an estimate of the number of elements in the queue.
duke@435 113 // The "careful" version admits the possibility of pop_local/pop_global
duke@435 114 // races.
duke@435 115 juint size() {
duke@435 116 return size(_bottom, get_top());
duke@435 117 }
duke@435 118
duke@435 119 juint dirty_size() {
duke@435 120 return dirty_size(_bottom, get_top());
duke@435 121 }
duke@435 122
ysr@777 123 void set_empty() {
ysr@777 124 _bottom = 0;
ysr@777 125 _age = Age();
ysr@777 126 }
ysr@777 127
duke@435 128 // Maximum number of elements allowed in the queue. This is two less
duke@435 129 // than the actual queue size, for somewhat complicated reasons.
duke@435 130 juint max_elems() { return n() - 2; }
duke@435 131
duke@435 132 };
duke@435 133
duke@435 134 template<class E> class GenericTaskQueue: public TaskQueueSuper {
duke@435 135 private:
duke@435 136 // Slow paths for push, pop_local. (pop_global has no fast path.)
duke@435 137 bool push_slow(E t, juint dirty_n_elems);
duke@435 138 bool pop_local_slow(juint localBot, Age oldAge);
duke@435 139
duke@435 140 public:
duke@435 141 // Initializes the queue to empty.
duke@435 142 GenericTaskQueue();
duke@435 143
duke@435 144 void initialize();
duke@435 145
duke@435 146 // Push the task "t" on the queue. Returns "false" iff the queue is
duke@435 147 // full.
duke@435 148 inline bool push(E t);
duke@435 149
duke@435 150 // If succeeds in claiming a task (from the 'local' end, that is, the
duke@435 151 // most recently pushed task), returns "true" and sets "t" to that task.
duke@435 152 // Otherwise, the queue is empty and returns false.
duke@435 153 inline bool pop_local(E& t);
duke@435 154
duke@435 155 // If succeeds in claiming a task (from the 'global' end, that is, the
duke@435 156 // least recently pushed task), returns "true" and sets "t" to that task.
duke@435 157 // Otherwise, the queue is empty and returns false.
duke@435 158 bool pop_global(E& t);
duke@435 159
duke@435 160 // Delete any resource associated with the queue.
duke@435 161 ~GenericTaskQueue();
duke@435 162
ysr@777 163 // apply the closure to all elements in the task queue
ysr@777 164 void oops_do(OopClosure* f);
ysr@777 165
duke@435 166 private:
duke@435 167 // Element array.
duke@435 168 volatile E* _elems;
duke@435 169 };
duke@435 170
duke@435 171 template<class E>
duke@435 172 GenericTaskQueue<E>::GenericTaskQueue():TaskQueueSuper() {
duke@435 173 assert(sizeof(Age) == sizeof(jint), "Depends on this.");
duke@435 174 }
duke@435 175
duke@435 176 template<class E>
duke@435 177 void GenericTaskQueue<E>::initialize() {
duke@435 178 _elems = NEW_C_HEAP_ARRAY(E, n());
duke@435 179 guarantee(_elems != NULL, "Allocation failed.");
duke@435 180 }
duke@435 181
duke@435 182 template<class E>
ysr@777 183 void GenericTaskQueue<E>::oops_do(OopClosure* f) {
ysr@777 184 // tty->print_cr("START OopTaskQueue::oops_do");
ysr@777 185 int iters = size();
ysr@777 186 juint index = _bottom;
ysr@777 187 for (int i = 0; i < iters; ++i) {
ysr@777 188 index = decrement_index(index);
ysr@777 189 // tty->print_cr(" doing entry %d," INTPTR_T " -> " INTPTR_T,
ysr@777 190 // index, &_elems[index], _elems[index]);
ysr@777 191 E* t = (E*)&_elems[index]; // cast away volatility
ysr@777 192 oop* p = (oop*)t;
ysr@777 193 assert((*t)->is_oop_or_null(), "Not an oop or null");
ysr@777 194 f->do_oop(p);
ysr@777 195 }
ysr@777 196 // tty->print_cr("END OopTaskQueue::oops_do");
ysr@777 197 }
ysr@777 198
ysr@777 199
ysr@777 200 template<class E>
duke@435 201 bool GenericTaskQueue<E>::push_slow(E t, juint dirty_n_elems) {
duke@435 202 if (dirty_n_elems == n() - 1) {
duke@435 203 // Actually means 0, so do the push.
duke@435 204 juint localBot = _bottom;
duke@435 205 _elems[localBot] = t;
duke@435 206 _bottom = increment_index(localBot);
duke@435 207 return true;
duke@435 208 } else
duke@435 209 return false;
duke@435 210 }
duke@435 211
duke@435 212 template<class E>
duke@435 213 bool GenericTaskQueue<E>::
duke@435 214 pop_local_slow(juint localBot, Age oldAge) {
duke@435 215 // This queue was observed to contain exactly one element; either this
duke@435 216 // thread will claim it, or a competing "pop_global". In either case,
duke@435 217 // the queue will be logically empty afterwards. Create a new Age value
duke@435 218 // that represents the empty queue for the given value of "_bottom". (We
duke@435 219 // must also increment "tag" because of the case where "bottom == 1",
duke@435 220 // "top == 0". A pop_global could read the queue element in that case,
duke@435 221 // then have the owner thread do a pop followed by another push. Without
duke@435 222 // the incrementing of "tag", the pop_global's CAS could succeed,
duke@435 223 // allowing it to believe it has claimed the stale element.)
duke@435 224 Age newAge;
duke@435 225 newAge._top = localBot;
duke@435 226 newAge._tag = oldAge.tag() + 1;
duke@435 227 // Perhaps a competing pop_global has already incremented "top", in which
duke@435 228 // case it wins the element.
duke@435 229 if (localBot == oldAge.top()) {
duke@435 230 Age tempAge;
duke@435 231 // No competing pop_global has yet incremented "top"; we'll try to
duke@435 232 // install new_age, thus claiming the element.
duke@435 233 assert(sizeof(Age) == sizeof(jint) && sizeof(jint) == sizeof(juint),
duke@435 234 "Assumption about CAS unit.");
duke@435 235 *(jint*)&tempAge = Atomic::cmpxchg(*(jint*)&newAge, (volatile jint*)&_age, *(jint*)&oldAge);
duke@435 236 if (tempAge == oldAge) {
duke@435 237 // We win.
duke@435 238 assert(dirty_size(localBot, get_top()) != n() - 1,
duke@435 239 "Shouldn't be possible...");
duke@435 240 return true;
duke@435 241 }
duke@435 242 }
duke@435 243 // We fail; a completing pop_global gets the element. But the queue is
duke@435 244 // empty (and top is greater than bottom.) Fix this representation of
duke@435 245 // the empty queue to become the canonical one.
duke@435 246 set_age(newAge);
duke@435 247 assert(dirty_size(localBot, get_top()) != n() - 1,
duke@435 248 "Shouldn't be possible...");
duke@435 249 return false;
duke@435 250 }
duke@435 251
duke@435 252 template<class E>
duke@435 253 bool GenericTaskQueue<E>::pop_global(E& t) {
duke@435 254 Age newAge;
duke@435 255 Age oldAge = get_age();
duke@435 256 juint localBot = _bottom;
duke@435 257 juint n_elems = size(localBot, oldAge.top());
duke@435 258 if (n_elems == 0) {
duke@435 259 return false;
duke@435 260 }
duke@435 261 t = _elems[oldAge.top()];
duke@435 262 newAge = oldAge;
duke@435 263 newAge._top = increment_index(newAge.top());
duke@435 264 if ( newAge._top == 0 ) newAge._tag++;
duke@435 265 Age resAge;
duke@435 266 *(jint*)&resAge = Atomic::cmpxchg(*(jint*)&newAge, (volatile jint*)&_age, *(jint*)&oldAge);
duke@435 267 // Note that using "_bottom" here might fail, since a pop_local might
duke@435 268 // have decremented it.
duke@435 269 assert(dirty_size(localBot, newAge._top) != n() - 1,
duke@435 270 "Shouldn't be possible...");
duke@435 271 return (resAge == oldAge);
duke@435 272 }
duke@435 273
duke@435 274 template<class E>
duke@435 275 GenericTaskQueue<E>::~GenericTaskQueue() {
duke@435 276 FREE_C_HEAP_ARRAY(E, _elems);
duke@435 277 }
duke@435 278
duke@435 279 // Inherits the typedef of "Task" from above.
duke@435 280 class TaskQueueSetSuper: public CHeapObj {
duke@435 281 protected:
duke@435 282 static int randomParkAndMiller(int* seed0);
duke@435 283 public:
duke@435 284 // Returns "true" if some TaskQueue in the set contains a task.
duke@435 285 virtual bool peek() = 0;
duke@435 286 };
duke@435 287
duke@435 288 template<class E> class GenericTaskQueueSet: public TaskQueueSetSuper {
duke@435 289 private:
duke@435 290 int _n;
duke@435 291 GenericTaskQueue<E>** _queues;
duke@435 292
duke@435 293 public:
duke@435 294 GenericTaskQueueSet(int n) : _n(n) {
duke@435 295 typedef GenericTaskQueue<E>* GenericTaskQueuePtr;
duke@435 296 _queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n);
duke@435 297 guarantee(_queues != NULL, "Allocation failure.");
duke@435 298 for (int i = 0; i < n; i++) {
duke@435 299 _queues[i] = NULL;
duke@435 300 }
duke@435 301 }
duke@435 302
duke@435 303 bool steal_1_random(int queue_num, int* seed, E& t);
duke@435 304 bool steal_best_of_2(int queue_num, int* seed, E& t);
duke@435 305 bool steal_best_of_all(int queue_num, int* seed, E& t);
duke@435 306
duke@435 307 void register_queue(int i, GenericTaskQueue<E>* q);
duke@435 308
duke@435 309 GenericTaskQueue<E>* queue(int n);
duke@435 310
duke@435 311 // The thread with queue number "queue_num" (and whose random number seed
duke@435 312 // is at "seed") is trying to steal a task from some other queue. (It
duke@435 313 // may try several queues, according to some configuration parameter.)
duke@435 314 // If some steal succeeds, returns "true" and sets "t" the stolen task,
duke@435 315 // otherwise returns false.
duke@435 316 bool steal(int queue_num, int* seed, E& t);
duke@435 317
duke@435 318 bool peek();
duke@435 319 };
duke@435 320
duke@435 321 template<class E>
duke@435 322 void GenericTaskQueueSet<E>::register_queue(int i, GenericTaskQueue<E>* q) {
duke@435 323 assert(0 <= i && i < _n, "index out of range.");
duke@435 324 _queues[i] = q;
duke@435 325 }
duke@435 326
duke@435 327 template<class E>
duke@435 328 GenericTaskQueue<E>* GenericTaskQueueSet<E>::queue(int i) {
duke@435 329 return _queues[i];
duke@435 330 }
duke@435 331
duke@435 332 template<class E>
duke@435 333 bool GenericTaskQueueSet<E>::steal(int queue_num, int* seed, E& t) {
duke@435 334 for (int i = 0; i < 2 * _n; i++)
duke@435 335 if (steal_best_of_2(queue_num, seed, t))
duke@435 336 return true;
duke@435 337 return false;
duke@435 338 }
duke@435 339
duke@435 340 template<class E>
duke@435 341 bool GenericTaskQueueSet<E>::steal_best_of_all(int queue_num, int* seed, E& t) {
duke@435 342 if (_n > 2) {
duke@435 343 int best_k;
duke@435 344 jint best_sz = 0;
duke@435 345 for (int k = 0; k < _n; k++) {
duke@435 346 if (k == queue_num) continue;
duke@435 347 jint sz = _queues[k]->size();
duke@435 348 if (sz > best_sz) {
duke@435 349 best_sz = sz;
duke@435 350 best_k = k;
duke@435 351 }
duke@435 352 }
duke@435 353 return best_sz > 0 && _queues[best_k]->pop_global(t);
duke@435 354 } else if (_n == 2) {
duke@435 355 // Just try the other one.
duke@435 356 int k = (queue_num + 1) % 2;
duke@435 357 return _queues[k]->pop_global(t);
duke@435 358 } else {
duke@435 359 assert(_n == 1, "can't be zero.");
duke@435 360 return false;
duke@435 361 }
duke@435 362 }
duke@435 363
duke@435 364 template<class E>
duke@435 365 bool GenericTaskQueueSet<E>::steal_1_random(int queue_num, int* seed, E& t) {
duke@435 366 if (_n > 2) {
duke@435 367 int k = queue_num;
duke@435 368 while (k == queue_num) k = randomParkAndMiller(seed) % _n;
duke@435 369 return _queues[2]->pop_global(t);
duke@435 370 } else if (_n == 2) {
duke@435 371 // Just try the other one.
duke@435 372 int k = (queue_num + 1) % 2;
duke@435 373 return _queues[k]->pop_global(t);
duke@435 374 } else {
duke@435 375 assert(_n == 1, "can't be zero.");
duke@435 376 return false;
duke@435 377 }
duke@435 378 }
duke@435 379
duke@435 380 template<class E>
duke@435 381 bool GenericTaskQueueSet<E>::steal_best_of_2(int queue_num, int* seed, E& t) {
duke@435 382 if (_n > 2) {
duke@435 383 int k1 = queue_num;
duke@435 384 while (k1 == queue_num) k1 = randomParkAndMiller(seed) % _n;
duke@435 385 int k2 = queue_num;
duke@435 386 while (k2 == queue_num || k2 == k1) k2 = randomParkAndMiller(seed) % _n;
duke@435 387 // Sample both and try the larger.
duke@435 388 juint sz1 = _queues[k1]->size();
duke@435 389 juint sz2 = _queues[k2]->size();
duke@435 390 if (sz2 > sz1) return _queues[k2]->pop_global(t);
duke@435 391 else return _queues[k1]->pop_global(t);
duke@435 392 } else if (_n == 2) {
duke@435 393 // Just try the other one.
duke@435 394 int k = (queue_num + 1) % 2;
duke@435 395 return _queues[k]->pop_global(t);
duke@435 396 } else {
duke@435 397 assert(_n == 1, "can't be zero.");
duke@435 398 return false;
duke@435 399 }
duke@435 400 }
duke@435 401
duke@435 402 template<class E>
duke@435 403 bool GenericTaskQueueSet<E>::peek() {
duke@435 404 // Try all the queues.
duke@435 405 for (int j = 0; j < _n; j++) {
duke@435 406 if (_queues[j]->peek())
duke@435 407 return true;
duke@435 408 }
duke@435 409 return false;
duke@435 410 }
duke@435 411
ysr@777 412 // When to terminate from the termination protocol.
ysr@777 413 class TerminatorTerminator: public CHeapObj {
ysr@777 414 public:
ysr@777 415 virtual bool should_exit_termination() = 0;
ysr@777 416 };
ysr@777 417
duke@435 418 // A class to aid in the termination of a set of parallel tasks using
duke@435 419 // TaskQueueSet's for work stealing.
duke@435 420
duke@435 421 class ParallelTaskTerminator: public StackObj {
duke@435 422 private:
duke@435 423 int _n_threads;
duke@435 424 TaskQueueSetSuper* _queue_set;
duke@435 425 jint _offered_termination;
duke@435 426
duke@435 427 bool peek_in_queue_set();
duke@435 428 protected:
duke@435 429 virtual void yield();
duke@435 430 void sleep(uint millis);
duke@435 431
duke@435 432 public:
duke@435 433
duke@435 434 // "n_threads" is the number of threads to be terminated. "queue_set" is a
duke@435 435 // queue sets of work queues of other threads.
duke@435 436 ParallelTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set);
duke@435 437
duke@435 438 // The current thread has no work, and is ready to terminate if everyone
duke@435 439 // else is. If returns "true", all threads are terminated. If returns
duke@435 440 // "false", available work has been observed in one of the task queues,
duke@435 441 // so the global task is not complete.
ysr@777 442 bool offer_termination() {
ysr@777 443 return offer_termination(NULL);
ysr@777 444 }
ysr@777 445
ysr@777 446 // As above, but it also terminates of the should_exit_termination()
ysr@777 447 // method of the terminator parameter returns true. If terminator is
ysr@777 448 // NULL, then it is ignored.
ysr@777 449 bool offer_termination(TerminatorTerminator* terminator);
duke@435 450
duke@435 451 // Reset the terminator, so that it may be reused again.
duke@435 452 // The caller is responsible for ensuring that this is done
duke@435 453 // in an MT-safe manner, once the previous round of use of
duke@435 454 // the terminator is finished.
duke@435 455 void reset_for_reuse();
duke@435 456
duke@435 457 };
duke@435 458
duke@435 459 #define SIMPLE_STACK 0
duke@435 460
duke@435 461 template<class E> inline bool GenericTaskQueue<E>::push(E t) {
duke@435 462 #if SIMPLE_STACK
duke@435 463 juint localBot = _bottom;
duke@435 464 if (_bottom < max_elems()) {
duke@435 465 _elems[localBot] = t;
duke@435 466 _bottom = localBot + 1;
duke@435 467 return true;
duke@435 468 } else {
duke@435 469 return false;
duke@435 470 }
duke@435 471 #else
duke@435 472 juint localBot = _bottom;
duke@435 473 assert((localBot >= 0) && (localBot < n()), "_bottom out of range.");
duke@435 474 jushort top = get_top();
duke@435 475 juint dirty_n_elems = dirty_size(localBot, top);
duke@435 476 assert((dirty_n_elems >= 0) && (dirty_n_elems < n()),
duke@435 477 "n_elems out of range.");
duke@435 478 if (dirty_n_elems < max_elems()) {
duke@435 479 _elems[localBot] = t;
duke@435 480 _bottom = increment_index(localBot);
duke@435 481 return true;
duke@435 482 } else {
duke@435 483 return push_slow(t, dirty_n_elems);
duke@435 484 }
duke@435 485 #endif
duke@435 486 }
duke@435 487
duke@435 488 template<class E> inline bool GenericTaskQueue<E>::pop_local(E& t) {
duke@435 489 #if SIMPLE_STACK
duke@435 490 juint localBot = _bottom;
duke@435 491 assert(localBot > 0, "precondition.");
duke@435 492 localBot--;
duke@435 493 t = _elems[localBot];
duke@435 494 _bottom = localBot;
duke@435 495 return true;
duke@435 496 #else
duke@435 497 juint localBot = _bottom;
duke@435 498 // This value cannot be n-1. That can only occur as a result of
duke@435 499 // the assignment to bottom in this method. If it does, this method
duke@435 500 // resets the size( to 0 before the next call (which is sequential,
duke@435 501 // since this is pop_local.)
duke@435 502 juint dirty_n_elems = dirty_size(localBot, get_top());
duke@435 503 assert(dirty_n_elems != n() - 1, "Shouldn't be possible...");
duke@435 504 if (dirty_n_elems == 0) return false;
duke@435 505 localBot = decrement_index(localBot);
duke@435 506 _bottom = localBot;
duke@435 507 // This is necessary to prevent any read below from being reordered
duke@435 508 // before the store just above.
duke@435 509 OrderAccess::fence();
duke@435 510 t = _elems[localBot];
duke@435 511 // This is a second read of "age"; the "size()" above is the first.
duke@435 512 // If there's still at least one element in the queue, based on the
duke@435 513 // "_bottom" and "age" we've read, then there can be no interference with
duke@435 514 // a "pop_global" operation, and we're done.
duke@435 515 juint tp = get_top();
duke@435 516 if (size(localBot, tp) > 0) {
duke@435 517 assert(dirty_size(localBot, tp) != n() - 1,
duke@435 518 "Shouldn't be possible...");
duke@435 519 return true;
duke@435 520 } else {
duke@435 521 // Otherwise, the queue contained exactly one element; we take the slow
duke@435 522 // path.
duke@435 523 return pop_local_slow(localBot, get_age());
duke@435 524 }
duke@435 525 #endif
duke@435 526 }
duke@435 527
duke@435 528 typedef oop Task;
duke@435 529 typedef GenericTaskQueue<Task> OopTaskQueue;
duke@435 530 typedef GenericTaskQueueSet<Task> OopTaskQueueSet;
duke@435 531
coleenp@548 532
coleenp@548 533 #define COMPRESSED_OOP_MASK 1
coleenp@548 534
coleenp@548 535 // This is a container class for either an oop* or a narrowOop*.
coleenp@548 536 // Both are pushed onto a task queue and the consumer will test is_narrow()
coleenp@548 537 // to determine which should be processed.
coleenp@548 538 class StarTask {
coleenp@548 539 void* _holder; // either union oop* or narrowOop*
coleenp@548 540 public:
coleenp@548 541 StarTask(narrowOop *p) { _holder = (void *)((uintptr_t)p | COMPRESSED_OOP_MASK); }
coleenp@548 542 StarTask(oop *p) { _holder = (void*)p; }
coleenp@548 543 StarTask() { _holder = NULL; }
coleenp@548 544 operator oop*() { return (oop*)_holder; }
coleenp@548 545 operator narrowOop*() {
coleenp@548 546 return (narrowOop*)((uintptr_t)_holder & ~COMPRESSED_OOP_MASK);
coleenp@548 547 }
coleenp@548 548
coleenp@548 549 // Operators to preserve const/volatile in assignments required by gcc
coleenp@548 550 void operator=(const volatile StarTask& t) volatile { _holder = t._holder; }
coleenp@548 551
coleenp@548 552 bool is_narrow() const {
coleenp@548 553 return (((uintptr_t)_holder & COMPRESSED_OOP_MASK) != 0);
coleenp@548 554 }
coleenp@548 555 };
coleenp@548 556
duke@435 557 typedef GenericTaskQueue<StarTask> OopStarTaskQueue;
duke@435 558 typedef GenericTaskQueueSet<StarTask> OopStarTaskQueueSet;
duke@435 559
jcoomes@810 560 typedef size_t RegionTask; // index for region
jcoomes@810 561 typedef GenericTaskQueue<RegionTask> RegionTaskQueue;
jcoomes@810 562 typedef GenericTaskQueueSet<RegionTask> RegionTaskQueueSet;
duke@435 563
jcoomes@810 564 class RegionTaskQueueWithOverflow: public CHeapObj {
duke@435 565 protected:
jcoomes@810 566 RegionTaskQueue _region_queue;
jcoomes@810 567 GrowableArray<RegionTask>* _overflow_stack;
duke@435 568
duke@435 569 public:
jcoomes@810 570 RegionTaskQueueWithOverflow() : _overflow_stack(NULL) {}
duke@435 571 // Initialize both stealable queue and overflow
duke@435 572 void initialize();
duke@435 573 // Save first to stealable queue and then to overflow
jcoomes@810 574 void save(RegionTask t);
duke@435 575 // Retrieve first from overflow and then from stealable queue
jcoomes@810 576 bool retrieve(RegionTask& region_index);
duke@435 577 // Retrieve from stealable queue
jcoomes@810 578 bool retrieve_from_stealable_queue(RegionTask& region_index);
duke@435 579 // Retrieve from overflow
jcoomes@810 580 bool retrieve_from_overflow(RegionTask& region_index);
duke@435 581 bool is_empty();
duke@435 582 bool stealable_is_empty();
duke@435 583 bool overflow_is_empty();
jcoomes@810 584 juint stealable_size() { return _region_queue.size(); }
jcoomes@810 585 RegionTaskQueue* task_queue() { return &_region_queue; }
duke@435 586 };
duke@435 587
jcoomes@810 588 #define USE_RegionTaskQueueWithOverflow

mercurial