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(T* t) {
  74   assert(t != NULL, "invariant");
  75   assert(_full.in_list(t), "invariant");
  76   remove_full(t);
  77   assert(!_full.in_list(t), "invariant");
  78   if (t->transient()) {
  79     deallocate(t);
  80     return;
  81   }
  82   assert(t->empty(), "invariant");
  83   assert(!t->retired(), "invariant");
  84   if (should_populate_cache()) {
  85     assert(!_free.in_list(t), "invariant");
  86     insert_free_head(t);
  87   } else {
  88     deallocate(t);
  89   }
  90 }
  91 
  92 template <typename T, template <typename> class RetrievalType, typename Callback>
  93 template <typename IteratorCallback, typename IteratorType>
  94 inline void JfrMemorySpace<T, RetrievalType, Callback>
  95 ::iterate(IteratorCallback& callback, bool full, jfr_iter_direction direction) {
  96   IteratorType iterator(full ? _full : _free, direction);
  97   while (iterator.has_next()) {
  98     callback.process(iterator.next());
  99   }
 100 }
 101 
 102 template <typename Mspace>
 103 inline size_t size_adjustment(size_t size, Mspace* mspace) {
 104   assert(mspace != NULL, "invariant");
 105   static const size_t min_elem_size = mspace->min_elem_size();
 106   if (size <= min_elem_size) {
 107     size = min_elem_size;
 108   }
 109   return size;
 110 }
 111 
 112 template <typename Mspace>
 113 inline typename Mspace::Type* mspace_allocate(size_t size, Mspace* mspace) {
 114   return mspace->allocate(size_adjustment(size, mspace));
 115 }
 116 
 117 template <typename Mspace>
 118 inline typename Mspace::Type* mspace_allocate_acquired(size_t size, Mspace* mspace, Thread* thread) {
 119   typename Mspace::Type* const t = mspace_allocate(size, mspace);
 120   if (t == NULL) return NULL;
 121   t->acquire(thread);
 122   return t;
 123 }
 124 
 125 template <typename Mspace>
 126 inline typename Mspace::Type* mspace_allocate_transient(size_t size, Mspace* mspace, Thread* thread) {
 127   typename Mspace::Type* const t = mspace_allocate_acquired(size, mspace, thread);
 128   if (t == NULL) return NULL;
 129   assert(t->acquired_by_self(), "invariant");
 130   t->set_transient();
 131   return t;
 132 }
 133 
 134 template <typename Mspace>
 135 inline typename Mspace::Type* mspace_allocate_transient_lease(size_t size, Mspace* mspace, Thread* thread) {
 136   typename Mspace::Type* const t = mspace_allocate_transient(size, mspace, thread);
 137   if (t == NULL) return NULL;
 138   assert(t->acquired_by_self(), "invariant");
 139   assert(t->transient(), "invaiant");
 140   t->set_lease();
 141   return t;
 142 }
 143 
 144 template <typename Mspace>
 145 inline typename Mspace::Type* mspace_allocate_to_full(size_t size, Mspace* mspace, Thread* thread) {
 146   typename Mspace::Type* const t = mspace_allocate_acquired(size, mspace, thread);
 147   if (t == NULL) return NULL;
 148   MspaceLock<Mspace> lock(mspace);
 149   mspace->insert_full_head(t);
 150   return t;
 151 }
 152 
 153 template <typename Mspace>
 154 inline typename Mspace::Type* mspace_allocate_transient_to_full(size_t size, Mspace* mspace, Thread* thread) {
 155   typename Mspace::Type* const t = mspace_allocate_transient(size, mspace, thread);
 156   if (t == NULL) return NULL;
 157   MspaceLock<Mspace> lock(mspace);
 158   mspace->insert_full_head(t);
 159   return t;
 160 }
 161 
 162 template <typename Mspace>
 163 inline typename Mspace::Type* mspace_allocate_transient_lease_to_full(size_t size, Mspace* mspace, Thread* thread) {
 164   typename Mspace::Type* const t = mspace_allocate_transient_lease(size, mspace, thread);
 165   if (t == NULL) return NULL;
 166   assert(t->acquired_by_self(), "invariant");
 167   assert(t->transient(), "invaiant");
 168   assert(t->lease(), "invariant");
 169   MspaceLock<Mspace> lock(mspace);
 170   mspace->insert_full_head(t);
 171   return t;
 172 }
 173 
 174 template <typename Mspace>
 175 inline typename Mspace::Type* mspace_get_free(size_t size, Mspace* mspace, Thread* thread) {
 176   return mspace->get(size, thread);
 177 }
 178 
 179 template <typename Mspace>
 180 inline typename Mspace::Type* mspace_get_free_with_retry(size_t size, Mspace* mspace, size_t retry_count, Thread* thread) {
 181   assert(size <= mspace->min_elem_size(), "invariant");
 182   for (size_t i = 0; i < retry_count; ++i) {
 183     typename Mspace::Type* const t = mspace_get_free(size, mspace, thread);
 184     if (t != NULL) {
 185       return t;
 186     }
 187   }
 188   return NULL;
 189 }
 190 
 191 template <typename Mspace>
 192 inline typename Mspace::Type* mspace_get_free_with_detach(size_t size, Mspace* mspace, Thread* thread) {
 193   typename Mspace::Type* t = mspace_get_free(size, mspace, thread);
 194   if (t != NULL) {
 195     mspace->remove_free(t);
 196   }
 197   return t;
 198 }
 199 
 200 template <typename Mspace>
 201 inline typename Mspace::Type* mspace_get_free_to_full(size_t size, Mspace* mspace, Thread* thread) {
 202   assert(size <= mspace->min_elem_size(), "invariant");
 203   typename Mspace::Type* t = mspace_get_free(size, mspace, thread);
 204   if (t == NULL) {
 205     return NULL;
 206   }
 207   assert(t->acquired_by_self(), "invariant");
 208   MspaceLock<Mspace> lock(mspace);
 209   move_to_head(t, mspace->free(), mspace->full());
 210   return t;
 211 }
 212 
 213 template <typename Mspace>
 214 inline typename Mspace::Type* mspace_get_to_full(size_t size, Mspace* mspace, Thread* thread) {
 215   size = size_adjustment(size, mspace);
 216   typename Mspace::Type* const t = mspace_get_free_to_full(size, mspace, thread);
 217   if (t != NULL) {
 218     return t;
 219   }
 220   return mspace_allocate_to_full(size, mspace, thread);
 221 }
 222 
 223 template <typename Mspace>
 224 inline typename Mspace::Type* mspace_get_free_lease_with_retry(size_t size, Mspace* mspace, size_t retry_count, Thread* thread) {
 225   typename Mspace::Type* t = mspace_get_free_with_retry(size, mspace, retry_count, thread);
 226   if (t != NULL) {
 227     t->set_lease();
 228   }
 229   return t;
 230 }
 231 
 232 template <typename Mspace>
 233 inline typename Mspace::Type* mspace_get_lease(size_t size, Mspace* mspace, Thread* thread) {
 234   typename Mspace::Type* t;
 235   t = mspace_get_free_lease(size, mspace, thread);
 236   if (t != NULL) {
 237     assert(t->acquired_by_self(), "invariant");
 238     assert(t->lease(), "invariant");
 239     return t;
 240   }
 241   t = mspace_allocate_transient_to_full(size, mspace, thread);
 242   if (t != NULL) {
 243     t->set_lease();
 244   }
 245   return t;
 246 }
 247 
 248 template <typename Mspace>
 249 inline void mspace_release(typename Mspace::Type* t, Mspace* mspace) {
 250   assert(t != NULL, "invariant");
 251   assert(t->unflushed_size() == 0, "invariant");
 252   assert(mspace != NULL, "invariant");
 253   mspace->release(t);
 254 }
 255 
 256 template <typename Mspace>
 257 inline void mspace_release_critical(typename Mspace::Type* t, Mspace* mspace) {
 258   MspaceLock<Mspace> lock(mspace);
 259   mspace_release(t, mspace);
 260 }
 261 
 262 template <typename List>
 263 inline void move_to_head(typename List::Node* t, List& from, List& to) {
 264   assert(from.in_list(t), "invariant");
 265   to.prepend(from.remove(t));
 266 }
 267 
 268 template <typename Processor, typename Mspace, typename Iterator>
 269 inline void process_free_list_iterator_control(Processor& processor, Mspace* mspace, jfr_iter_direction direction = forward) {
 270   mspace->template iterate<Processor, Iterator>(processor, false, direction);
 271 }
 272 
 273 template <typename Processor, typename Mspace, typename Iterator>
 274 inline void process_full_list_iterator_control(Processor& processor, Mspace* mspace, jfr_iter_direction direction = forward) {
 275   mspace->template iterate<Processor, Iterator>(processor, true, direction);
 276 }
 277 
 278 template <typename Processor, typename Mspace>
 279 inline void process_full_list(Processor& processor, Mspace* mspace, jfr_iter_direction direction = forward) {
 280   assert(mspace != NULL, "invariant");
 281   if (mspace->is_full_empty()) return;
 282   process_full_list_iterator_control<Processor, Mspace, typename Mspace::Iterator>(processor, mspace, direction);
 283 }
 284 
 285 template <typename Processor, typename Mspace>
 286 inline void process_free_list(Processor& processor, Mspace* mspace, jfr_iter_direction direction = forward) {
 287   assert(mspace != NULL, "invariant");
 288   assert(mspace->has_free(), "invariant");
 289   process_free_list_iterator_control<Processor, Mspace, typename Mspace::Iterator>(processor, mspace, direction);
 290 }
 291 
 292 template <typename Mspace>
 293 inline bool ReleaseOp<Mspace>::process(typename Mspace::Type* t) {
 294   assert(t != NULL, "invariant");
 295   assert(t->retired() || t->acquired_by_self(), "invariant");
 296   if (t->transient()) {
 297     mspace_release_critical(t, _mspace);
 298     return true;
 299   }
 300   t->reinitialize();
 301   t->release(); // publish
 302   return true;
 303 }
 304 
 305 template <typename Mspace>
 306 inline bool MutexedReleaseOp<Mspace>::process(typename Mspace::Type* t) {
 307   assert(t != NULL, "invariant");
 308   if (t->retired() || t->try_acquire(_thread)) {
 309     return ReleaseOp<Mspace>::process(t); // mutexed access
 310   }
 311   return true;
 312 }
 313 
 314 template <typename Mspace>
 315 inline bool ThreadLocalReleaseOp<Mspace>::process(typename Mspace::Type* t) {
 316   assert(t != NULL, "invariant");
 317   assert(!t->transient(), "invariant");
 318   assert(!t->lease(), "invariant");
 319   if (t->retired()) {
 320     t->reinitialize();
 321     mspace_release_critical(t, _mspace);
 322   }
 323   return true;
 324 }
 325 
 326 #ifdef ASSERT
 327 template <typename T>
 328 inline void assert_migration_state(const T* old, const T* new_buffer, size_t used, size_t requested) {
 329   assert(old != NULL, "invariant");
 330   assert(new_buffer != NULL, "invariant");
 331   assert(old->pos() >= old->start(), "invariant");
 332   assert(old->pos() + used <= old->end(), "invariant");
 333   assert(new_buffer->free_size() >= (used + requested), "invariant");
 334 }
 335 #endif // ASSERT
 336 
 337 template <typename T>
 338 inline void migrate_outstanding_writes(const T* old, T* new_buffer, size_t used, size_t requested) {
 339   DEBUG_ONLY(assert_migration_state(old, new_buffer, used, requested);)
 340   if (used > 0) {
 341     memcpy(new_buffer->pos(), old->pos(), used);
 342   }
 343 }
 344 
 345 #endif // SHARE_VM_JFR_RECORDER_STORAGE_JFRMEMORYSPACE_INLINE_HPP
 346