duke@435: /* xdono@1014: * Copyright 2001-2009 Sun Microsystems, Inc. All Rights Reserved. duke@435: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. duke@435: * duke@435: * This code is free software; you can redistribute it and/or modify it duke@435: * under the terms of the GNU General Public License version 2 only, as duke@435: * published by the Free Software Foundation. duke@435: * duke@435: * This code is distributed in the hope that it will be useful, but WITHOUT duke@435: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or duke@435: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License duke@435: * version 2 for more details (a copy is included in the LICENSE file that duke@435: * accompanied this code). duke@435: * duke@435: * You should have received a copy of the GNU General Public License version duke@435: * 2 along with this work; if not, write to the Free Software Foundation, duke@435: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. duke@435: * duke@435: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, duke@435: * CA 95054 USA or visit www.sun.com if you need additional information or duke@435: * have any questions. duke@435: * duke@435: */ duke@435: ysr@976: #ifdef LP64 ysr@976: typedef juint TAG_TYPE; ysr@976: // for a taskqueue size of 4M ysr@976: #define LOG_TASKQ_SIZE 22 ysr@976: #else ysr@976: typedef jushort TAG_TYPE; ysr@976: // for a taskqueue size of 16K ysr@976: #define LOG_TASKQ_SIZE 14 ysr@976: #endif ysr@976: duke@435: class TaskQueueSuper: public CHeapObj { duke@435: protected: duke@435: // The first free element after the last one pushed (mod _n). ysr@976: volatile uint _bottom; duke@435: duke@435: // log2 of the size of the queue. duke@435: enum SomeProtectedConstants { ysr@976: Log_n = LOG_TASKQ_SIZE duke@435: }; ysr@976: #undef LOG_TASKQ_SIZE duke@435: duke@435: // Size of the queue. ysr@976: uint n() { return (1 << Log_n); } duke@435: // For computing "x mod n" efficiently. ysr@976: uint n_mod_mask() { return n() - 1; } duke@435: duke@435: struct Age { ysr@976: TAG_TYPE _top; ysr@976: TAG_TYPE _tag; duke@435: ysr@976: TAG_TYPE tag() const { return _tag; } ysr@976: TAG_TYPE top() const { return _top; } duke@435: duke@435: Age() { _tag = 0; _top = 0; } duke@435: duke@435: friend bool operator ==(const Age& a1, const Age& a2) { duke@435: return a1.tag() == a2.tag() && a1.top() == a2.top(); duke@435: } duke@435: }; duke@435: Age _age; duke@435: // These make sure we do single atomic reads and writes. duke@435: Age get_age() { ysr@976: uint res = *(volatile uint*)(&_age); duke@435: return *(Age*)(&res); duke@435: } duke@435: void set_age(Age a) { ysr@976: *(volatile uint*)(&_age) = *(uint*)(&a); duke@435: } duke@435: ysr@976: TAG_TYPE get_top() { duke@435: return get_age().top(); duke@435: } duke@435: duke@435: // These both operate mod _n. ysr@976: uint increment_index(uint ind) { duke@435: return (ind + 1) & n_mod_mask(); duke@435: } ysr@976: uint decrement_index(uint ind) { duke@435: return (ind - 1) & n_mod_mask(); duke@435: } duke@435: duke@435: // Returns a number in the range [0.._n). If the result is "n-1", it duke@435: // should be interpreted as 0. ysr@976: uint dirty_size(uint bot, uint top) { ysr@976: return ((int)bot - (int)top) & n_mod_mask(); duke@435: } duke@435: duke@435: // Returns the size corresponding to the given "bot" and "top". ysr@976: uint size(uint bot, uint top) { ysr@976: uint sz = dirty_size(bot, top); duke@435: // Has the queue "wrapped", so that bottom is less than top? duke@435: // There's a complicated special case here. A pair of threads could duke@435: // perform pop_local and pop_global operations concurrently, starting duke@435: // from a state in which _bottom == _top+1. The pop_local could duke@435: // succeed in decrementing _bottom, and the pop_global in incrementing duke@435: // _top (in which case the pop_global will be awarded the contested duke@435: // queue element.) The resulting state must be interpreted as an empty duke@435: // queue. (We only need to worry about one such event: only the queue duke@435: // owner performs pop_local's, and several concurrent threads duke@435: // attempting to perform the pop_global will all perform the same CAS, duke@435: // and only one can succeed. Any stealing thread that reads after ysr@976: // either the increment or decrement will see an empty queue, and will duke@435: // not join the competitors. The "sz == -1 || sz == _n-1" state will duke@435: // not be modified by concurrent queues, so the owner thread can reset duke@435: // the state to _bottom == top so subsequent pushes will be performed duke@435: // normally. duke@435: if (sz == (n()-1)) return 0; duke@435: else return sz; duke@435: } duke@435: duke@435: public: duke@435: TaskQueueSuper() : _bottom(0), _age() {} duke@435: duke@435: // Return "true" if the TaskQueue contains any tasks. duke@435: bool peek(); duke@435: duke@435: // Return an estimate of the number of elements in the queue. duke@435: // The "careful" version admits the possibility of pop_local/pop_global duke@435: // races. ysr@976: uint size() { duke@435: return size(_bottom, get_top()); duke@435: } duke@435: ysr@976: uint dirty_size() { duke@435: return dirty_size(_bottom, get_top()); duke@435: } duke@435: ysr@777: void set_empty() { ysr@777: _bottom = 0; ysr@777: _age = Age(); ysr@777: } ysr@777: duke@435: // Maximum number of elements allowed in the queue. This is two less duke@435: // than the actual queue size, for somewhat complicated reasons. ysr@976: uint max_elems() { return n() - 2; } duke@435: duke@435: }; duke@435: duke@435: template class GenericTaskQueue: public TaskQueueSuper { duke@435: private: duke@435: // Slow paths for push, pop_local. (pop_global has no fast path.) ysr@976: bool push_slow(E t, uint dirty_n_elems); ysr@976: bool pop_local_slow(uint localBot, Age oldAge); duke@435: duke@435: public: duke@435: // Initializes the queue to empty. duke@435: GenericTaskQueue(); duke@435: duke@435: void initialize(); duke@435: duke@435: // Push the task "t" on the queue. Returns "false" iff the queue is duke@435: // full. duke@435: inline bool push(E t); duke@435: duke@435: // If succeeds in claiming a task (from the 'local' end, that is, the duke@435: // most recently pushed task), returns "true" and sets "t" to that task. duke@435: // Otherwise, the queue is empty and returns false. duke@435: inline bool pop_local(E& t); duke@435: duke@435: // If succeeds in claiming a task (from the 'global' end, that is, the duke@435: // least recently pushed task), returns "true" and sets "t" to that task. duke@435: // Otherwise, the queue is empty and returns false. duke@435: bool pop_global(E& t); duke@435: duke@435: // Delete any resource associated with the queue. duke@435: ~GenericTaskQueue(); duke@435: ysr@777: // apply the closure to all elements in the task queue ysr@777: void oops_do(OopClosure* f); ysr@777: duke@435: private: duke@435: // Element array. duke@435: volatile E* _elems; duke@435: }; duke@435: duke@435: template duke@435: GenericTaskQueue::GenericTaskQueue():TaskQueueSuper() { ysr@976: assert(sizeof(Age) == sizeof(int), "Depends on this."); duke@435: } duke@435: duke@435: template duke@435: void GenericTaskQueue::initialize() { duke@435: _elems = NEW_C_HEAP_ARRAY(E, n()); duke@435: guarantee(_elems != NULL, "Allocation failed."); duke@435: } duke@435: duke@435: template ysr@777: void GenericTaskQueue::oops_do(OopClosure* f) { ysr@777: // tty->print_cr("START OopTaskQueue::oops_do"); ysr@976: uint iters = size(); ysr@976: uint index = _bottom; ysr@976: for (uint i = 0; i < iters; ++i) { ysr@777: index = decrement_index(index); ysr@777: // tty->print_cr(" doing entry %d," INTPTR_T " -> " INTPTR_T, ysr@777: // index, &_elems[index], _elems[index]); ysr@777: E* t = (E*)&_elems[index]; // cast away volatility ysr@777: oop* p = (oop*)t; ysr@777: assert((*t)->is_oop_or_null(), "Not an oop or null"); ysr@777: f->do_oop(p); ysr@777: } ysr@777: // tty->print_cr("END OopTaskQueue::oops_do"); ysr@777: } ysr@777: ysr@777: ysr@777: template ysr@976: bool GenericTaskQueue::push_slow(E t, uint dirty_n_elems) { duke@435: if (dirty_n_elems == n() - 1) { duke@435: // Actually means 0, so do the push. ysr@976: uint localBot = _bottom; duke@435: _elems[localBot] = t; duke@435: _bottom = increment_index(localBot); duke@435: return true; duke@435: } else duke@435: return false; duke@435: } duke@435: duke@435: template duke@435: bool GenericTaskQueue:: ysr@976: pop_local_slow(uint localBot, Age oldAge) { duke@435: // This queue was observed to contain exactly one element; either this duke@435: // thread will claim it, or a competing "pop_global". In either case, duke@435: // the queue will be logically empty afterwards. Create a new Age value duke@435: // that represents the empty queue for the given value of "_bottom". (We duke@435: // must also increment "tag" because of the case where "bottom == 1", duke@435: // "top == 0". A pop_global could read the queue element in that case, duke@435: // then have the owner thread do a pop followed by another push. Without duke@435: // the incrementing of "tag", the pop_global's CAS could succeed, duke@435: // allowing it to believe it has claimed the stale element.) duke@435: Age newAge; duke@435: newAge._top = localBot; duke@435: newAge._tag = oldAge.tag() + 1; duke@435: // Perhaps a competing pop_global has already incremented "top", in which duke@435: // case it wins the element. duke@435: if (localBot == oldAge.top()) { duke@435: Age tempAge; duke@435: // No competing pop_global has yet incremented "top"; we'll try to duke@435: // install new_age, thus claiming the element. ysr@976: assert(sizeof(Age) == sizeof(int), "Assumption about CAS unit."); ysr@976: *(uint*)&tempAge = Atomic::cmpxchg(*(uint*)&newAge, (volatile uint*)&_age, *(uint*)&oldAge); duke@435: if (tempAge == oldAge) { duke@435: // We win. duke@435: assert(dirty_size(localBot, get_top()) != n() - 1, duke@435: "Shouldn't be possible..."); duke@435: return true; duke@435: } duke@435: } duke@435: // We fail; a completing pop_global gets the element. But the queue is duke@435: // empty (and top is greater than bottom.) Fix this representation of duke@435: // the empty queue to become the canonical one. duke@435: set_age(newAge); duke@435: assert(dirty_size(localBot, get_top()) != n() - 1, duke@435: "Shouldn't be possible..."); duke@435: return false; duke@435: } duke@435: duke@435: template duke@435: bool GenericTaskQueue::pop_global(E& t) { duke@435: Age newAge; duke@435: Age oldAge = get_age(); ysr@976: uint localBot = _bottom; ysr@976: uint n_elems = size(localBot, oldAge.top()); duke@435: if (n_elems == 0) { duke@435: return false; duke@435: } duke@435: t = _elems[oldAge.top()]; duke@435: newAge = oldAge; duke@435: newAge._top = increment_index(newAge.top()); duke@435: if ( newAge._top == 0 ) newAge._tag++; duke@435: Age resAge; ysr@976: *(uint*)&resAge = Atomic::cmpxchg(*(uint*)&newAge, (volatile uint*)&_age, *(uint*)&oldAge); duke@435: // Note that using "_bottom" here might fail, since a pop_local might duke@435: // have decremented it. duke@435: assert(dirty_size(localBot, newAge._top) != n() - 1, duke@435: "Shouldn't be possible..."); duke@435: return (resAge == oldAge); duke@435: } duke@435: duke@435: template duke@435: GenericTaskQueue::~GenericTaskQueue() { duke@435: FREE_C_HEAP_ARRAY(E, _elems); duke@435: } duke@435: duke@435: // Inherits the typedef of "Task" from above. duke@435: class TaskQueueSetSuper: public CHeapObj { duke@435: protected: duke@435: static int randomParkAndMiller(int* seed0); duke@435: public: duke@435: // Returns "true" if some TaskQueue in the set contains a task. duke@435: virtual bool peek() = 0; duke@435: }; duke@435: duke@435: template class GenericTaskQueueSet: public TaskQueueSetSuper { duke@435: private: ysr@976: uint _n; duke@435: GenericTaskQueue** _queues; duke@435: duke@435: public: duke@435: GenericTaskQueueSet(int n) : _n(n) { duke@435: typedef GenericTaskQueue* GenericTaskQueuePtr; duke@435: _queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n); duke@435: guarantee(_queues != NULL, "Allocation failure."); duke@435: for (int i = 0; i < n; i++) { duke@435: _queues[i] = NULL; duke@435: } duke@435: } duke@435: ysr@976: bool steal_1_random(uint queue_num, int* seed, E& t); ysr@976: bool steal_best_of_2(uint queue_num, int* seed, E& t); ysr@976: bool steal_best_of_all(uint queue_num, int* seed, E& t); duke@435: ysr@976: void register_queue(uint i, GenericTaskQueue* q); duke@435: ysr@976: GenericTaskQueue* queue(uint n); duke@435: duke@435: // The thread with queue number "queue_num" (and whose random number seed duke@435: // is at "seed") is trying to steal a task from some other queue. (It duke@435: // may try several queues, according to some configuration parameter.) duke@435: // If some steal succeeds, returns "true" and sets "t" the stolen task, duke@435: // otherwise returns false. ysr@976: bool steal(uint queue_num, int* seed, E& t); duke@435: duke@435: bool peek(); duke@435: }; duke@435: duke@435: template ysr@976: void GenericTaskQueueSet::register_queue(uint i, GenericTaskQueue* q) { ysr@976: assert(i < _n, "index out of range."); duke@435: _queues[i] = q; duke@435: } duke@435: duke@435: template ysr@976: GenericTaskQueue* GenericTaskQueueSet::queue(uint i) { duke@435: return _queues[i]; duke@435: } duke@435: duke@435: template ysr@976: bool GenericTaskQueueSet::steal(uint queue_num, int* seed, E& t) { ysr@976: for (uint i = 0; i < 2 * _n; i++) duke@435: if (steal_best_of_2(queue_num, seed, t)) duke@435: return true; duke@435: return false; duke@435: } duke@435: duke@435: template ysr@976: bool GenericTaskQueueSet::steal_best_of_all(uint queue_num, int* seed, E& t) { duke@435: if (_n > 2) { duke@435: int best_k; ysr@976: uint best_sz = 0; ysr@976: for (uint k = 0; k < _n; k++) { duke@435: if (k == queue_num) continue; ysr@976: uint sz = _queues[k]->size(); duke@435: if (sz > best_sz) { duke@435: best_sz = sz; duke@435: best_k = k; duke@435: } duke@435: } duke@435: return best_sz > 0 && _queues[best_k]->pop_global(t); duke@435: } else if (_n == 2) { duke@435: // Just try the other one. duke@435: int k = (queue_num + 1) % 2; duke@435: return _queues[k]->pop_global(t); duke@435: } else { duke@435: assert(_n == 1, "can't be zero."); duke@435: return false; duke@435: } duke@435: } duke@435: duke@435: template ysr@976: bool GenericTaskQueueSet::steal_1_random(uint queue_num, int* seed, E& t) { duke@435: if (_n > 2) { ysr@976: uint k = queue_num; duke@435: while (k == queue_num) k = randomParkAndMiller(seed) % _n; duke@435: return _queues[2]->pop_global(t); duke@435: } else if (_n == 2) { duke@435: // Just try the other one. duke@435: int k = (queue_num + 1) % 2; duke@435: return _queues[k]->pop_global(t); duke@435: } else { duke@435: assert(_n == 1, "can't be zero."); duke@435: return false; duke@435: } duke@435: } duke@435: duke@435: template ysr@976: bool GenericTaskQueueSet::steal_best_of_2(uint queue_num, int* seed, E& t) { duke@435: if (_n > 2) { ysr@976: uint k1 = queue_num; duke@435: while (k1 == queue_num) k1 = randomParkAndMiller(seed) % _n; ysr@976: uint k2 = queue_num; duke@435: while (k2 == queue_num || k2 == k1) k2 = randomParkAndMiller(seed) % _n; duke@435: // Sample both and try the larger. ysr@976: uint sz1 = _queues[k1]->size(); ysr@976: uint sz2 = _queues[k2]->size(); duke@435: if (sz2 > sz1) return _queues[k2]->pop_global(t); duke@435: else return _queues[k1]->pop_global(t); duke@435: } else if (_n == 2) { duke@435: // Just try the other one. ysr@976: uint k = (queue_num + 1) % 2; duke@435: return _queues[k]->pop_global(t); duke@435: } else { duke@435: assert(_n == 1, "can't be zero."); duke@435: return false; duke@435: } duke@435: } duke@435: duke@435: template duke@435: bool GenericTaskQueueSet::peek() { duke@435: // Try all the queues. ysr@976: for (uint j = 0; j < _n; j++) { duke@435: if (_queues[j]->peek()) duke@435: return true; duke@435: } duke@435: return false; duke@435: } duke@435: ysr@777: // When to terminate from the termination protocol. ysr@777: class TerminatorTerminator: public CHeapObj { ysr@777: public: ysr@777: virtual bool should_exit_termination() = 0; ysr@777: }; ysr@777: duke@435: // A class to aid in the termination of a set of parallel tasks using duke@435: // TaskQueueSet's for work stealing. duke@435: jmasa@981: #undef TRACESPINNING jmasa@981: duke@435: class ParallelTaskTerminator: public StackObj { duke@435: private: duke@435: int _n_threads; duke@435: TaskQueueSetSuper* _queue_set; ysr@976: int _offered_termination; duke@435: jmasa@981: #ifdef TRACESPINNING jmasa@981: static uint _total_yields; jmasa@981: static uint _total_spins; jmasa@981: static uint _total_peeks; jmasa@981: #endif jmasa@981: duke@435: bool peek_in_queue_set(); duke@435: protected: duke@435: virtual void yield(); duke@435: void sleep(uint millis); duke@435: duke@435: public: duke@435: duke@435: // "n_threads" is the number of threads to be terminated. "queue_set" is a duke@435: // queue sets of work queues of other threads. duke@435: ParallelTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set); duke@435: duke@435: // The current thread has no work, and is ready to terminate if everyone duke@435: // else is. If returns "true", all threads are terminated. If returns duke@435: // "false", available work has been observed in one of the task queues, duke@435: // so the global task is not complete. ysr@777: bool offer_termination() { ysr@777: return offer_termination(NULL); ysr@777: } ysr@777: ysr@777: // As above, but it also terminates of the should_exit_termination() ysr@777: // method of the terminator parameter returns true. If terminator is ysr@777: // NULL, then it is ignored. ysr@777: bool offer_termination(TerminatorTerminator* terminator); duke@435: duke@435: // Reset the terminator, so that it may be reused again. duke@435: // The caller is responsible for ensuring that this is done duke@435: // in an MT-safe manner, once the previous round of use of duke@435: // the terminator is finished. duke@435: void reset_for_reuse(); duke@435: jmasa@981: #ifdef TRACESPINNING jmasa@981: static uint total_yields() { return _total_yields; } jmasa@981: static uint total_spins() { return _total_spins; } jmasa@981: static uint total_peeks() { return _total_peeks; } jmasa@981: static void print_termination_counts(); jmasa@981: #endif duke@435: }; duke@435: duke@435: #define SIMPLE_STACK 0 duke@435: duke@435: template inline bool GenericTaskQueue::push(E t) { duke@435: #if SIMPLE_STACK ysr@976: uint localBot = _bottom; duke@435: if (_bottom < max_elems()) { duke@435: _elems[localBot] = t; duke@435: _bottom = localBot + 1; duke@435: return true; duke@435: } else { duke@435: return false; duke@435: } duke@435: #else ysr@976: uint localBot = _bottom; duke@435: assert((localBot >= 0) && (localBot < n()), "_bottom out of range."); ysr@976: TAG_TYPE top = get_top(); ysr@976: uint dirty_n_elems = dirty_size(localBot, top); duke@435: assert((dirty_n_elems >= 0) && (dirty_n_elems < n()), duke@435: "n_elems out of range."); duke@435: if (dirty_n_elems < max_elems()) { duke@435: _elems[localBot] = t; duke@435: _bottom = increment_index(localBot); duke@435: return true; duke@435: } else { duke@435: return push_slow(t, dirty_n_elems); duke@435: } duke@435: #endif duke@435: } duke@435: duke@435: template inline bool GenericTaskQueue::pop_local(E& t) { duke@435: #if SIMPLE_STACK ysr@976: uint localBot = _bottom; duke@435: assert(localBot > 0, "precondition."); duke@435: localBot--; duke@435: t = _elems[localBot]; duke@435: _bottom = localBot; duke@435: return true; duke@435: #else ysr@976: uint localBot = _bottom; duke@435: // This value cannot be n-1. That can only occur as a result of duke@435: // the assignment to bottom in this method. If it does, this method duke@435: // resets the size( to 0 before the next call (which is sequential, duke@435: // since this is pop_local.) ysr@976: uint dirty_n_elems = dirty_size(localBot, get_top()); duke@435: assert(dirty_n_elems != n() - 1, "Shouldn't be possible..."); duke@435: if (dirty_n_elems == 0) return false; duke@435: localBot = decrement_index(localBot); duke@435: _bottom = localBot; duke@435: // This is necessary to prevent any read below from being reordered duke@435: // before the store just above. duke@435: OrderAccess::fence(); duke@435: t = _elems[localBot]; duke@435: // This is a second read of "age"; the "size()" above is the first. duke@435: // If there's still at least one element in the queue, based on the duke@435: // "_bottom" and "age" we've read, then there can be no interference with duke@435: // a "pop_global" operation, and we're done. ysr@976: TAG_TYPE tp = get_top(); // XXX duke@435: if (size(localBot, tp) > 0) { duke@435: assert(dirty_size(localBot, tp) != n() - 1, duke@435: "Shouldn't be possible..."); duke@435: return true; duke@435: } else { duke@435: // Otherwise, the queue contained exactly one element; we take the slow duke@435: // path. duke@435: return pop_local_slow(localBot, get_age()); duke@435: } duke@435: #endif duke@435: } duke@435: duke@435: typedef oop Task; duke@435: typedef GenericTaskQueue OopTaskQueue; duke@435: typedef GenericTaskQueueSet OopTaskQueueSet; duke@435: coleenp@548: coleenp@548: #define COMPRESSED_OOP_MASK 1 coleenp@548: coleenp@548: // This is a container class for either an oop* or a narrowOop*. coleenp@548: // Both are pushed onto a task queue and the consumer will test is_narrow() coleenp@548: // to determine which should be processed. coleenp@548: class StarTask { coleenp@548: void* _holder; // either union oop* or narrowOop* coleenp@548: public: ysr@1280: StarTask(narrowOop* p) { ysr@1280: assert(((uintptr_t)p & COMPRESSED_OOP_MASK) == 0, "Information loss!"); ysr@1280: _holder = (void *)((uintptr_t)p | COMPRESSED_OOP_MASK); ysr@1280: } ysr@1280: StarTask(oop* p) { ysr@1280: assert(((uintptr_t)p & COMPRESSED_OOP_MASK) == 0, "Information loss!"); ysr@1280: _holder = (void*)p; ysr@1280: } coleenp@548: StarTask() { _holder = NULL; } coleenp@548: operator oop*() { return (oop*)_holder; } coleenp@548: operator narrowOop*() { coleenp@548: return (narrowOop*)((uintptr_t)_holder & ~COMPRESSED_OOP_MASK); coleenp@548: } coleenp@548: coleenp@548: // Operators to preserve const/volatile in assignments required by gcc coleenp@548: void operator=(const volatile StarTask& t) volatile { _holder = t._holder; } coleenp@548: coleenp@548: bool is_narrow() const { coleenp@548: return (((uintptr_t)_holder & COMPRESSED_OOP_MASK) != 0); coleenp@548: } coleenp@548: }; coleenp@548: duke@435: typedef GenericTaskQueue OopStarTaskQueue; duke@435: typedef GenericTaskQueueSet OopStarTaskQueueSet; duke@435: jcoomes@810: typedef size_t RegionTask; // index for region jcoomes@810: typedef GenericTaskQueue RegionTaskQueue; jcoomes@810: typedef GenericTaskQueueSet RegionTaskQueueSet; duke@435: jcoomes@810: class RegionTaskQueueWithOverflow: public CHeapObj { duke@435: protected: jcoomes@810: RegionTaskQueue _region_queue; jcoomes@810: GrowableArray* _overflow_stack; duke@435: duke@435: public: jcoomes@810: RegionTaskQueueWithOverflow() : _overflow_stack(NULL) {} duke@435: // Initialize both stealable queue and overflow duke@435: void initialize(); duke@435: // Save first to stealable queue and then to overflow jcoomes@810: void save(RegionTask t); duke@435: // Retrieve first from overflow and then from stealable queue jcoomes@810: bool retrieve(RegionTask& region_index); duke@435: // Retrieve from stealable queue jcoomes@810: bool retrieve_from_stealable_queue(RegionTask& region_index); duke@435: // Retrieve from overflow jcoomes@810: bool retrieve_from_overflow(RegionTask& region_index); duke@435: bool is_empty(); duke@435: bool stealable_is_empty(); duke@435: bool overflow_is_empty(); ysr@976: uint stealable_size() { return _region_queue.size(); } jcoomes@810: RegionTaskQueue* task_queue() { return &_region_queue; } duke@435: }; duke@435: jcoomes@810: #define USE_RegionTaskQueueWithOverflow