taskqueue.hpp 24.9 KB
Newer Older
D
duke 已提交
1
/*
2
 * Copyright (c) 2001, 2010, Oracle and/or its affiliates. All rights reserved.
D
duke 已提交
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
19 20 21
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
D
duke 已提交
22 23 24
 *
 */

J
jcoomes 已提交
25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
// Simple TaskQueue stats that are collected by default in debug builds.

#if !defined(TASKQUEUE_STATS) && defined(ASSERT)
#define TASKQUEUE_STATS 1
#elif !defined(TASKQUEUE_STATS)
#define TASKQUEUE_STATS 0
#endif

#if TASKQUEUE_STATS
#define TASKQUEUE_STATS_ONLY(code) code
#else
#define TASKQUEUE_STATS_ONLY(code)
#endif // TASKQUEUE_STATS

#if TASKQUEUE_STATS
class TaskQueueStats {
public:
  enum StatId {
    push,             // number of taskqueue pushes
    pop,              // number of taskqueue pops
    pop_slow,         // subset of taskqueue pops that were done slow-path
    steal_attempt,    // number of taskqueue steal attempts
    steal,            // number of taskqueue steals
    overflow,         // number of overflow pushes
    overflow_max_len, // max length of overflow stack
    last_stat_id
  };

public:
  inline TaskQueueStats()       { reset(); }

  inline void record_push()     { ++_stats[push]; }
  inline void record_pop()      { ++_stats[pop]; }
  inline void record_pop_slow() { record_pop(); ++_stats[pop_slow]; }
  inline void record_steal(bool success);
  inline void record_overflow(size_t new_length);

62 63
  TaskQueueStats & operator +=(const TaskQueueStats & addend);

J
jcoomes 已提交
64 65 66 67 68
  inline size_t get(StatId id) const { return _stats[id]; }
  inline const size_t* get() const   { return _stats; }

  inline void reset();

69
  // Print the specified line of the header (does not include a line separator).
J
jcoomes 已提交
70 71
  static void print_header(unsigned int line, outputStream* const stream = tty,
                           unsigned int width = 10);
72
  // Print the statistics (does not include a line separator).
J
jcoomes 已提交
73 74
  void print(outputStream* const stream = tty, unsigned int width = 10) const;

75 76
  DEBUG_ONLY(void verify() const;)

J
jcoomes 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
private:
  size_t                    _stats[last_stat_id];
  static const char * const _names[last_stat_id];
};

void TaskQueueStats::record_steal(bool success) {
  ++_stats[steal_attempt];
  if (success) ++_stats[steal];
}

void TaskQueueStats::record_overflow(size_t new_len) {
  ++_stats[overflow];
  if (new_len > _stats[overflow_max_len]) _stats[overflow_max_len] = new_len;
}

void TaskQueueStats::reset() {
  memset(_stats, 0, sizeof(_stats));
}
#endif // TASKQUEUE_STATS

97
template <unsigned int N>
D
duke 已提交
98 99
class TaskQueueSuper: public CHeapObj {
protected:
100 101 102 103
  // Internal type for indexing the queue; also used for the tag.
  typedef NOT_LP64(uint16_t) LP64_ONLY(uint32_t) idx_t;

  // The first free element after the last one pushed (mod N).
104
  volatile uint _bottom;
D
duke 已提交
105

106
  enum { MOD_N_MASK = N - 1 };
D
duke 已提交
107

108 109 110 111 112
  class Age {
  public:
    Age(size_t data = 0)         { _data = data; }
    Age(const Age& age)          { _data = age._data; }
    Age(idx_t top, idx_t tag)    { _fields._top = top; _fields._tag = tag; }
D
duke 已提交
113

114 115
    Age   get()        const volatile { return _data; }
    void  set(Age age) volatile       { _data = age._data; }
D
duke 已提交
116

117 118
    idx_t top()        const volatile { return _fields._top; }
    idx_t tag()        const volatile { return _fields._tag; }
D
duke 已提交
119

120 121 122 123 124
    // Increment top; if it wraps, increment tag also.
    void increment() {
      _fields._top = increment_index(_fields._top);
      if (_fields._top == 0) ++_fields._tag;
    }
D
duke 已提交
125

126 127 128 129
    Age cmpxchg(const Age new_age, const Age old_age) volatile {
      return (size_t) Atomic::cmpxchg_ptr((intptr_t)new_age._data,
                                          (volatile intptr_t *)&_data,
                                          (intptr_t)old_age._data);
D
duke 已提交
130
    }
131 132 133 134 135 136 137 138 139 140 141 142

