Home | History | Annotate | Download | only in heap
      1 // Copyright 2011 the V8 project authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #ifndef V8_STORE_BUFFER_H_
      6 #define V8_STORE_BUFFER_H_
      7 
      8 #include "src/allocation.h"
      9 #include "src/base/logging.h"
     10 #include "src/base/platform/platform.h"
     11 #include "src/globals.h"
     12 
     13 namespace v8 {
     14 namespace internal {
     15 
     16 class Page;
     17 class PagedSpace;
     18 class StoreBuffer;
     19 
     20 typedef void (*ObjectSlotCallback)(HeapObject** from, HeapObject* to);
     21 
     22 // Used to implement the write barrier by collecting addresses of pointers
     23 // between spaces.
     24 class StoreBuffer {
     25  public:
     26   explicit StoreBuffer(Heap* heap);
     27 
     28   static void StoreBufferOverflow(Isolate* isolate);
     29 
     30   void SetUp();
     31   void TearDown();
     32 
     33   // This is used to add addresses to the store buffer non-concurrently.
     34   inline void Mark(Address addr);
     35 
     36   // This is used to add addresses to the store buffer when multiple threads
     37   // may operate on the store buffer.
     38   inline void MarkSynchronized(Address addr);
     39 
     40   // This is used by the heap traversal to enter the addresses into the store
     41   // buffer that should still be in the store buffer after GC.  It enters
     42   // addresses directly into the old buffer because the GC starts by wiping the
     43   // old buffer and thereafter only visits each cell once so there is no need
     44   // to attempt to remove any dupes.  During the first part of a GC we
     45   // are using the store buffer to access the old spaces and at the same time
     46   // we are rebuilding the store buffer using this function.  There is, however
     47   // no issue of overwriting the buffer we are iterating over, because this
     48   // stage of the scavenge can only reduce the number of addresses in the store
     49   // buffer (some objects are promoted so pointers to them do not need to be in
     50   // the store buffer).  The later parts of the GC scan the pages that are
     51   // exempt from the store buffer and process the promotion queue.  These steps
     52   // can overflow this buffer.  We check for this and on overflow we call the
     53   // callback set up with the StoreBufferRebuildScope object.
     54   inline void EnterDirectlyIntoStoreBuffer(Address addr);
     55 
     56   // Iterates over all pointers that go from old space to new space.  It will
     57   // delete the store buffer as it starts so the callback should reenter
     58   // surviving old-to-new pointers into the store buffer to rebuild it.
     59   void IteratePointersToNewSpace(ObjectSlotCallback callback);
     60 
     61   static const int kStoreBufferOverflowBit = 1 << (14 + kPointerSizeLog2);
     62   static const int kStoreBufferSize = kStoreBufferOverflowBit;
     63   static const int kStoreBufferLength = kStoreBufferSize / sizeof(Address);
     64   static const int kOldStoreBufferLength = kStoreBufferLength * 16;
     65   static const int kHashSetLengthLog2 = 12;
     66   static const int kHashSetLength = 1 << kHashSetLengthLog2;
     67 
     68   void Compact();
     69 
     70   void GCPrologue();
     71   void GCEpilogue();
     72 
     73   Object*** Limit() { return reinterpret_cast<Object***>(old_limit_); }
     74   Object*** Start() { return reinterpret_cast<Object***>(old_start_); }
     75   Object*** Top() { return reinterpret_cast<Object***>(old_top_); }
     76   void SetTop(Object*** top) {
     77     DCHECK(top >= Start());
     78     DCHECK(top <= Limit());
     79     old_top_ = reinterpret_cast<Address*>(top);
     80   }
     81 
     82   bool old_buffer_is_sorted() { return old_buffer_is_sorted_; }
     83   bool old_buffer_is_filtered() { return old_buffer_is_filtered_; }
     84 
     85   void EnsureSpace(intptr_t space_needed);
     86   void Verify();
     87 
     88   bool PrepareForIteration();
     89 
     90   void Filter(int flag);
     91 
     92   // Eliminates all stale store buffer entries from the store buffer, i.e.,
     93   // slots that are not part of live objects anymore. This method must be
     94   // called after marking, when the whole transitive closure is known and
     95   // must be called before sweeping when mark bits are still intact.
     96   void ClearInvalidStoreBufferEntries();
     97   void VerifyValidStoreBufferEntries();
     98 
     99  private:
    100   Heap* heap_;
    101 
    102   // The store buffer is divided up into a new buffer that is constantly being
    103   // filled by mutator activity and an old buffer that is filled with the data
    104   // from the new buffer after compression.
    105   Address* start_;
    106   Address* limit_;
    107 
    108   Address* old_start_;
    109   Address* old_limit_;
    110   Address* old_top_;
    111   Address* old_reserved_limit_;
    112   base::VirtualMemory* old_virtual_memory_;
    113 
    114   bool old_buffer_is_sorted_;
    115   bool old_buffer_is_filtered_;
    116   bool during_gc_;
    117   // The garbage collector iterates over many pointers to new space that are not
    118   // handled by the store buffer.  This flag indicates whether the pointers
    119   // found by the callbacks should be added to the store buffer or not.
    120   bool store_buffer_rebuilding_enabled_;
    121   StoreBufferCallback callback_;
    122   bool may_move_store_buffer_entries_;
    123 
    124   base::VirtualMemory* virtual_memory_;
    125 
    126   // Two hash sets used for filtering.
    127   // If address is in the hash set then it is guaranteed to be in the
    128   // old part of the store buffer.
    129   uintptr_t* hash_set_1_;
    130   uintptr_t* hash_set_2_;
    131   bool hash_sets_are_empty_;
    132 
    133   // Used for synchronization of concurrent store buffer access.
    134   base::Mutex mutex_;
    135 
    136   void ClearFilteringHashSets();
    137 
    138   bool SpaceAvailable(intptr_t space_needed);
    139   void ExemptPopularPages(int prime_sample_step, int threshold);
    140 
    141   void ProcessOldToNewSlot(Address slot_address,
    142                            ObjectSlotCallback slot_callback);
    143 
    144   void FindPointersToNewSpaceInRegion(Address start, Address end,
    145                                       ObjectSlotCallback slot_callback);
    146 
    147   void IteratePointersInStoreBuffer(ObjectSlotCallback slot_callback);
    148 
    149 #ifdef VERIFY_HEAP
    150   void VerifyPointers(LargeObjectSpace* space);
    151 #endif
    152 
    153   friend class DontMoveStoreBufferEntriesScope;
    154   friend class FindPointersToNewSpaceVisitor;
    155   friend class StoreBufferRebuildScope;
    156 };
    157 
    158 
    159 class StoreBufferRebuilder {
    160  public:
    161   explicit StoreBufferRebuilder(StoreBuffer* store_buffer)
    162       : store_buffer_(store_buffer) {}
    163 
    164   void Callback(MemoryChunk* page, StoreBufferEvent event);
    165 
    166  private:
    167   StoreBuffer* store_buffer_;
    168 
    169   // We record in this variable how full the store buffer was when we started
    170   // iterating over the current page, finding pointers to new space.  If the
    171   // store buffer overflows again we can exempt the page from the store buffer
    172   // by rewinding to this point instead of having to search the store buffer.
    173   Object*** start_of_current_page_;
    174   // The current page we are scanning in the store buffer iterator.
    175   MemoryChunk* current_page_;
    176 };
    177 
    178 
    179 class StoreBufferRebuildScope {
    180  public:
    181   explicit StoreBufferRebuildScope(Heap* heap, StoreBuffer* store_buffer,
    182                                    StoreBufferCallback callback)
    183       : store_buffer_(store_buffer),
    184         stored_state_(store_buffer->store_buffer_rebuilding_enabled_),
    185         stored_callback_(store_buffer->callback_) {
    186     store_buffer_->store_buffer_rebuilding_enabled_ = true;
    187     store_buffer_->callback_ = callback;
    188     (*callback)(heap, NULL, kStoreBufferStartScanningPagesEvent);
    189   }
    190 
    191   ~StoreBufferRebuildScope() {
    192     store_buffer_->callback_ = stored_callback_;
    193     store_buffer_->store_buffer_rebuilding_enabled_ = stored_state_;
    194   }
    195 
    196  private:
    197   StoreBuffer* store_buffer_;
    198   bool stored_state_;
    199   StoreBufferCallback stored_callback_;
    200 };
    201 
    202 
    203 class DontMoveStoreBufferEntriesScope {
    204  public:
    205   explicit DontMoveStoreBufferEntriesScope(StoreBuffer* store_buffer)
    206       : store_buffer_(store_buffer),
    207         stored_state_(store_buffer->may_move_store_buffer_entries_) {
    208     store_buffer_->may_move_store_buffer_entries_ = false;
    209   }
    210 
    211   ~DontMoveStoreBufferEntriesScope() {
    212     store_buffer_->may_move_store_buffer_entries_ = stored_state_;
    213   }
    214 
    215  private:
    216   StoreBuffer* store_buffer_;
    217   bool stored_state_;
    218 };
    219 }  // namespace internal
    220 }  // namespace v8
    221 
    222 #endif  // V8_STORE_BUFFER_H_
    223