taskqueue.hpp 26.7 KB
Newer Older
D
duke 已提交
1
/*
2
 * Copyright (c) 2001, 2013, 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
 *
 */

25 26 27 28 29 30
#ifndef SHARE_VM_UTILITIES_TASKQUEUE_HPP
#define SHARE_VM_UTILITIES_TASKQUEUE_HPP

#include "memory/allocation.hpp"
#include "memory/allocation.inline.hpp"
#include "runtime/mutex.hpp"
31
#include "runtime/orderAccess.inline.hpp"
32 33
#include "utilities/stack.hpp"

J
jcoomes 已提交
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 62 63 64 65 66 67 68 69 70
// 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);

71 72
  TaskQueueStats & operator +=(const TaskQueueStats & addend);

J
jcoomes 已提交
73 74 75 76 77
  inline size_t get(StatId id) const { return _stats[id]; }
  inline const size_t* get() const   { return _stats; }

  inline void reset();

78
  // Print the specified line of the header (does not include a line separator).
J
jcoomes 已提交
79 80
  static void print_header(unsigned int line, outputStream* const stream = tty,
                           unsigned int width = 10);
81
  // Print the statistics (does not include a line separator).
J
jcoomes 已提交
82 83
  void print(outputStream* const stream = tty, unsigned int width = 10) const;

84 85
  DEBUG_ONLY(void verify() const;)

J
jcoomes 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105
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

106 107
// TaskQueueSuper collects functionality common to all GenericTaskQueue instances.

Z
zgu 已提交
108 109
template <unsigned int N, MEMFLAGS F>
class TaskQueueSuper: public CHeapObj<F> {
D
duke 已提交
110
protected:
111 112 113 114
  // 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).
115
  volatile uint _bottom;
D
duke 已提交
116

117
  enum { MOD_N_MASK = N - 1 };
D
duke 已提交
118

119 120 121 122 123
  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 已提交
124

125 126
    Age   get()        const volatile { return _data; }
    void  set(Age age) volatile       { _data = age._data; }
D
duke 已提交
127

128 129
    idx_t top()        const volatile { return _fields._top; }
    idx_t tag()        const volatile { return _fields._tag; }
D
duke 已提交
130

131 132 133 134 135
    // Increment top; if it wraps, increment tag also.
    void increment() {
      _fields._top = increment_index(_fields._top);
      if (_fields._top == 0) ++_fields._tag;
    }
D
duke 已提交
136

137 138 139 140
    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 已提交
141
    }
142 143 144 145 146 147 148 149 150 151 152 153

    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 已提交
154 155
  };

156
  volatile Age _age;
D
duke 已提交
157

158 159 160
  // These both operate mod N.
  static uint increment_index(uint ind) {
    return (ind + 1) & MOD_N_MASK;
D
duke 已提交
161
  }
162 163
  static uint decrement_index(uint ind) {
    return (ind - 1) & MOD_N_MASK;
D
duke 已提交
164 165
  }

166 167
  // Returns a number in the range [0..N).  If the result is "N-1", it should be
  // interpreted as 0.
168
  uint dirty_size(uint bot, uint top) const {
169
    return (bot - top) & MOD_N_MASK;
D
duke 已提交
170 171 172
  }

  // Returns the size corresponding to the given "bot" and "top".
173
  uint size(uint bot, uint top) const {
174
    uint sz = dirty_size(bot, top);
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
    // 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 已提交
190 191 192 193 194
  }

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

195 196 197
  // 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 已提交
198 199 200 201

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

206
  uint dirty_size() const {
207
    return dirty_size(_bottom, _age.top());
D
duke 已提交
208 209
  }

210 211
  void set_empty() {
    _bottom = 0;
212
    _age.set(0);
213 214
  }

D
duke 已提交
215 216
  // Maximum number of elements allowed in the queue.  This is two less
  // than the actual queue size, for somewhat complicated reasons.
217
  uint max_elems() const { return N - 2; }