    bool operator ==(const Age& other) const { return _data == other._data; }

  private:
    struct fields {
      idx_t _top;
      idx_t _tag;
    };
    union {
      size_t _data;
      fields _fields;
    };
D
duke 已提交
143 144
  };

145
  volatile Age _age;
D
duke 已提交
146

147 148 149
  // These both operate mod N.
  static uint increment_index(uint ind) {
    return (ind + 1) & MOD_N_MASK;
D
duke 已提交
150
  }
151 152
  static uint decrement_index(uint ind) {
    return (ind - 1) & MOD_N_MASK;
D
duke 已提交
153 154
  }

155 156
  // Returns a number in the range [0..N).  If the result is "N-1", it should be
  // interpreted as 0.
157
  uint dirty_size(uint bot, uint top) const {
158
    return (bot - top) & MOD_N_MASK;
D
duke 已提交
159 160 161
  }

  // Returns the size corresponding to the given "bot" and "top".
162
  uint size(uint bot, uint top) const {
163
    uint sz = dirty_size(bot, top);
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178
    // Has the queue "wrapped", so that bottom is less than top?  There's a
    // complicated special case here.  A pair of threads could perform pop_local
    // and pop_global operations concurrently, starting from a state in which
    // _bottom == _top+1.  The pop_local could succeed in decrementing _bottom,
    // and the pop_global in incrementing _top (in which case the pop_global
    // will be awarded the contested queue element.)  The resulting state must
    // be interpreted as an empty queue.  (We only need to worry about one such
    // event: only the queue owner performs pop_local's, and several concurrent
    // threads attempting to perform the pop_global will all perform the same
    // CAS, and only one can succeed.)  Any stealing thread that reads after
    // either the increment or decrement will see an empty queue, and will not
    // join the competitors.  The "sz == -1 || sz == N-1" state will not be
    // modified by concurrent queues, so the owner thread can reset the state to
    // _bottom == top so subsequent pushes will be performed normally.
    return (sz == N - 1) ? 0 : sz;
D
duke 已提交
179 180 181 182 183
  }

public:
  TaskQueueSuper() : _bottom(0), _age() {}

184 185 186
  // Return true if the TaskQueue contains/does not contain any tasks.
  bool peek()     const { return _bottom != _age.top(); }
  bool is_empty() const { return size() == 0; }
D
duke 已提交
187 188 189 190

  // Return an estimate of the number of elements in the queue.
  // The "careful" version admits the possibility of pop_local/pop_global
  // races.
191
  uint size() const {
192
    return size(_bottom, _age.top());
D
duke 已提交
193 194
  }

195
  uint dirty_size() const {
196
    return dirty_size(_bottom, _age.top());
D
duke 已提交
197 198
  }

199 200
  void set_empty() {
    _bottom = 0;
201
    _age.set(0);
202 203
  }

D
duke 已提交
204 205
  // Maximum number of elements allowed in the queue.  This is two less
  // than the actual queue size, for somewhat complicated reasons.
206
  uint max_elems() const { return N - 2; }
207 208 209

  // Total size of queue.
  static const uint total_size() { return N; }
J
jcoomes 已提交
210 211

  TASKQUEUE_STATS_ONLY(TaskQueueStats stats;)
D
duke 已提交
212 213
};

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228
template<class E, unsigned int N = TASKQUEUE_SIZE>
class GenericTaskQueue: public TaskQueueSuper<N> {
protected:
  typedef typename TaskQueueSuper<N>::Age Age;
  typedef typename TaskQueueSuper<N>::idx_t idx_t;

