• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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_HEAP_STORE_BUFFER_H_
6 #define V8_HEAP_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/cancelable-task.h"
12 #include "src/globals.h"
13 #include "src/heap/gc-tracer.h"
14 #include "src/heap/remembered-set.h"
15 #include "src/heap/slot-set.h"
16 
17 namespace v8 {
18 namespace internal {
19 
20 // Intermediate buffer that accumulates old-to-new stores from the generated
21 // code. Moreover, it stores invalid old-to-new slots with two entries.
22 // The first is a tagged address of the start of the invalid range, the second
23 // one is the end address of the invalid range or null if there is just one slot
24 // that needs to be removed from the remembered set. On buffer overflow the
25 // slots are moved to the remembered set.
26 class StoreBuffer {
27  public:
28   enum StoreBufferMode { IN_GC, NOT_IN_GC };
29 
30   static const int kStoreBufferSize = 1 << (11 + kPointerSizeLog2);
31   static const int kStoreBufferMask = kStoreBufferSize - 1;
32   static const int kStoreBuffers = 2;
33   static const intptr_t kDeletionTag = 1;
34 
35   V8_EXPORT_PRIVATE static int StoreBufferOverflow(Isolate* isolate);
36 
37   static void DeleteDuringGarbageCollection(StoreBuffer* store_buffer,
38                                             Address start, Address end);
39   static void InsertDuringGarbageCollection(StoreBuffer* store_buffer,
40                                             Address slot);
41 
42   static void DeleteDuringRuntime(StoreBuffer* store_buffer, Address start,
43                                   Address end);
44   static void InsertDuringRuntime(StoreBuffer* store_buffer, Address slot);
45 
46   explicit StoreBuffer(Heap* heap);
47   void SetUp();
48   void TearDown();
49 
50   // Used to add entries from generated code.
top_address()51   inline Address* top_address() { return reinterpret_cast<Address*>(&top_); }
52 
53   // Moves entries from a specific store buffer to the remembered set. This
54   // method takes a lock.
55   void MoveEntriesToRememberedSet(int index);
56 
57   // This method ensures that all used store buffer entries are transferred to
58   // the remembered set.
59   void MoveAllEntriesToRememberedSet();
60 
IsDeletionAddress(Address address)61   inline bool IsDeletionAddress(Address address) const {
62     return address & kDeletionTag;
63   }
64 
MarkDeletionAddress(Address address)65   inline Address MarkDeletionAddress(Address address) {
66     return address | kDeletionTag;
67   }
68 
UnmarkDeletionAddress(Address address)69   inline Address UnmarkDeletionAddress(Address address) {
70     return address & ~kDeletionTag;
71   }
72 
73   inline void InsertDeletionIntoStoreBuffer(Address start, Address end);
74   inline void InsertIntoStoreBuffer(Address slot);
75 
InsertEntry(Address slot)76   void InsertEntry(Address slot) {
77     // Insertions coming from the GC are directly inserted into the remembered
78     // set. Insertions coming from the runtime are added to the store buffer to
79     // allow concurrent processing.
80     insertion_callback(this, slot);
81   }
82 
83   // If we only want to delete a single slot, end should be set to null which
84   // will be written into the second field. When processing the store buffer
85   // the more efficient Remove method will be called in this case.
86   void DeleteEntry(Address start, Address end = kNullAddress) {
87     // Deletions coming from the GC are directly deleted from the remembered
88     // set. Deletions coming from the runtime are added to the store buffer
89     // to allow concurrent processing.
90     deletion_callback(this, start, end);
91   }
92 
93   void SetMode(StoreBufferMode mode);
94 
95   // Used by the concurrent processing thread to transfer entries from the
96   // store buffer to the remembered set.
97   void ConcurrentlyProcessStoreBuffer();
98 
Empty()99   bool Empty() {
100     for (int i = 0; i < kStoreBuffers; i++) {
101       if (lazy_top_[i]) {
102         return false;
103       }
104     }
105     return top_ == start_[current_];
106   }
107 
heap()108   Heap* heap() { return heap_; }
109 
110  private:
111   // There are two store buffers. If one store buffer fills up, the main thread
112   // publishes the top pointer of the store buffer that needs processing in its
113   // global lazy_top_ field. After that it start the concurrent processing
114   // thread. The concurrent processing thread uses the pointer in lazy_top_.
115   // It will grab the given mutex and transfer its entries to the remembered
116   // set. If the concurrent thread does not make progress, the main thread will
117   // perform the work.
118   // Important: there is an ordering constrained. The store buffer with the
119   // older entries has to be processed first.
120   class Task : public CancelableTask {
121    public:
Task(Isolate * isolate,StoreBuffer * store_buffer)122     Task(Isolate* isolate, StoreBuffer* store_buffer)
123         : CancelableTask(isolate),
124           store_buffer_(store_buffer),
125           tracer_(isolate->heap()->tracer()) {}
~Task()126     virtual ~Task() {}
127 
128    private:
RunInternal()129     void RunInternal() override {
130       TRACE_BACKGROUND_GC(tracer_,
131                           GCTracer::BackgroundScope::BACKGROUND_STORE_BUFFER);
132       store_buffer_->ConcurrentlyProcessStoreBuffer();
133     }
134     StoreBuffer* store_buffer_;
135     GCTracer* tracer_;
136     DISALLOW_COPY_AND_ASSIGN(Task);
137   };
138 
mode()139   StoreBufferMode mode() const { return mode_; }
140 
141   void FlipStoreBuffers();
142 
143   Heap* heap_;
144 
145   Address* top_;
146 
147   // The start and the limit of the buffer that contains store slots
148   // added from the generated code. We have two chunks of store buffers.
149   // Whenever one fills up, we notify a concurrent processing thread and
150   // use the other empty one in the meantime.
151   Address* start_[kStoreBuffers];
152   Address* limit_[kStoreBuffers];
153 
154   // At most one lazy_top_ pointer is set at any time.
155   Address* lazy_top_[kStoreBuffers];
156   base::Mutex mutex_;
157 
158   // We only want to have at most one concurrent processing tas running.
159   bool task_running_;
160 
161   // Points to the current buffer in use.
162   int current_;
163 
164   // During GC, entries are directly added to the remembered set without
165   // going through the store buffer. This is signaled by a special
166   // IN_GC mode.
167   StoreBufferMode mode_;
168 
169   VirtualMemory virtual_memory_;
170 
171   // Callbacks are more efficient than reading out the gc state for every
172   // store buffer operation.
173   void (*insertion_callback)(StoreBuffer*, Address);
174   void (*deletion_callback)(StoreBuffer*, Address, Address);
175 };
176 
177 }  // namespace internal
178 }  // namespace v8
179 
180 #endif  // V8_HEAP_STORE_BUFFER_H_
181