218 219 220

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

  TASKQUEUE_STATS_ONLY(TaskQueueStats stats;)
D
duke 已提交
223 224
};

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
//
// GenericTaskQueue implements an ABP, Aurora-Blumofe-Plaxton, double-
// ended-queue (deque), intended for use in work stealing. Queue operations
// are non-blocking.
//
// A queue owner thread performs push() and pop_local() operations on one end
// of the queue, while other threads may steal work using the pop_global()
// method.
//
// The main difference to the original algorithm is that this
// implementation allows wrap-around at the end of its allocated
// storage, which is an array.
//
// The original paper is:
//
// Arora, N. S., Blumofe, R. D., and Plaxton, C. G.
// Thread scheduling for multiprogrammed multiprocessors.
// Theory of Computing Systems 34, 2 (2001), 115-144.
//
// The following paper provides an correctness proof and an
// implementation for weakly ordered memory models including (pseudo-)
// code containing memory barriers for a Chase-Lev deque. Chase-Lev is
// similar to ABP, with the main difference that it allows resizing of the
// underlying storage:
//
// Le, N. M., Pop, A., Cohen A., and Nardell, F. Z.
// Correct and efficient work-stealing for weak memory models
// Proceedings of the 18th ACM SIGPLAN symposium on Principles and
// practice of parallel programming (PPoPP 2013), 69-80
//
Z
zgu 已提交
255 256 257

template <class E, MEMFLAGS F, unsigned int N = TASKQUEUE_SIZE>
class GenericTaskQueue: public TaskQueueSuper<N, F> {
258
  ArrayAllocator<E, F> _array_allocator;
259
protected:
Z
zgu 已提交
260 261
  typedef typename TaskQueueSuper<N, F>::Age Age;
  typedef typename TaskQueueSuper<N, F>::idx_t idx_t;
262

Z
zgu 已提交
263 264 265 266 267
  using TaskQueueSuper<N, F>::_bottom;
  using TaskQueueSuper<N, F>::_age;
  using TaskQueueSuper<N, F>::increment_index;
  using TaskQueueSuper<N, F>::decrement_index;
  using TaskQueueSuper<N, F>::dirty_size;
268 269

public:
Z
zgu 已提交
270 271 272 273 274 275
  using TaskQueueSuper<N, F>::max_elems;
  using TaskQueueSuper<N, F>::size;

#if  TASKQUEUE_STATS
  using TaskQueueSuper<N, F>::stats;
#endif
276

D
duke 已提交
277 278
private:
  // Slow paths for push, pop_local.  (pop_global has no fast path.)
279 280
  bool push_slow(E t, uint dirty_n_elems);
  bool pop_local_slow(uint localBot, Age oldAge);
D
duke 已提交
281 282

public:
283 284
  typedef E element_type;

D
duke 已提交
285 286 287 288 289
  // Initializes the queue to empty.
  GenericTaskQueue();

  void initialize();

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

293 294 295
  // 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).
296
  inline bool pop_local(volatile E& t);
D
duke 已提交
297

298 299
  // Like pop_local(), but uses the "global" end of the queue (the least
  // recently pushed).
300
  bool pop_global(volatile E& t);
D
duke 已提交
301 302 303 304

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

305 306 307
  // apply the closure to all elements in the task queue
  void oops_do(OopClosure* f);

D
duke 已提交
308 309 310 311 312
private:
  // Element array.
  volatile E* _elems;
};

Z
zgu 已提交
313 314
template<class E, MEMFLAGS F, unsigned int N>
GenericTaskQueue<E, F, N>::GenericTaskQueue() {
315
  assert(sizeof(Age) == sizeof(size_t), "Depends on this.");
D
duke 已提交
316 317
}

Z
zgu 已提交
318 319
template<class E, MEMFLAGS F, unsigned int N>
void GenericTaskQueue<E, F, N>::initialize() {
320
  _elems = _array_allocator.allocate(N);
D
duke 已提交
321 322
}

