1 /*
   2  * Copyright (c) 2011, 2019, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  *
  23  */
  24 
  25 #include "precompiled.hpp"
  26 #include "jfr/recorder/storage/jfrBuffer.hpp"
  27 #include "runtime/atomic.hpp"
  28 #include "runtime/orderAccess.hpp"
  29 #include "runtime/thread.inline.hpp"
  30 
  31 static const u1* const MUTEX_CLAIM = NULL;
  32 
  33 JfrBuffer::JfrBuffer() : _next(NULL),
  34                          _prev(NULL),
  35                          _identity(NULL),
  36                          _pos(NULL),
  37                          _top(NULL),
  38                          _flags(0),
  39                          _header_size(0),
  40                          _size(0) {}
  41 
  42 bool JfrBuffer::initialize(size_t header_size, size_t size, const void* id /* NULL */) {
  43   _header_size = (u2)header_size;
  44   _size = (u4)(size / BytesPerWord);
  45   assert(_identity == NULL, "invariant");
  46   _identity = id;
  47   set_pos(start());
  48   set_top(start());
  49   assert(_next == NULL, "invariant");
  50   assert(free_size() == size, "invariant");
  51   assert(!transient(), "invariant");
  52   assert(!lease(), "invariant");
  53   assert(!retired(), "invariant");
  54   return true;
  55 }
  56 
  57 void JfrBuffer::reinitialize(bool exclusion /* false */) {
  58   assert(!lease(), "invariant");
  59   assert(!transient(), "invariant");
  60   set_pos(start());
  61   if (exclusion != excluded()) {
  62     // update
  63     if (exclusion) {
  64       set_excluded();
  65     } else {
  66       clear_excluded();
  67     }
  68   }
  69   clear_retired();
  70   set_top(start());
  71 }
  72 
  73 void JfrBuffer::concurrent_reinitialization() {
  74   concurrent_top();
  75   assert(!lease(), "invariant");
  76   assert(!transient(), "invariant");
  77   set_pos(start());
  78   set_concurrent_top(start());
  79   clear_retired();
  80 }
  81 
  82 size_t JfrBuffer::discard() {
  83   size_t discard_size = unflushed_size();
  84   set_top(pos());
  85   return discard_size;
  86 }
  87 
  88 const u1* JfrBuffer::stable_top() const {
  89   const u1* current_top;
  90   do {
  91     current_top = Atomic::load(&_top);
  92   } while (MUTEX_CLAIM == current_top);
  93   return current_top;
  94 }
  95 
  96 const u1* JfrBuffer::top() const {
  97   return _top;
  98 }
  99 
 100 void JfrBuffer::set_top(const u1* new_top) {
 101   _top = new_top;
 102 }
 103 
 104 const u1* JfrBuffer::concurrent_top() const {
 105   do {
 106     const u1* current_top = stable_top();
 107     if (Atomic::cmpxchg(&_top, current_top, MUTEX_CLAIM) == current_top) {
 108       return current_top;
 109     }
 110   } while (true);
 111 }
 112 
 113 void JfrBuffer::set_concurrent_top(const u1* new_top) {
 114   assert(new_top != MUTEX_CLAIM, "invariant");
 115   assert(new_top <= end(), "invariant");
 116   assert(new_top >= start(), "invariant");
 117   assert(top() == MUTEX_CLAIM, "invariant");
 118   OrderAccess::storestore();
 119   _top = new_top;
 120 }
 121 
 122 size_t JfrBuffer::unflushed_size() const {
 123   return pos() - stable_top();
 124 }
 125 
 126 void JfrBuffer::acquire(const void* id) {
 127   assert(id != NULL, "invariant");
 128   const void* current_id;
 129   do {
 130     current_id = Atomic::load(&_identity);
 131   } while (current_id != NULL || Atomic::cmpxchg(&_identity, current_id, id) != current_id);
 132 }
 133 
 134 bool JfrBuffer::try_acquire(const void* id) {
 135   assert(id != NULL, "invariant");
 136   const void* const current_id = Atomic::load(&_identity);
 137   return current_id == NULL && Atomic::cmpxchg(&_identity, current_id, id) == current_id;
 138 }
 139 
 140 void JfrBuffer::release() {
 141   OrderAccess::storestore();
 142   _identity = NULL;
 143 }
 144 
 145 bool JfrBuffer::acquired_by(const void* id) const {
 146   return identity() == id;
 147 }
 148 
 149 bool JfrBuffer::acquired_by_self() const {
 150   return acquired_by(Thread::current());
 151 }
 152 
 153 #ifdef ASSERT
 154 static bool validate_to(const JfrBuffer* const to, size_t size) {
 155   assert(to != NULL, "invariant");
 156   assert(to->acquired_by_self(), "invariant");
 157   assert(to->free_size() >= size, "invariant");
 158   return true;
 159 }
 160 
 161 static bool validate_concurrent_this(const JfrBuffer* const t, size_t size) {
 162   assert(t->top() == MUTEX_CLAIM, "invariant");
 163   return true;
 164 }
 165 
 166 static bool validate_this(const JfrBuffer* const t, size_t size) {
 167   assert(t->top() + size <= t->pos(), "invariant");
 168   return true;
 169 }
 170 #endif // ASSERT
 171 
 172 void JfrBuffer::move(JfrBuffer* const to, size_t size) {
 173   assert(validate_to(to, size), "invariant");
 174   assert(validate_this(this, size), "invariant");
 175   const u1* current_top = top();
 176   assert(current_top != NULL, "invariant");
 177   memcpy(to->pos(), current_top, size);
 178   to->set_pos(size);
 179   to->release();
 180   set_top(current_top + size);
 181 }
 182 
 183 void JfrBuffer::concurrent_move_and_reinitialize(JfrBuffer* const to, size_t size) {
 184   assert(validate_to(to, size), "invariant");
 185   const u1* current_top = concurrent_top();
 186   assert(validate_concurrent_this(this, size), "invariant");
 187   const size_t actual_size = MIN2(size, (size_t)(pos() - current_top));
 188   assert(actual_size <= size, "invariant");
 189   memcpy(to->pos(), current_top, actual_size);
 190   to->set_pos(actual_size);
 191   set_pos(start());
 192   to->release();
 193   set_concurrent_top(start());
 194 }
 195 
 196 enum FLAG {
 197   RETIRED = 1,
 198   TRANSIENT = 2,
 199   LEASE = 4,
 200   EXCLUDED = 8
 201 };
 202 
 203 bool JfrBuffer::transient() const {
 204   return (u1)TRANSIENT == (_flags & (u1)TRANSIENT);
 205 }
 206 
 207 void JfrBuffer::set_transient() {
 208   _flags |= (u1)TRANSIENT;
 209   assert(transient(), "invariant");
 210 }
 211 
 212 void JfrBuffer::clear_transient() {
 213   if (transient()) {
 214     _flags ^= (u1)TRANSIENT;
 215   }
 216   assert(!transient(), "invariant");
 217 }
 218 
 219 bool JfrBuffer::lease() const {
 220   return (u1)LEASE == (_flags & (u1)LEASE);
 221 }
 222 
 223 void JfrBuffer::set_lease() {
 224   _flags |= (u1)LEASE;
 225   assert(lease(), "invariant");
 226 }
 227 
 228 void JfrBuffer::clear_lease() {
 229   if (lease()) {
 230     _flags ^= (u1)LEASE;
 231   }
 232   assert(!lease(), "invariant");
 233 }
 234 
 235 bool JfrBuffer::excluded() const {
 236   return (u1)EXCLUDED == (_flags & (u1)EXCLUDED);
 237 }
 238 
 239 void JfrBuffer::set_excluded() {
 240   _flags |= (u1)EXCLUDED;
 241   assert(excluded(), "invariant");
 242 }
 243 
 244 void JfrBuffer::clear_excluded() {
 245   if (excluded()) {
 246     OrderAccess::storestore();
 247     _flags ^= (u1)EXCLUDED;
 248   }
 249   assert(!excluded(), "invariant");
 250 }
 251 
 252 bool JfrBuffer::retired() const {
 253   return (_flags & (u1)RETIRED) == (u1)RETIRED;
 254 }
 255 
 256 void JfrBuffer::set_retired() {
 257   OrderAccess::storestore();
 258   _flags |= (u1)RETIRED;
 259 }
 260 
 261 void JfrBuffer::clear_retired() {
 262   if (retired()) {
 263     OrderAccess::storestore();
 264     _flags ^= (u1)RETIRED;
 265   }
 266 }