  using TaskQueueSuper<N>::_bottom;
  using TaskQueueSuper<N>::_age;
  using TaskQueueSuper<N>::increment_index;
  using TaskQueueSuper<N>::decrement_index;
  using TaskQueueSuper<N>::dirty_size;

public:
  using TaskQueueSuper<N>::max_elems;
  using TaskQueueSuper<N>::size;
J
jcoomes 已提交
229
  TASKQUEUE_STATS_ONLY(using TaskQueueSuper<N>::stats;)
230

D
duke 已提交
231 232
private:
  // Slow paths for push, pop_local.  (pop_global has no fast path.)
233 234
  bool push_slow(E t, uint dirty_n_elems);
  bool pop_local_slow(uint localBot, Age oldAge);
D
duke 已提交
235 236

public:
237 238
  typedef E element_type;

D
duke 已提交
239 240 241 242 243
  // Initializes the queue to empty.
  GenericTaskQueue();

  void initialize();

244
  // Push the task "t" on the queue.  Returns "false" iff the queue is full.
D
duke 已提交
245 246
  inline bool push(E t);

247 248 249
  // Attempts to claim a task from the "local" end of the queue (the most
  // recently pushed).  If successful, returns true and sets t to the task;
  // otherwise, returns false (the queue is empty).
D
duke 已提交
250 251
  inline bool pop_local(E& t);

252 253
  // Like pop_local(), but uses the "global" end of the queue (the least
  // recently pushed).
D
duke 已提交
254 255 256 257 258
  bool pop_global(E& t);

  // Delete any resource associated with the queue.
  ~GenericTaskQueue();

259 260 261
  // apply the closure to all elements in the task queue
  void oops_do(OopClosure* f);

D
duke 已提交
262 263 264 265 266
private:
  // Element array.
  volatile E* _elems;
};

267 268
template<class E, unsigned int N>
GenericTaskQueue<E, N>::GenericTaskQueue() {
269
  assert(sizeof(Age) == sizeof(size_t), "Depends on this.");
D
duke 已提交
270 271
}

272 273
template<class E, unsigned int N>
void GenericTaskQueue<E, N>::initialize() {
274
  _elems = NEW_C_HEAP_ARRAY(E, N);
D
duke 已提交
275 276
}

277 278
template<class E, unsigned int N>
void GenericTaskQueue<E, N>::oops_do(OopClosure* f) {
279
  // tty->print_cr("START OopTaskQueue::oops_do");
280 281 282
  uint iters = size();
  uint index = _bottom;
  for (uint i = 0; i < iters; ++i) {
283 284 285 286 287 288 289 290 291 292 293
    index = decrement_index(index);
    // tty->print_cr("  doing entry %d," INTPTR_T " -> " INTPTR_T,
    //            index, &_elems[index], _elems[index]);
    E* t = (E*)&_elems[index];      // cast away volatility
    oop* p = (oop*)t;
    assert((*t)->is_oop_or_null(), "Not an oop or null");
    f->do_oop(p);
  }
  // tty->print_cr("END OopTaskQueue::oops_do");
}

294 295
template<class E, unsigned int N>
bool GenericTaskQueue<E, N>::push_slow(E t, uint dirty_n_elems) {
296
  if (dirty_n_elems == N - 1) {
D
duke 已提交
297
    // Actually means 0, so do the push.
298
    uint localBot = _bottom;
299 300
    // g++ complains if the volatile result of the assignment is unused.
    const_cast<E&>(_elems[localBot] = t);
301
    OrderAccess::release_store(&_bottom, increment_index(localBot));
J
jcoomes 已提交
302
    TASKQUEUE_STATS_ONLY(stats.record_push());
D
duke 已提交
303
    return true;
304 305
  }
  return false;
D
duke 已提交
306 307
}