Z
zgu 已提交
323 324
template<class E, MEMFLAGS F, unsigned int N>
void GenericTaskQueue<E, F, N>::oops_do(OopClosure* f) {
325
  // tty->print_cr("START OopTaskQueue::oops_do");
326 327 328
  uint iters = size();
  uint index = _bottom;
  for (uint i = 0; i < iters; ++i) {
329 330 331 332 333 334 335 336 337 338 339
    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");
}

Z
zgu 已提交
340 341
template<class E, MEMFLAGS F, unsigned int N>
bool GenericTaskQueue<E, F, N>::push_slow(E t, uint dirty_n_elems) {
342
  if (dirty_n_elems == N - 1) {
D
duke 已提交
343
    // Actually means 0, so do the push.
344
    uint localBot = _bottom;
345 346 347 348 349 350
    // g++ complains if the volatile result of the assignment is
    // unused, so we cast the volatile away.  We cannot cast directly
    // to void, because gcc treats that as not using the result of the
    // assignment.  However, casting to E& means that we trigger an
    // unused-value warning.  So, we cast the E& to void.
    (void)const_cast<E&>(_elems[localBot] = t);
351
    OrderAccess::release_store(&_bottom, increment_index(localBot));
J
jcoomes 已提交
352
    TASKQUEUE_STATS_ONLY(stats.record_push());
D
duke 已提交
353
    return true;
354 355
  }
  return false;
D
duke 已提交
356 357
}

358 359 360 361 362 363
// 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()).
Z
zgu 已提交
364 365
template<class E, MEMFLAGS F, unsigned int N>
bool GenericTaskQueue<E, F, N>::pop_local_slow(uint localBot, Age oldAge) {
D
duke 已提交
366 367 368 369 370 371 372 373 374
  // 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.)
375
  Age newAge((idx_t)localBot, oldAge.tag() + 1);
D
duke 已提交
376 377 378 379 380
  // 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.
381
    Age tempAge = _age.cmpxchg(newAge, oldAge);
D
duke 已提交
382 383
    if (tempAge == oldAge) {
      // We win.
384
      assert(dirty_size(localBot, _age.top()) != N - 1, "sanity");
J
jcoomes 已提交
385
      TASKQUEUE_STATS_ONLY(stats.record_pop_slow());
D
duke 已提交
386 387 388
      return true;
    }
  }
389 390 391 392 393
  // 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 已提交
394 395 396
  return false;
}

Z
zgu 已提交
397
template<class E, MEMFLAGS F, unsigned int N>
398
bool GenericTaskQueue<E, F, N>::pop_global(volatile E& t) {
399
  Age oldAge = _age.get();
400 401 402 403 404 405 406
  // Architectures with weak memory model require a barrier here
  // to guarantee that bottom is not older than age,
  // which is crucial for the correctness of the algorithm.
#if !(defined SPARC || defined IA32 || defined AMD64)
  OrderAccess::fence();
#endif
  uint localBot = OrderAccess::load_acquire((volatile juint*)&_bottom);
407
  uint n_elems = size(localBot, oldAge.top());
D
duke 已提交
408 409 410
  if (n_elems == 0) {
    return false;
  }
411

412 413 414 415 416 417
  // g++ complains if the volatile result of the assignment is
  // unused, so we cast the volatile away.  We cannot cast directly
  // to void, because gcc treats that as not using the result of the
  // assignment.  However, casting to E& means that we trigger an
  // unused-value warning.  So, we cast the E& to void.
  (void) const_cast<E&>(t = _elems[oldAge.top()]);
418 419 420 421
  Age newAge(oldAge);
  newAge.increment();
  Age resAge = _age.cmpxchg(newAge, oldAge);

D
duke 已提交
422 423
  // Note that using "_bottom" here might fail, since a pop_local might
  // have decremented it.
424 425
  assert(dirty_size(localBot, newAge.top()) != N - 1, "sanity");
  return resAge == oldAge;
D
duke 已提交
426 427
}

