1 /*
   2  * Copyright (c) 2016, 2016, 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_MEMORY_VTBUFFER_HPP
  26 #define SHARE_VM_MEMORY_VTBUFFER_HPP
  27 
  28 #include "memory/allocation.hpp"
  29 #include "runtime/globals.hpp"
  30 #include "runtime/os.hpp"
  31 #include "utilities/globalDefinitions.hpp"
  32 
  33 class VTBufferChunk : public CMmapObj<VTBufferChunk, mtValueTypes> {
  34   friend class VMStructs;
  35 
  36   static const int MAGIC_NUMBER = 3141592;
  37 
  38 private:
  39   int            _magic;
  40   int            _index;
  41   VTBufferChunk* _prev;
  42   VTBufferChunk* _next;
  43   JavaThread*    _owner;
  44  public:
  45 
  46   /* A VTBufferChunk is a 4KB page used to create a thread-local
  47    * buffer to store values. They are allocated in a global pool,
  48    * and then threads can get them to create their own buffer.
  49    * Each thread creates a linked list of VTBufferChunk to build
  50    * its buffer. Fields _prev and _next are used to link the
  51    * chunks together, the _owner field indicates to which thread
  52    * this chunk belongs to (if NULL, it means the chunk has been
  53    * returned to the global pool). When creating the linked list,
  54    * the field _index is used to store the position of the chunk
  55    * in the list. The index is used to optimize the comparison
  56    * of addresses of buffered values. Because the thread local
  57    * buffer is made of non-contiguous chunks, it is not possible
  58    * to directly compare the two addresses. The comparison requires
  59    * first to compare the indexes of each address' chunk, and if
  60    * they are equal, compare the addresses directly. Without
  61    * the _index field, this operation would require to walk the
  62    * linked list for each comparison.
  63    */
  64 
  65   VTBufferChunk(JavaThread* thread) {
  66     _magic = MAGIC_NUMBER;
  67     _index = -1;
  68     _prev = NULL;
  69     _next = NULL;
  70     _owner = thread;
  71   }
  72 
  73   int            index() { return _index; }
  74   void           set_index(int index) { _index = index; }
  75   VTBufferChunk* prev() { return _prev; }
  76   void           set_prev(VTBufferChunk* prev) { _prev = prev; }
  77   VTBufferChunk* next() { return _next; }
  78   void           set_next(VTBufferChunk* next) { _next = next; }
  79   JavaThread*    owner() { return _owner; }
  80   void           set_owner(JavaThread* thread) {
  81     assert(thread == NULL || _owner == NULL || _owner == thread, "Sanity check");
  82     _owner = thread;
  83   }
  84 
  85   bool is_valid() {
  86     return _magic == MAGIC_NUMBER && _owner != NULL && _index != -1;
  87   }
  88 
  89   void* first_alloc() { return (void*)((char*)this + align_object_size(sizeof (VTBufferChunk))); }
  90   void* alloc_limit() { return (void*)((char*)this + chunk_size() - 1); }
  91 
  92   static int chunk_size() {
  93     return os::vm_page_size();
  94   }
  95 
  96   static uintptr_t chunk_mask() {
  97     return ~(chunk_size() - 1);
  98   }
  99 
 100   static ByteSize index_offset() { return byte_offset_of(VTBufferChunk, _index); }
 101 
 102   static size_t max_alloc_size() {
 103     return chunk_size() - align_object_size(sizeof (VTBufferChunk));
 104   }
 105 
 106   static VTBufferChunk* chunk(void* address) {
 107     VTBufferChunk* c = (VTBufferChunk*)((intptr_t)address & chunk_mask());
 108     assert(c->is_valid(), "Sanity check");
 109     return c;
 110   }
 111 
 112   static bool check_buffered(void* address) {
 113     assert(address != NULL, "Sanity check");
 114     VTBufferChunk* c = (VTBufferChunk*)((intptr_t)address & chunk_mask());
 115     return c->is_valid();
 116   }
 117 
 118   bool contains(void* address) {
 119     return address > (char*)chunk(address) && address < ((char*)chunk(address) + chunk_size());
 120   }
 121 };
 122 
 123 class VTBuffer : AllStatic {
 124   friend class VMStructs;
 125 private:
 126   static VTBufferChunk* _free_list;
 127   static Mutex* _pool_lock;
 128   static int _pool_counter;
 129   static int _max_pool_counter;
 130   static int _total_allocated;
 131   static int _total_deallocated;
 132   static int _total_failed;
 133   static const int _max_free_list = 64;  // Should be tunable
 134 
 135 public:
 136   static Mutex* lock() { return _pool_lock; }
 137   static oop allocate_value(ValueKlass* k, TRAPS);
 138   static bool allocate_vt_chunk(JavaThread* thread);
 139   static void recycle_chunk(JavaThread* thread, VTBufferChunk* chunk);
 140   static void return_vt_chunk(JavaThread* thread, VTBufferChunk* chunk);
 141 
 142   static int in_pool() { return _pool_counter; }
 143   static int max_in_pool() { return _max_pool_counter; }
 144   static int total_allocated() { return _total_allocated; }
 145   static int total_deallocated() { return _total_deallocated; }
 146   static int total_failed() { return _total_failed; }
 147 
 148   static bool is_in_vt_buffer(const void* p) {
 149     intptr_t chunk_mask = (~(VTBufferChunk::chunk_size() - 1));
 150     VTBufferChunk* c = (VTBufferChunk*)((intptr_t)p & chunk_mask);
 151     return c->is_valid();
 152   }
 153 
 154   static bool value_belongs_to_frame(oop p, frame *f);
 155   static void recycle_vt_in_frame(JavaThread* thread, frame* f);
 156   static void recycle_vtbuffer(JavaThread *thread, frame f);
 157   static address relocate_value(address old, address previous, int previous_size_in_words);
 158   static oop relocate_return_value(JavaThread* thread, frame fr, oop old);
 159 
 160   static void fix_frame_vt_alloc_ptr(frame fr, VTBufferChunk* chunk);
 161 
 162 };
 163 
 164 struct VT_relocation_entry {
 165   int chunk_index;
 166   address old_ptr;
 167   address new_ptr;
 168 };
 169 
 170 
 171 class BufferedValuesMarking : public BufferedValueClosure {
 172   frame* _frame;
 173   struct VT_relocation_entry* _reloc_table;
 174   int _size;
 175   int* _index;
 176 public:
 177   BufferedValuesMarking(frame* frame, struct VT_relocation_entry* reloc_table, int size, int* index) {
 178     _frame = frame;
 179     _reloc_table = reloc_table;
 180     _size = size;
 181     _index = index;
 182   }
 183   virtual void do_buffered_value(oop* p);
 184 };
 185 
 186 class BufferedValuesPointersUpdate : public BufferedValueClosure {
 187   frame* _frame;
 188 public:
 189   BufferedValuesPointersUpdate(frame* frame) {
 190     _frame = frame;
 191   }
 192   virtual void do_buffered_value(oop* p);
 193 };
 194 
 195 #endif /* SHARE_VM_MEMORY_VTBUFFER_HPP */