308 309 310 311 312 313
// pop_local_slow() is done by the owning thread and is trying to
// get the last task in the queue.  It will compete with pop_global()
// that will be used by other threads.  The tag age is incremented
// whenever the queue goes empty which it will do here if this thread
// gets the last task or in pop_global() if the queue wraps (top == 0
// and pop_global() succeeds, see pop_global()).
314
template<class E, unsigned int N>
J
jcoomes 已提交
315
bool GenericTaskQueue<E, N>::pop_local_slow(uint localBot, Age oldAge) {
D
duke 已提交
316 317 318 319 320 321 322 323 324
  // This queue was observed to contain exactly one element; either this
  // thread will claim it, or a competing "pop_global".  In either case,
  // the queue will be logically empty afterwards.  Create a new Age value
  // that represents the empty queue for the given value of "_bottom".  (We
  // must also increment "tag" because of the case where "bottom == 1",
  // "top == 0".  A pop_global could read the queue element in that case,
  // then have the owner thread do a pop followed by another push.  Without
  // the incrementing of "tag", the pop_global's CAS could succeed,
  // allowing it to believe it has claimed the stale element.)
325
  Age newAge((idx_t)localBot, oldAge.tag() + 1);
D
duke 已提交
326 327 328 329 330
  // Perhaps a competing pop_global has already incremented "top", in which
  // case it wins the element.
  if (localBot == oldAge.top()) {
    // No competing pop_global has yet incremented "top"; we'll try to
    // install new_age, thus claiming the element.
331
    Age tempAge = _age.cmpxchg(newAge, oldAge);
D
duke 已提交
332 333
    if (tempAge == oldAge) {
      // We win.
334
      assert(dirty_size(localBot, _age.top()) != N - 1, "sanity");
J
jcoomes 已提交
335
      TASKQUEUE_STATS_ONLY(stats.record_pop_slow());
D
duke 已提交
336 337 338
      return true;
    }
  }
339 340 341 342 343
  // We lose; a completing pop_global gets the element.  But the queue is empty
  // and top is greater than bottom.  Fix this representation of the empty queue
  // to become the canonical one.
  _age.set(newAge);
  assert(dirty_size(localBot, _age.top()) != N - 1, "sanity");
D
duke 已提交
344 345 346
  return false;
}

347 348
template<class E, unsigned int N>
bool GenericTaskQueue<E, N>::pop_global(E& t) {
349
  Age oldAge = _age.get();
350 351
  uint localBot = _bottom;
  uint n_elems = size(localBot, oldAge.top());
D
duke 已提交
352 353 354
  if (n_elems == 0) {
    return false;
  }
355

356
  const_cast<E&>(t = _elems[oldAge.top()]);
357 358 359 360
  Age newAge(oldAge);
  newAge.increment();
  Age resAge = _age.cmpxchg(newAge, oldAge);

D
duke 已提交
361 362
  // Note that using "_bottom" here might fail, since a pop_local might
  // have decremented it.
363 364
  assert(dirty_size(localBot, newAge.top()) != N - 1, "sanity");
  return resAge == oldAge;
D
duke 已提交
365 366
}

367 368
template<class E, unsigned int N>
GenericTaskQueue<E, N>::~GenericTaskQueue() {
D
duke 已提交
369 370 371
  FREE_C_HEAP_ARRAY(E, _elems);
}

372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390
// OverflowTaskQueue is a TaskQueue that also includes an overflow stack for
// elements that do not fit in the TaskQueue.
//
// Three methods from super classes are overridden:
//
// initialize() - initialize the super classes and create the overflow stack
// push() - push onto the task queue or, if that fails, onto the overflow stack
// is_empty() - return true if both the TaskQueue and overflow stack are empty
//
// Note that size() is not overridden--it returns the number of elements in the
// TaskQueue, and does not include the size of the overflow stack.  This
// simplifies replacement of GenericTaskQueues with OverflowTaskQueues.
template<class E, unsigned int N = TASKQUEUE_SIZE>
class OverflowTaskQueue: public GenericTaskQueue<E, N>
{
public:
  typedef GrowableArray<E>       overflow_t;
  typedef GenericTaskQueue<E, N> taskqueue_t;

J
jcoomes 已提交
391 392
  TASKQUEUE_STATS_ONLY(using taskqueue_t::stats;)

393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442
  OverflowTaskQueue();
  ~OverflowTaskQueue();
  void initialize();