Z
zgu 已提交
428 429 430
template<class E, MEMFLAGS F, unsigned int N>
GenericTaskQueue<E, F, N>::~GenericTaskQueue() {
  FREE_C_HEAP_ARRAY(E, _elems, F);
D
duke 已提交
431 432
}

433 434 435
// OverflowTaskQueue is a TaskQueue that also includes an overflow stack for
// elements that do not fit in the TaskQueue.
//
436
// This class hides two methods from super classes:
437 438 439 440
//
// 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
//
441
// Note that size() is not hidden--it returns the number of elements in the
442 443
// TaskQueue, and does not include the size of the overflow stack.  This
// simplifies replacement of GenericTaskQueues with OverflowTaskQueues.
Z
zgu 已提交
444 445
template<class E, MEMFLAGS F, unsigned int N = TASKQUEUE_SIZE>
class OverflowTaskQueue: public GenericTaskQueue<E, F, N>
446 447
{
public:
Z
zgu 已提交
448 449
  typedef Stack<E, F>               overflow_t;
  typedef GenericTaskQueue<E, F, N> taskqueue_t;
450

J
jcoomes 已提交
451 452
  TASKQUEUE_STATS_ONLY(using taskqueue_t::stats;)

453 454 455 456 457 458
  // 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);

459 460
  inline overflow_t* overflow_stack() { return &_overflow_stack; }

461
  inline bool taskqueue_empty() const { return taskqueue_t::is_empty(); }
462
  inline bool overflow_empty()  const { return _overflow_stack.is_empty(); }
463 464 465 466 467
  inline bool is_empty()        const {
    return taskqueue_empty() && overflow_empty();
  }

private:
468
  overflow_t _overflow_stack;
469 470
};

Z
zgu 已提交
471 472
template <class E, MEMFLAGS F, unsigned int N>
bool OverflowTaskQueue<E, F, N>::push(E t)
473 474 475
{
  if (!taskqueue_t::push(t)) {
    overflow_stack()->push(t);
476
    TASKQUEUE_STATS_ONLY(stats.record_overflow(overflow_stack()->size()));
477 478 479 480
  }
  return true;
}

Z
zgu 已提交
481 482
template <class E, MEMFLAGS F, unsigned int N>
bool OverflowTaskQueue<E, F, N>::pop_overflow(E& t)
483 484 485 486 487 488
{
  if (overflow_empty()) return false;
  t = overflow_stack()->pop();
  return true;
}

Z
zgu 已提交
489
class TaskQueueSetSuper {
D
duke 已提交
490 491 492 493 494 495 496
protected:
  static int randomParkAndMiller(int* seed0);
public:
  // Returns "true" if some TaskQueue in the set contains a task.
  virtual bool peek() = 0;
};

Z
zgu 已提交
497 498 499 500 501
template <MEMFLAGS F> class TaskQueueSetSuperImpl: public CHeapObj<F>, public TaskQueueSetSuper {
};

template<class T, MEMFLAGS F>
class GenericTaskQueueSet: public TaskQueueSetSuperImpl<F> {
D
duke 已提交
502
private:
503
  uint _n;
504
  T** _queues;
D
duke 已提交
505 506

public:
507 508
  typedef typename T::element_type E;

D
duke 已提交
509
  GenericTaskQueueSet(int n) : _n(n) {
510
    typedef T* GenericTaskQueuePtr;
Z
zgu 已提交
511
    _queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n, F);
D
duke 已提交
512 513 514 515 516
    for (int i = 0; i < n; i++) {
      _queues[i] = NULL;
    }
  }

517
  bool steal_best_of_2(uint queue_num, int* seed, E& t);
D
duke 已提交
518

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

521
  T* queue(uint n);
D
duke 已提交
522

523 524 525 526 527
  // 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.
528
  bool steal(uint queue_num, int* seed, E& t);
D
duke 已提交
529 530 531 532

  bool peek();
};

