[GC] For weak memory models, more memory barriers are needed for the GC lock-free algorithms.

Tue, 04 Jul 2017 09:57:19 +0800

author
fujie
date
Tue, 04 Jul 2017 09:57:19 +0800
changeset 415
8d48e8755036
parent 414
c5f826fdfc22
child 416
eda7ffa4af8b

[GC] For weak memory models, more memory barriers are needed for the GC lock-free algorithms.

src/share/vm/utilities/taskqueue.hpp file | annotate | diff | comparison | revisions
     1.1 --- a/src/share/vm/utilities/taskqueue.hpp	Tue Jun 20 14:59:09 2017 +0800
     1.2 +++ b/src/share/vm/utilities/taskqueue.hpp	Tue Jul 04 09:57:19 2017 +0800
     1.3 @@ -143,9 +143,23 @@
     1.4    // Internal type for indexing the queue; also used for the tag.
     1.5    typedef NOT_LP64(uint16_t) LP64_ONLY(uint32_t) idx_t;
     1.6  
     1.7 +#ifdef MIPS64
     1.8 +private:
     1.9 +#endif
    1.10    // The first free element after the last one pushed (mod N).
    1.11    volatile uint _bottom;
    1.12  
    1.13 +#ifdef MIPS64
    1.14 +protected:
    1.15 +  inline uint get_bottom() const {
    1.16 +    return OrderAccess::load_acquire((volatile juint*)&_bottom);
    1.17 +  }
    1.18 +
    1.19 +  inline void set_bottom(uint new_bottom) {
    1.20 +    OrderAccess::release_store(&_bottom, new_bottom);
    1.21 +  }
    1.22 +#endif
    1.23 +
    1.24    enum { MOD_N_MASK = N - 1 };
    1.25  
    1.26    class Age {
    1.27 @@ -154,11 +168,21 @@
    1.28      Age(const Age& age)          { _data = age._data; }
    1.29      Age(idx_t top, idx_t tag)    { _fields._top = top; _fields._tag = tag; }
    1.30  
    1.31 +#ifndef MIPS64
    1.32      Age   get()        const volatile { return _data; }
    1.33      void  set(Age age) volatile       { _data = age._data; }
    1.34 -
    1.35      idx_t top()        const volatile { return _fields._top; }
    1.36      idx_t tag()        const volatile { return _fields._tag; }
    1.37 +#else
    1.38 +    Age   get()        const volatile { 
    1.39 +      size_t res = OrderAccess::load_ptr_acquire((volatile intptr_t*) &_data);
    1.40 +      return *(Age*)(&res);
    1.41 +    }
    1.42 +
    1.43 +    void  set(Age age) volatile       { OrderAccess::release_store_ptr((volatile intptr_t*) &_data, *(size_t*)(&age._data)); }
    1.44 +    idx_t top()        const volatile { return OrderAccess::load_acquire((volatile idx_t*) &(_fields._top)); }
    1.45 +    idx_t tag()        const volatile { return OrderAccess::load_acquire((volatile idx_t*) &(_fields._tag)); }
    1.46 +#endif
    1.47  
    1.48      // Increment top; if it wraps, increment tag also.
    1.49      void increment() {
    1.50 @@ -225,22 +249,41 @@
    1.51    TaskQueueSuper() : _bottom(0), _age() {}
    1.52  
    1.53    // Return true if the TaskQueue contains/does not contain any tasks.
    1.54 -  bool peek()     const { return _bottom != _age.top(); }
    1.55 +  bool peek()     const { 
    1.56 +#ifdef MIPS64
    1.57 +    return get_bottom() != _age.top(); 
    1.58 +#else
    1.59 +    return _bottom != _age.top(); 
    1.60 +#endif
    1.61 +  }
    1.62 +
    1.63    bool is_empty() const { return size() == 0; }
    1.64  
    1.65    // Return an estimate of the number of elements in the queue.
    1.66    // The "careful" version admits the possibility of pop_local/pop_global
    1.67    // races.
    1.68    uint size() const {
    1.69 +#ifdef MIPS64
    1.70 +    return size(get_bottom(), _age.top());
    1.71 +#else
    1.72      return size(_bottom, _age.top());
    1.73 +#endif
    1.74    }
    1.75  
    1.76    uint dirty_size() const {
    1.77 +#ifdef MIPS64
    1.78 +    return dirty_size(get_bottom(), _age.top());
    1.79 +#else
    1.80      return dirty_size(_bottom, _age.top());
    1.81 +#endif
    1.82    }
    1.83  
    1.84    void set_empty() {
    1.85 +#ifdef MIPS64
    1.86 +    set_bottom(0);
    1.87 +#else
    1.88      _bottom = 0;
    1.89 +#endif
    1.90      _age.set(0);
    1.91    }
    1.92  
    1.93 @@ -292,7 +335,9 @@
    1.94    typedef typename TaskQueueSuper<N, F>::Age Age;
    1.95    typedef typename TaskQueueSuper<N, F>::idx_t idx_t;
    1.96  
    1.97 +#ifndef MIPS64
    1.98    using TaskQueueSuper<N, F>::_bottom;
    1.99 +#endif
   1.100    using TaskQueueSuper<N, F>::_age;
   1.101    using TaskQueueSuper<N, F>::increment_index;
   1.102    using TaskQueueSuper<N, F>::decrement_index;
   1.103 @@ -356,7 +401,11 @@
   1.104  void GenericTaskQueue<E, F, N>::oops_do(OopClosure* f) {
   1.105    // tty->print_cr("START OopTaskQueue::oops_do");
   1.106    uint iters = size();
   1.107 +#ifdef MIPS64
   1.108 +  uint index = this->get_bottom();
   1.109 +#else
   1.110    uint index = _bottom;
   1.111 +#endif
   1.112    for (uint i = 0; i < iters; ++i) {
   1.113      index = decrement_index(index);
   1.114      // tty->print_cr("  doing entry %d," INTPTR_T " -> " INTPTR_T,
   1.115 @@ -373,18 +422,23 @@
   1.116  bool GenericTaskQueue<E, F, N>::push_slow(E t, uint dirty_n_elems) {
   1.117    if (dirty_n_elems == N - 1) {
   1.118      // Actually means 0, so do the push.
   1.119 +#ifdef MIPS64
   1.120 +    uint localBot = this->get_bottom();
   1.121 +#else
   1.122      uint localBot = _bottom;
   1.123 +#endif
   1.124      // g++ complains if the volatile result of the assignment is
   1.125      // unused, so we cast the volatile away.  We cannot cast directly
   1.126      // to void, because gcc treats that as not using the result of the
   1.127      // assignment.  However, casting to E& means that we trigger an
   1.128      // unused-value warning.  So, we cast the E& to void.
   1.129      (void)const_cast<E&>(_elems[localBot] = t);
   1.130 +#ifdef MIPS64
   1.131 +    this->set_bottom(increment_index(localBot));
   1.132 +#else
   1.133      OrderAccess::release_store(&_bottom, increment_index(localBot));
   1.134 +#endif
   1.135      TASKQUEUE_STATS_ONLY(stats.record_push());
   1.136 -#ifdef MIPS64
   1.137 -    if (Use3A2000) OrderAccess::fence();
   1.138 -#endif
   1.139      return true;
   1.140    }
   1.141    return false;
   1.142 @@ -438,7 +492,11 @@
   1.143  #if !(defined SPARC || defined IA32 || defined AMD64)
   1.144    OrderAccess::fence();
   1.145  #endif
   1.146 +#ifdef MIPS64
   1.147 +  uint localBot = this->get_bottom();
   1.148 +#else
   1.149    uint localBot = OrderAccess::load_acquire((volatile juint*)&_bottom);
   1.150 +#endif
   1.151    uint n_elems = size(localBot, oldAge.top());
   1.152    if (n_elems == 0) {
   1.153      return false;
   1.154 @@ -509,9 +567,6 @@
   1.155    if (!taskqueue_t::push(t)) {
   1.156      overflow_stack()->push(t);
   1.157      TASKQUEUE_STATS_ONLY(stats.record_overflow(overflow_stack()->size()));
   1.158 -#ifdef MIPS64
   1.159 -    if (Use3A2000) OrderAccess::fence();
   1.160 -#endif
   1.161    }
   1.162    return true;
   1.163  }
   1.164 @@ -584,16 +639,10 @@
   1.165    for (uint i = 0; i < 2 * _n; i++) {
   1.166      if (steal_best_of_2(queue_num, seed, t)) {
   1.167        TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(true));
   1.168 -#ifdef MIPS64
   1.169 -      if (Use3A2000) OrderAccess::fence();
   1.170 -#endif
   1.171        return true;
   1.172      }
   1.173    }
   1.174    TASKQUEUE_STATS_ONLY(queue(queue_num)->stats.record_steal(false));
   1.175 -#ifdef MIPS64
   1.176 -  if (Use3A2000) OrderAccess::fence();
   1.177 -#endif
   1.178    return false;
   1.179  }
   1.180  
   1.181 @@ -695,7 +744,11 @@
   1.182  
   1.183  template<class E, MEMFLAGS F, unsigned int N> inline bool
   1.184  GenericTaskQueue<E, F, N>::push(E t) {
   1.185 +#ifdef MIPS64
   1.186 +  uint localBot = this->get_bottom();
   1.187 +#else
   1.188    uint localBot = _bottom;
   1.189 +#endif
   1.190    assert(localBot < N, "_bottom out of range.");
   1.191    idx_t top = _age.top();
   1.192    uint dirty_n_elems = dirty_size(localBot, top);
   1.193 @@ -707,11 +760,12 @@
   1.194      // assignment.  However, casting to E& means that we trigger an
   1.195      // unused-value warning.  So, we cast the E& to void.
   1.196      (void) const_cast<E&>(_elems[localBot] = t);
   1.197 +#ifdef MIPS64
   1.198 +    this->set_bottom(increment_index(localBot));
   1.199 +#else
   1.200      OrderAccess::release_store(&_bottom, increment_index(localBot));
   1.201 +#endif
   1.202      TASKQUEUE_STATS_ONLY(stats.record_push());
   1.203 -#ifdef MIPS64
   1.204 -    if (Use3A2000) OrderAccess::fence();
   1.205 -#endif
   1.206      return true;
   1.207    } else {
   1.208      return push_slow(t, dirty_n_elems);
   1.209 @@ -720,7 +774,11 @@
   1.210  
   1.211  template<class E, MEMFLAGS F, unsigned int N> inline bool
   1.212  GenericTaskQueue<E, F, N>::pop_local(volatile E& t) {
   1.213 +#ifdef MIPS64
   1.214 +  uint localBot = this->get_bottom();
   1.215 +#else
   1.216    uint localBot = _bottom;
   1.217 +#endif
   1.218    // This value cannot be N-1.  That can only occur as a result of
   1.219    // the assignment to bottom in this method.  If it does, this method
   1.220    // resets the size to 0 before the next call (which is sequential,
   1.221 @@ -729,7 +787,11 @@
   1.222    assert(dirty_n_elems != N - 1, "Shouldn't be possible...");
   1.223    if (dirty_n_elems == 0) return false;
   1.224    localBot = decrement_index(localBot);
   1.225 +#ifdef MIPS64
   1.226 +  this->set_bottom(localBot);
   1.227 +#else
   1.228    _bottom = localBot;
   1.229 +#endif
   1.230    // This is necessary to prevent any read below from being reordered
   1.231    // before the store just above.
   1.232    OrderAccess::fence();

mercurial