  inline overflow_t* overflow_stack() const { return _overflow_stack; }

  // Push task t onto the queue or onto the overflow stack.  Return true.
  inline bool push(E t);

  // Attempt to pop from the overflow stack; return true if anything was popped.
  inline bool pop_overflow(E& t);

  inline bool taskqueue_empty() const { return taskqueue_t::is_empty(); }
  inline bool overflow_empty()  const { return overflow_stack()->is_empty(); }
  inline bool is_empty()        const {
    return taskqueue_empty() && overflow_empty();
  }

private:
  overflow_t* _overflow_stack;
};

template <class E, unsigned int N>
OverflowTaskQueue<E, N>::OverflowTaskQueue()
{
  _overflow_stack = NULL;
}

template <class E, unsigned int N>
OverflowTaskQueue<E, N>::~OverflowTaskQueue()
{
  if (_overflow_stack != NULL) {
    delete _overflow_stack;
    _overflow_stack = NULL;
  }
}

template <class E, unsigned int N>
void OverflowTaskQueue<E, N>::initialize()
{
  taskqueue_t::initialize();
  assert(_overflow_stack == NULL, "memory leak");
  _overflow_stack = new (ResourceObj::C_HEAP) GrowableArray<E>(10, true);
}

template <class E, unsigned int N>
bool OverflowTaskQueue<E, N>::push(E t)
{
  if (!taskqueue_t::push(t)) {
    overflow_stack()->push(t);
J
jcoomes 已提交
443
    TASKQUEUE_STATS_ONLY(stats.record_overflow(overflow_stack()->length()));
444 445 446 447 448 449 450 451 452 453 454 455
  }
  return true;
}

template <class E, unsigned int N>
bool OverflowTaskQueue<E, N>::pop_overflow(E& t)
{
  if (overflow_empty()) return false;
  t = overflow_stack()->pop();
  return true;
}

D
duke 已提交
456 457 458 459 460 461 462 463
class TaskQueueSetSuper: public CHeapObj {
protected:
  static int randomParkAndMiller(int* seed0);
public:
  // Returns "true" if some TaskQueue in the set contains a task.
  virtual bool peek() = 0;
};

464 465
template<class T>
class GenericTaskQueueSet: public TaskQueueSetSuper {
D
duke 已提交
466
private:
467
  uint _n;
468
  T** _queues;
D
duke 已提交
469 470

public:
471 472
  typedef typename T::element_type E;

D
duke 已提交
473
  GenericTaskQueueSet(int n) : _n(n) {
474
    typedef T* GenericTaskQueuePtr;
D
duke 已提交
475 476 477 478 479 480
    _queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n);
    for (int i = 0; i < n; i++) {
      _queues[i] = NULL;
    }
  }

481 482 483
  bool steal_1_random(uint queue_num, int* seed, E& t);
  bool steal_best_of_2(uint queue_num, int* seed, E& t);
  bool steal_best_of_all(uint queue_num, int* seed, E& t);
D
duke 已提交
484

485
  void register_queue(uint i, T* q);
D
duke 已提交
486

487
  T* queue(uint n);
D
duke 已提交
488

489 490 491 492 493
  // The thread with queue number "queue_num" (and whose random number seed is
  // at "seed") is trying to steal a task from some other queue.  (It may try
  // several queues, according to some configuration parameter.)  If some steal
  // succeeds, returns "true" and sets "t" to the stolen task, otherwise returns
  // false.
494
  bool steal(uint queue_num, int* seed, E& t);
D
duke 已提交
495 496 497 498

  bool peek();
};