Z
zgu 已提交
533 534
template<class T, MEMFLAGS F> void
GenericTaskQueueSet<T, F>::register_queue(uint i, T* q) {
535
  assert(i < _n, "index out of range.");
D
duke 已提交
536 537 538
  _queues[i] = q;
}

Z
zgu 已提交
539 540
template<class T, MEMFLAGS F> T*
GenericTaskQueueSet<T, F>::queue(uint i) {
D
duke 已提交
541 542 543
  return _queues[i];
}

Z
zgu 已提交
544 545
template<class T, MEMFLAGS F> bool
GenericTaskQueueSet<T, F>::steal(uint queue_num, int* seed, E& t) {
J
jcoomes 已提交
546 547 548
  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 已提交
549
      return true;
J
jcoomes 已提交
550 551 552
    }
  }
  TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(false));
D
duke 已提交
553 554 555
  return false;
}

Z
zgu 已提交
556 557
template<class T, MEMFLAGS F> bool
GenericTaskQueueSet<T, F>::steal_best_of_2(uint queue_num, int* seed, E& t) {
D
duke 已提交
558
  if (_n > 2) {
559
    uint k1 = queue_num;
Z
zgu 已提交
560
    while (k1 == queue_num) k1 = TaskQueueSetSuper::randomParkAndMiller(seed) % _n;
561
    uint k2 = queue_num;
Z
zgu 已提交
562
    while (k2 == queue_num || k2 == k1) k2 = TaskQueueSetSuper::randomParkAndMiller(seed) % _n;
D
duke 已提交
563
    // Sample both and try the larger.
564 565
    uint sz1 = _queues[k1]->size();
    uint sz2 = _queues[k2]->size();
D
duke 已提交
566 567 568 569
    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.
570
    uint k = (queue_num + 1) % 2;
D
duke 已提交
571 572 573 574 575 576 577
    return _queues[k]->pop_global(t);
  } else {
    assert(_n == 1, "can't be zero.");
    return false;
  }
}

Z
zgu 已提交
578 579
template<class T, MEMFLAGS F>
bool GenericTaskQueueSet<T, F>::peek() {
D
duke 已提交
580
  // Try all the queues.
581
  for (uint j = 0; j < _n; j++) {
D
duke 已提交
582 583 584 585 586 587
    if (_queues[j]->peek())
      return true;
  }
  return false;
}

588
// When to terminate from the termination protocol.
Z
zgu 已提交
589
class TerminatorTerminator: public CHeapObj<mtInternal> {
590 591 592 593
public:
  virtual bool should_exit_termination() = 0;
};

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

597 598
#undef TRACESPINNING

D
duke 已提交
599 600 601 602
class ParallelTaskTerminator: public StackObj {
private:
  int _n_threads;
  TaskQueueSetSuper* _queue_set;
603
  int _offered_termination;
D
duke 已提交
604

605 606 607 608 609 610
#ifdef TRACESPINNING
  static uint _total_yields;
  static uint _total_spins;
  static uint _total_peeks;
#endif

D
duke 已提交
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625
  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.
626 627 628 629
  bool offer_termination() {
    return offer_termination(NULL);
  }

630
  // As above, but it also terminates if the should_exit_termination()
631 632 633
  // method of the terminator parameter returns true. If terminator is
  // NULL, then it is ignored.
  bool offer_termination(TerminatorTerminator* terminator);
D
duke 已提交
634 635 636 637 638 639

  // 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();
640 641 642
  // Same as above but the number of parallel threads is set to the
  // given number.
  void reset_for_reuse(int n_threads);
D
duke 已提交
643

644 645 646 647 648 649
#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 已提交
650 651
};

