1 /*
   2  * Copyright (c) 2016, 2018, 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 #ifndef SHARE_VM_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_INLINE_HPP
  26 #define SHARE_VM_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_INLINE_HPP
  27 
  28 #include "jfr/recorder/storage/jfrMemorySpace.hpp"
  29 
  30 template <typename T, template <typename> class RetrievalType, typename Callback>
  31 JfrMemorySpace<T, RetrievalType, Callback>::
  32 JfrMemorySpace(size_t min_elem_size, size_t limit_size, size_t cache_count, Callback* callback) :
  33   _free(),
  34   _full(),
  35   _min_elem_size(min_elem_size),
  36   _limit_size(limit_size),
  37   _cache_count(cache_count),
  38   _callback(callback) {}
  39 
  40 template <typename T, template <typename> class RetrievalType, typename Callback>
  41 JfrMemorySpace<T, RetrievalType, Callback>::~JfrMemorySpace() {
  42   Iterator full_iter(_full);
  43   while (full_iter.has_next()) {
  44     Type* t = full_iter.next();
  45     _full.remove(t);
  46     deallocate(t);
  47   }
  48   Iterator free_iter(_free);
  49   while (free_iter.has_next()) {
  50     Type* t = free_iter.next();
  51     _free.remove(t);
  52     deallocate(t);
  53   }
  54 }
  55 
  56 template <typename T, template <typename> class RetrievalType, typename Callback>
  57 bool JfrMemorySpace<T, RetrievalType, Callback>::initialize() {
  58   assert(_min_elem_size % os::vm_page_size() == 0, "invariant");
  59   assert(_limit_size % os::vm_page_size() == 0, "invariant");
  60   // pre-allocate cache elements
  61   for (size_t i = 0; i < _cache_count; ++i) {
  62     Type* const t = allocate(_min_elem_size);
  63     if (t == NULL) {
  64       return false;
  65     }
  66     insert_free_head(t);
  67   }
  68   assert(_free.count() == _cache_count, "invariant");
  69   return true;
  70 }
  71 
  72 template <typename T, template <typename> class RetrievalType, typename Callback>
  73 inline void JfrMemorySpace<T, RetrievalType, Callback>::release_full(T* t) {
  74   assert(is_locked(), "invariant");
  75   assert(t != NULL, "invariant");
  76   assert(_full.in_list(t), "invariant");
  77   remove_full(t);
  78   assert(!_full.in_list(t), "invariant");
  79   if (t->transient()) {
  80     deallocate(t);
  81     return;
  82   }
  83   assert(t->empty(), "invariant");
  84   assert(!t->retired(), "invariant");
  85   assert(t->identity() == NULL, "invariant");
  86   if (should_populate_cache()) {
  87     assert(!_free.in_list(t), "invariant");
  88     insert_free_head(t);
  89   } else {
  90     deallocate(t);
  91   }
  92 }
  93 
  94 template <typename T, template <typename> class RetrievalType, typename Callback>
  95 inline void JfrMemorySpace<T, RetrievalType, Callback>::release_free(T* t) {
  96   assert(is_locked(), "invariant");
  97   assert(t != NULL, "invariant");
  98   assert(_free.in_list(t), "invariant");
  99   if (t->transient()) {
 100     remove_free(t);
 101     assert(!_free.in_list(t), "invariant");
 102     deallocate(t);
 103     return;
 104   }
 105   assert(t->empty(), "invariant");
 106   assert(!t->retired(), "invariant");
 107   assert(t->identity() == NULL, "invariant");
 108   if (!should_populate_cache()) {
 109     remove_free(t);
 110     assert(!_free.in_list(t), "invariant");
 111     deallocate(t);
 112   }
 113 }
 114 
 115 template <typename T, template <typename> class RetrievalType, typename Callback>
 116 template <typename IteratorCallback, typename IteratorType>
 117 inline void JfrMemorySpace<T, RetrievalType, Callback>
 118 ::iterate(IteratorCallback& callback, bool full, jfr_iter_direction direction) {
 119   IteratorType iterator(full ? _full : _free, direction);
 120   while (iterator.has_next()) {
 121     callback.process(iterator.next());
 122   }
 123 }
 124 
 125 template <typename Mspace>
 126 inline size_t size_adjustment(size_t size, Mspace* mspace) {
 127   assert(mspace != NULL, "invariant");
 128   static const size_t min_elem_size = mspace->min_elem_size();
 129   if (size < min_elem_size) {
 130     size = min_elem_size;
 131   }
 132   return size;
 133 }
 134 
 135 template <typename Mspace>
 136 inline typename Mspace::Type* mspace_allocate(size_t size, Mspace* mspace) {
 137   return mspace->allocate(size_adjustment(size, mspace));
 138 }
 139 
 140 template <typename Mspace>
 141 inline typename Mspace::Type* mspace_allocate_acquired(size_t size, Mspace* mspace, Thread* thread) {
 142   typename Mspace::Type* const t = mspace_allocate(size, mspace);
 143   if (t == NULL) return NULL;
 144   t->acquire(thread);
 145   return t;
 146 }
 147 
 148 template <typename Mspace>
 149 inline typename Mspace::Type* mspace_allocate_transient(size_t size, Mspace* mspace, Thread* thread) {
 150   typename Mspace::Type* const t = mspace_allocate_acquired(size, mspace, thread);
 151   if (t == NULL) return NULL;
 152   assert(t->acquired_by_self(), "invariant");
 153   t->set_transient();
 154   return t;
 155 }
 156 
 157 template <typename Mspace>
 158 inline typename Mspace::Type* mspace_allocate_transient_lease(size_t size, Mspace* mspace, Thread* thread) {
 159   typename Mspace::Type* const t = mspace_allocate_transient(size, mspace, thread);
 160   if (t == NULL) return NULL;
 161   assert(t->acquired_by_self(), "invariant");
 162   assert(t->transient(), "invaiant");
 163   t->set_lease();
 164   return t;
 165 }
 166 
 167 template <typename Mspace>
 168 inline typename Mspace::Type* mspace_allocate_to_full(size_t size, Mspace* mspace, Thread* thread) {
 169   assert(mspace->is_locked(), "invariant");
 170   typename Mspace::Type* const t = mspace_allocate_acquired(size, mspace, thread);
 171   if (t == NULL) return NULL;
 172   mspace->insert_full_head(t);
 173   return t;
 174 }
 175 
 176 template <typename Mspace>
 177 inline typename Mspace::Type* mspace_allocate_transient_to_full(size_t size, Mspace* mspace, Thread* thread) {
 178   typename Mspace::Type* const t = mspace_allocate_transient(size, mspace, thread);
 179   if (t == NULL) return NULL;
 180   MspaceLock<Mspace> lock(mspace);
 181   mspace->insert_full_head(t);
 182   return t;
 183 }
 184 
 185 template <typename Mspace>
 186 inline typename Mspace::Type* mspace_allocate_transient_lease_to_full(size_t size, Mspace* mspace, Thread* thread) {
 187   typename Mspace::Type* const t = mspace_allocate_transient_lease(size, mspace, thread);
 188   if (t == NULL) return NULL;
 189   assert(t->acquired_by_self(), "invariant");
 190   assert(t->transient(), "invaiant");
 191   assert(t->lease(), "invariant");
 192   MspaceLock<Mspace> lock(mspace);
 193   mspace->insert_full_head(t);
 194   return t;
 195 }
 196 
 197 template <typename Mspace>
 198 inline typename Mspace::Type* mspace_allocate_transient_lease_to_free(size_t size, Mspace* mspace, Thread* thread) {
 199   typename Mspace::Type* const t = mspace_allocate_transient_lease(size, mspace, thread);
 200   if (t == NULL) return NULL;
 201   assert(t->acquired_by_self(), "invariant");
 202   assert(t->transient(), "invaiant");
 203   assert(t->lease(), "invariant");
 204   MspaceLock<Mspace> lock(mspace);
 205   mspace->insert_free_head(t);
 206   return t;
 207 }
 208 
 209 template <typename Mspace>
 210 inline typename Mspace::Type* mspace_get_free(size_t size, Mspace* mspace, Thread* thread) {
 211   return mspace->get(size, thread);
 212 }
 213 
 214 template <typename Mspace>
 215 inline typename Mspace::Type* mspace_get_free_with_retry(size_t size, Mspace* mspace, size_t retry_count, Thread* thread) {
 216   assert(size <= mspace->min_elem_size(), "invariant");
 217   for (size_t i = 0; i < retry_count; ++i) {
 218     typename Mspace::Type* const t = mspace_get_free(size, mspace, thread);
 219     if (t != NULL) {
 220       return t;
 221     }
 222   }
 223   return NULL;
 224 }
 225 
 226 template <typename Mspace>
 227 inline typename Mspace::Type* mspace_get_free_with_detach(size_t size, Mspace* mspace, Thread* thread) {
 228   typename Mspace::Type* t = mspace_get_free(size, mspace, thread);
 229   if (t != NULL) {
 230     mspace->remove_free(t);
 231   }
 232   return t;
 233 }
 234 
 235 template <typename Mspace>
 236 inline typename Mspace::Type* mspace_get_free_to_full(size_t size, Mspace* mspace, Thread* thread) {
 237   assert(size <= mspace->min_elem_size(), "invariant");
 238   assert(mspace->is_locked(), "invariant");
 239   typename Mspace::Type* t = mspace_get_free(size, mspace, thread);
 240   if (t == NULL) {
 241     return NULL;
 242   }
 243   assert(t->acquired_by_self(), "invariant");
 244   move_to_head(t, mspace->free(), mspace->full());
 245   return t;
 246 }
 247 
 248 template <typename Mspace>
 249 inline typename Mspace::Type* mspace_get_to_full(size_t size, Mspace* mspace, Thread* thread) {
 250   size = size_adjustment(size, mspace);
 251   MspaceLock<Mspace> lock(mspace);
 252   if (size <= mspace->min_elem_size()) {
 253     typename Mspace::Type* const t = mspace_get_free_to_full(size, mspace, thread);
 254     if (t != NULL) {
 255       return t;
 256     }
 257   }
 258   return mspace_allocate_to_full(size, mspace, thread);
 259 }
 260 
 261 template <typename Mspace>
 262 inline typename Mspace::Type* mspace_get_free_lease_with_retry(size_t size, Mspace* mspace, size_t retry_count, Thread* thread) {
 263   typename Mspace::Type* t = mspace_get_free_with_retry(size, mspace, retry_count, thread);
 264   if (t != NULL) {
 265     t->set_lease();
 266   }
 267   return t;
 268 }
 269 
 270 template <typename Mspace>
 271 inline typename Mspace::Type* mspace_get_lease(size_t size, Mspace* mspace, Thread* thread) {
 272   typename Mspace::Type* t;
 273   t = mspace_get_free_lease(size, mspace, thread);
 274   if (t != NULL) {
 275     assert(t->acquired_by_self(), "invariant");
 276     assert(t->lease(), "invariant");
 277     return t;
 278   }
 279   t = mspace_allocate_transient_to_full(size, mspace, thread);
 280   if (t != NULL) {
 281     t->set_lease();
 282   }
 283   return t;
 284 }
 285 
 286 template <typename Mspace>
 287 inline void mspace_release_full(typename Mspace::Type* t, Mspace* mspace) {
 288   assert(t != NULL, "invariant");
 289   assert(t->unflushed_size() == 0, "invariant");
 290   assert(mspace != NULL, "invariant");
 291   assert(mspace->is_locked(), "invariant");
 292   mspace->release_full(t);
 293 }
 294 
 295 template <typename Mspace>
 296 inline void mspace_release_free(typename Mspace::Type* t, Mspace* mspace) {
 297   assert(t != NULL, "invariant");
 298   assert(t->unflushed_size() == 0, "invariant");
 299   assert(mspace != NULL, "invariant");
 300   assert(mspace->is_locked(), "invariant");
 301   mspace->release_free(t);
 302 }
 303 
 304 template <typename Mspace>
 305 inline void mspace_release_full_critical(typename Mspace::Type* t, Mspace* mspace) {
 306   MspaceLock<Mspace> lock(mspace);
 307   mspace_release_full(t, mspace);
 308 }
 309 
 310 template <typename Mspace>
 311 inline void mspace_release_free_critical(typename Mspace::Type* t, Mspace* mspace) {
 312   MspaceLock<Mspace> lock(mspace);
 313   mspace_release_free(t, mspace);
 314 }
 315 
 316 template <typename List>
 317 inline void move_to_head(typename List::Node* t, List& from, List& to) {
 318   assert(from.in_list(t), "invariant");
 319   to.prepend(from.remove(t));
 320 }
 321 
 322 template <typename Processor, typename Mspace, typename Iterator>
 323 inline void process_free_list_iterator_control(Processor& processor, Mspace* mspace, jfr_iter_direction direction = forward) {
 324   mspace->template iterate<Processor, Iterator>(processor, false, direction);
 325 }
 326 
 327 template <typename Processor, typename Mspace, typename Iterator>
 328 inline void process_full_list_iterator_control(Processor& processor, Mspace* mspace, jfr_iter_direction direction = forward) {
 329   mspace->template iterate<Processor, Iterator>(processor, true, direction);
 330 }
 331 
 332 template <typename Processor, typename Mspace>
 333 inline void process_full_list(Processor& processor, Mspace* mspace, jfr_iter_direction direction = forward) {
 334   assert(mspace != NULL, "invariant");
 335   if (mspace->is_full_empty()) return;
 336   process_full_list_iterator_control<Processor, Mspace, typename Mspace::Iterator>(processor, mspace, direction);
 337 }
 338 
 339 template <typename Processor, typename Mspace>
 340 inline void process_free_list(Processor& processor, Mspace* mspace, jfr_iter_direction direction = forward) {
 341   assert(mspace != NULL, "invariant");
 342   assert(mspace->has_free(), "invariant");
 343   process_free_list_iterator_control<Processor, Mspace, typename Mspace::Iterator>(processor, mspace, direction);
 344 }
 345 
 346 template <typename Mspace>
 347 inline bool ReleaseOp<Mspace>::process(typename Mspace::Type* t) {
 348   assert(t != NULL, "invariant");
 349   if (t->retired() || t->try_acquire(_thread)) {
 350     if (t->transient()) {
 351       if (_release_full) {
 352         mspace_release_full_critical(t, _mspace);
 353       } else {
 354         mspace_release_free_critical(t, _mspace);
 355       }
 356       return true;
 357     }
 358     t->reinitialize();
 359     assert(t->empty(), "invariant");
 360     t->release(); // publish
 361   }
 362   return true;
 363 }
 364 
 365 #ifdef ASSERT
 366 template <typename T>
 367 inline void assert_migration_state(const T* old, const T* new_buffer, size_t used, size_t requested) {
 368   assert(old != NULL, "invariant");
 369   assert(new_buffer != NULL, "invariant");
 370   assert(old->pos() >= old->start(), "invariant");
 371   assert(old->pos() + used <= old->end(), "invariant");
 372   assert(new_buffer->free_size() >= (used + requested), "invariant");
 373 }
 374 #endif // ASSERT
 375 
 376 template <typename T>
 377 inline void migrate_outstanding_writes(const T* old, T* new_buffer, size_t used, size_t requested) {
 378   DEBUG_ONLY(assert_migration_state(old, new_buffer, used, requested);)
 379   if (used > 0) {
 380     memcpy(new_buffer->pos(), old->pos(), used);
 381   }
 382 }
 383 
 384 #endif // SHARE_VM_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_INLINE_HPP
 385