499 500
template<class T> void
GenericTaskQueueSet<T>::register_queue(uint i, T* q) {
501
  assert(i < _n, "index out of range.");
D
duke 已提交
502 503 504
  _queues[i] = q;
}

505 506
template<class T> T*
GenericTaskQueueSet<T>::queue(uint i) {
D
duke 已提交
507 508 509
  return _queues[i];
}

510 511
template<class T> bool
GenericTaskQueueSet<T>::steal(uint queue_num, int* seed, E& t) {
J
jcoomes 已提交
512 513 514
  for (uint i = 0; i < 2 * _n; i++) {
    if (steal_best_of_2(queue_num, seed, t)) {
      TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(true));
D
duke 已提交
515
      return true;
J
jcoomes 已提交
516 517 518
    }
  }
  TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(false));
D
duke 已提交
519 520 521
  return false;
}

522 523
template<class T> bool
GenericTaskQueueSet<T>::steal_best_of_all(uint queue_num, int* seed, E& t) {
D
duke 已提交
524 525
  if (_n > 2) {
    int best_k;
526 527
    uint best_sz = 0;
    for (uint k = 0; k < _n; k++) {
D
duke 已提交
528
      if (k == queue_num) continue;
529
      uint sz = _queues[k]->size();
D
duke 已提交
530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
      if (sz > best_sz) {
        best_sz = sz;
        best_k = k;
      }
    }
    return best_sz > 0 && _queues[best_k]->pop_global(t);
  } else if (_n == 2) {
    // Just try the other one.
    int k = (queue_num + 1) % 2;
    return _queues[k]->pop_global(t);
  } else {
    assert(_n == 1, "can't be zero.");
    return false;
  }
}

546 547
template<class T> bool
GenericTaskQueueSet<T>::steal_1_random(uint queue_num, int* seed, E& t) {
D
duke 已提交
548
  if (_n > 2) {
549
    uint k = queue_num;
D
duke 已提交
550 551 552 553 554 555 556 557 558 559 560 561
    while (k == queue_num) k = randomParkAndMiller(seed) % _n;
    return _queues[2]->pop_global(t);
  } else if (_n == 2) {
    // Just try the other one.
    int k = (queue_num + 1) % 2;
    return _queues[k]->pop_global(t);
  } else {
    assert(_n == 1, "can't be zero.");
    return false;
  }
}

562 563
template<class T> bool
GenericTaskQueueSet<T>::steal_best_of_2(uint queue_num, int* seed, E& t) {
D
duke 已提交
564
  if (_n > 2) {
565
    uint k1 = queue_num;
D
duke 已提交
566
    while (k1 == queue_num) k1 = randomParkAndMiller(seed) % _n;
567
    uint k2 = queue_num;
D
duke 已提交
568 569
    while (k2 == queue_num || k2 == k1) k2 = randomParkAndMiller(seed) % _n;
    // Sample both and try the larger.
570 571
    uint sz1 = _queues[k1]->size();
    uint sz2 = _queues[k2]->size();
D
duke 已提交
572 573 574 575
    if (sz2 > sz1) return _queues[k2]->pop_global(t);
    else return _queues[k1]->pop_global(t);
  } else if (_n == 2) {
    // Just try the other one.
576
    uint k = (queue_num + 1) % 2;
D
duke 已提交
577 578 579 580 581 582 583
    return _queues[k]->pop_global(t);
  } else {
    assert(_n == 1, "can't be zero.");
    return false;
  }
}

584 585
template<class T>
bool GenericTaskQueueSet<T>::peek() {
D
duke 已提交
586
  // Try all the queues.
587
  for (uint j = 0; j < _n; j++) {
D
duke 已提交
588 589 590 591 592 593
    if (_queues[j]->peek())
      return true;
  }
  return false;
}

594 595 596 597 598 599
// When to terminate from the termination protocol.
class TerminatorTerminator: public CHeapObj {
public:
  virtual bool should_exit_termination() = 0;
};