Z
zgu 已提交
652 653
template<class E, MEMFLAGS F, unsigned int N> inline bool
GenericTaskQueue<E, F, N>::push(E t) {
654
  uint localBot = _bottom;
655
  assert(localBot < N, "_bottom out of range.");
656
  idx_t top = _age.top();
657
  uint dirty_n_elems = dirty_size(localBot, top);
658
  assert(dirty_n_elems < N, "n_elems out of range.");
D
duke 已提交
659
  if (dirty_n_elems < max_elems()) {
660 661 662 663 664 665
    // g++ complains if the volatile result of the assignment is
    // unused, so we cast the volatile away.  We cannot cast directly
    // to void, because gcc treats that as not using the result of the
    // assignment.  However, casting to E& means that we trigger an
    // unused-value warning.  So, we cast the E& to void.
    (void) const_cast<E&>(_elems[localBot] = t);
666
    OrderAccess::release_store(&_bottom, increment_index(localBot));
J
jcoomes 已提交
667
    TASKQUEUE_STATS_ONLY(stats.record_push());
D
duke 已提交
668 669 670 671 672 673
    return true;
  } else {
    return push_slow(t, dirty_n_elems);
  }
}

Z
zgu 已提交
674
template<class E, MEMFLAGS F, unsigned int N> inline bool
675
GenericTaskQueue<E, F, N>::pop_local(volatile E& t) {
676
  uint localBot = _bottom;
677
  // This value cannot be N-1.  That can only occur as a result of
D
duke 已提交
678
  // the assignment to bottom in this method.  If it does, this method
679
  // resets the size to 0 before the next call (which is sequential,
D
duke 已提交
680
  // since this is pop_local.)
681 682
  uint dirty_n_elems = dirty_size(localBot, _age.top());
  assert(dirty_n_elems != N - 1, "Shouldn't be possible...");
D
duke 已提交
683 684 685 686 687 688
  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();
689 690 691 692 693 694
  // g++ complains if the volatile result of the assignment is
  // unused, so we cast the volatile away.  We cannot cast directly
  // to void, because gcc treats that as not using the result of the
  // assignment.  However, casting to E& means that we trigger an
  // unused-value warning.  So, we cast the E& to void.
  (void) const_cast<E&>(t = _elems[localBot]);
D
duke 已提交
695 696 697 698
  // 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.
699
  idx_t tp = _age.top();    // XXX
D
duke 已提交
700
  if (size(localBot, tp) > 0) {
701
    assert(dirty_size(localBot, tp) != N - 1, "sanity");
J
jcoomes 已提交
702
    TASKQUEUE_STATS_ONLY(stats.record_pop());
D
duke 已提交
703 704 705 706
    return true;
  } else {
    // Otherwise, the queue contained exactly one element; we take the slow
    // path.
707
    return pop_local_slow(localBot, _age.get());
D
duke 已提交
708 709 710
  }
}

Z
zgu 已提交
711 712
typedef GenericTaskQueue<oop, mtGC>             OopTaskQueue;
typedef GenericTaskQueueSet<OopTaskQueue, mtGC> OopTaskQueueSet;
D
duke 已提交
713

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

// 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*
725 726 727

  enum { COMPRESSED_OOP_MASK = 1 };

728
 public:
729 730 731 732 733 734 735 736
  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;
  }
737 738 739 740 741 742
  StarTask()             { _holder = NULL; }
  operator oop*()        { return (oop*)_holder; }
  operator narrowOop*()  {
    return (narrowOop*)((uintptr_t)_holder & ~COMPRESSED_OOP_MASK);
  }

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

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

757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772
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 {
773
    (void)const_cast<oop&>(_obj = t._obj);
774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791
    _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

Z
zgu 已提交
792 793
typedef OverflowTaskQueue<StarTask, mtClass>           OopStarTaskQueue;
typedef GenericTaskQueueSet<OopStarTaskQueue, mtClass> OopStarTaskQueueSet;
D
duke 已提交
794

Z
zgu 已提交
795 796
typedef OverflowTaskQueue<size_t, mtInternal>             RegionTaskQueue;
typedef GenericTaskQueueSet<RegionTaskQueue, mtClass>     RegionTaskQueueSet;
797

798 799

#endif // SHARE_VM_UTILITIES_TASKQUEUE_HPP