D
duke 已提交
600 601 602
// A class to aid in the termination of a set of parallel tasks using
// TaskQueueSet's for work stealing.

603 604
#undef TRACESPINNING

D
duke 已提交
605 606 607 608
class ParallelTaskTerminator: public StackObj {
private:
  int _n_threads;
  TaskQueueSetSuper* _queue_set;
609
  int _offered_termination;
D
duke 已提交
610

611 612 613 614 615 616
#ifdef TRACESPINNING
  static uint _total_yields;
  static uint _total_spins;
  static uint _total_peeks;
#endif

D
duke 已提交
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
  bool peek_in_queue_set();
protected:
  virtual void yield();
  void sleep(uint millis);

public:

  // "n_threads" is the number of threads to be terminated.  "queue_set" is a
  // queue sets of work queues of other threads.
  ParallelTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set);

  // The current thread has no work, and is ready to terminate if everyone
  // else is.  If returns "true", all threads are terminated.  If returns
  // "false", available work has been observed in one of the task queues,
  // so the global task is not complete.
632 633 634 635
  bool offer_termination() {
    return offer_termination(NULL);
  }

636
  // As above, but it also terminates if the should_exit_termination()
637 638 639
  // method of the terminator parameter returns true. If terminator is
  // NULL, then it is ignored.
  bool offer_termination(TerminatorTerminator* terminator);
D
duke 已提交
640 641 642 643 644 645

  // Reset the terminator, so that it may be reused again.
  // The caller is responsible for ensuring that this is done
  // in an MT-safe manner, once the previous round of use of
  // the terminator is finished.
  void reset_for_reuse();
646 647 648
  // Same as above but the number of parallel threads is set to the
  // given number.
  void reset_for_reuse(int n_threads);
D
duke 已提交
649

650 651 652 653 654 655
#ifdef TRACESPINNING
  static uint total_yields() { return _total_yields; }
  static uint total_spins() { return _total_spins; }
  static uint total_peeks() { return _total_peeks; }
  static void print_termination_counts();
#endif
D
duke 已提交
656 657
};

658 659
template<class E, unsigned int N> inline bool
GenericTaskQueue<E, N>::push(E t) {
660
  uint localBot = _bottom;
661 662
  assert((localBot >= 0) && (localBot < N), "_bottom out of range.");
  idx_t top = _age.top();
663
  uint dirty_n_elems = dirty_size(localBot, top);
664
  assert(dirty_n_elems < N, "n_elems out of range.");
D
duke 已提交
665
  if (dirty_n_elems < max_elems()) {
666 667
    // g++ complains if the volatile result of the assignment is unused.
    const_cast<E&>(_elems[localBot] = t);
668
    OrderAccess::release_store(&_bottom, increment_index(localBot));
J
jcoomes 已提交
669
    TASKQUEUE_STATS_ONLY(stats.record_push());
D
duke 已提交
670 671 672 673 674 675
    return true;
  } else {
    return push_slow(t, dirty_n_elems);
  }
}

676 677
template<class E, unsigned int N> inline bool
GenericTaskQueue<E, N>::pop_local(E& t) {
678
  uint localBot = _bottom;
679
  // This value cannot be N-1.  That can only occur as a result of
D
duke 已提交
680
  // the assignment to bottom in this method.  If it does, this method
681
  // resets the size to 0 before the next call (which is sequential,
D
duke 已提交
682
  // since this is pop_local.)
683 684
  uint dirty_n_elems = dirty_size(localBot, _age.top());
  assert(dirty_n_elems != N - 1, "Shouldn't be possible...");
D
duke 已提交
685 686 687 688 689 690
  if (dirty_n_elems == 0) return false;
  localBot = decrement_index(localBot);
  _bottom = localBot;
  // This is necessary to prevent any read below from being reordered
  // before the store just above.
  OrderAccess::fence();
691
  const_cast<E&>(t = _elems[localBot]);
D
duke 已提交
692 693 694 695
  // This is a second read of "age"; the "size()" above is the first.
  // If there's still at least one element in the queue, based on the
  // "_bottom" and "age" we've read, then there can be no interference with
  // a "pop_global" operation, and we're done.
696
  idx_t tp = _age.top();    // XXX
D
duke 已提交
697
  if (size(localBot, tp) > 0) {
698
    assert(dirty_size(localBot, tp) != N - 1, "sanity");
J
jcoomes 已提交
699
    TASKQUEUE_STATS_ONLY(stats.record_pop());
D
duke 已提交
700 701 702 703
    return true;
  } else {
    // Otherwise, the queue contained exactly one element; we take the slow
    // path.
704
    return pop_local_slow(localBot, _age.get());
D
duke 已提交
705 706 707
  }
}

708
typedef GenericTaskQueue<oop>             OopTaskQueue;
709
typedef GenericTaskQueueSet<OopTaskQueue> OopTaskQueueSet;
D
duke 已提交
710

711 712 713 714 715
#ifdef _MSC_VER
#pragma warning(push)
// warning C4522: multiple assignment operators specified
#pragma warning(disable:4522)
#endif
716 717 718 719 720 721

// This is a container class for either an oop* or a narrowOop*.
// Both are pushed onto a task queue and the consumer will test is_narrow()
// to determine which should be processed.
class StarTask {
  void*  _holder;        // either union oop* or narrowOop*
722 723 724

  enum { COMPRESSED_OOP_MASK = 1 };

725
 public:
726 727 728 729 730 731 732 733
  StarTask(narrowOop* p) {
    assert(((uintptr_t)p & COMPRESSED_OOP_MASK) == 0, "Information loss!");
    _holder = (void *)((uintptr_t)p | COMPRESSED_OOP_MASK);
  }
  StarTask(oop* p)       {
    assert(((uintptr_t)p & COMPRESSED_OOP_MASK) == 0, "Information loss!");
    _holder = (void*)p;
  }
734 735 736 737 738 739
  StarTask()             { _holder = NULL; }
  operator oop*()        { return (oop*)_holder; }
  operator narrowOop*()  {
    return (narrowOop*)((uintptr_t)_holder & ~COMPRESSED_OOP_MASK);
  }

740 741 742 743 744 745 746 747
  StarTask& operator=(const StarTask& t) {
    _holder = t._holder;
    return *this;
  }
  volatile StarTask& operator=(const volatile StarTask& t) volatile {
    _holder = t._holder;
    return *this;
  }
748 749 750 751 752 753

  bool is_narrow() const {
    return (((uintptr_t)_holder & COMPRESSED_OOP_MASK) != 0);
  }
};

754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788
class ObjArrayTask
{
public:
  ObjArrayTask(oop o = NULL, int idx = 0): _obj(o), _index(idx) { }
  ObjArrayTask(oop o, size_t idx): _obj(o), _index(int(idx)) {
    assert(idx <= size_t(max_jint), "too big");
  }
  ObjArrayTask(const ObjArrayTask& t): _obj(t._obj), _index(t._index) { }

  ObjArrayTask& operator =(const ObjArrayTask& t) {
    _obj = t._obj;
    _index = t._index;
    return *this;
  }
  volatile ObjArrayTask&
  operator =(const volatile ObjArrayTask& t) volatile {
    _obj = t._obj;
    _index = t._index;
    return *this;
  }

  inline oop obj()   const { return _obj; }
  inline int index() const { return _index; }

  DEBUG_ONLY(bool is_valid() const); // Tasks to be pushed/popped must be valid.

private:
  oop _obj;
  int _index;
};

#ifdef _MSC_VER
#pragma warning(pop)
#endif

789
typedef OverflowTaskQueue<StarTask>           OopStarTaskQueue;
790
typedef GenericTaskQueueSet<OopStarTaskQueue> OopStarTaskQueueSet;
D
duke 已提交
791

792 793
typedef OverflowTaskQueue<size_t>             RegionTaskQueue;
typedef GenericTaskQueueSet<RegionTaskQueue>  RegionTaskQueueSet;
794