Home | History | Annotate | Download | only in heap
      1 // Copyright 2015 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 #include "src/heap/array-buffer-tracker.h"
      6 #include "src/heap/array-buffer-tracker-inl.h"
      7 #include "src/heap/heap.h"
      8 
      9 namespace v8 {
     10 namespace internal {
     11 
     12 LocalArrayBufferTracker::~LocalArrayBufferTracker() {
     13   CHECK(array_buffers_.empty());
     14 }
     15 
     16 template <LocalArrayBufferTracker::FreeMode free_mode>
     17 void LocalArrayBufferTracker::Free() {
     18   size_t freed_memory = 0;
     19   for (TrackingData::iterator it = array_buffers_.begin();
     20        it != array_buffers_.end();) {
     21     JSArrayBuffer* buffer = reinterpret_cast<JSArrayBuffer*>(it->first);
     22     if ((free_mode == kFreeAll) ||
     23         Marking::IsWhite(ObjectMarking::MarkBitFrom(buffer))) {
     24       const size_t len = it->second;
     25       heap_->isolate()->array_buffer_allocator()->Free(buffer->backing_store(),
     26                                                        len);
     27       freed_memory += len;
     28       it = array_buffers_.erase(it);
     29     } else {
     30       ++it;
     31     }
     32   }
     33   if (freed_memory > 0) {
     34     heap_->update_external_memory_concurrently_freed(
     35         static_cast<intptr_t>(freed_memory));
     36   }
     37 }
     38 
     39 template <typename Callback>
     40 void LocalArrayBufferTracker::Process(Callback callback) {
     41   JSArrayBuffer* new_buffer = nullptr;
     42   size_t freed_memory = 0;
     43   for (TrackingData::iterator it = array_buffers_.begin();
     44        it != array_buffers_.end();) {
     45     const CallbackResult result = callback(it->first, &new_buffer);
     46     if (result == kKeepEntry) {
     47       ++it;
     48     } else if (result == kUpdateEntry) {
     49       DCHECK_NOT_NULL(new_buffer);
     50       Page* target_page = Page::FromAddress(new_buffer->address());
     51       // We need to lock the target page because we cannot guarantee
     52       // exclusive access to new space pages.
     53       if (target_page->InNewSpace()) target_page->mutex()->Lock();
     54       LocalArrayBufferTracker* tracker = target_page->local_tracker();
     55       if (tracker == nullptr) {
     56         target_page->AllocateLocalTracker();
     57         tracker = target_page->local_tracker();
     58       }
     59       DCHECK_NOT_NULL(tracker);
     60       tracker->Add(new_buffer, it->second);
     61       if (target_page->InNewSpace()) target_page->mutex()->Unlock();
     62       it = array_buffers_.erase(it);
     63     } else if (result == kRemoveEntry) {
     64       const size_t len = it->second;
     65       heap_->isolate()->array_buffer_allocator()->Free(
     66           it->first->backing_store(), len);
     67       freed_memory += len;
     68       it = array_buffers_.erase(it);
     69     } else {
     70       UNREACHABLE();
     71     }
     72   }
     73   if (freed_memory > 0) {
     74     heap_->update_external_memory_concurrently_freed(
     75         static_cast<intptr_t>(freed_memory));
     76   }
     77 }
     78 
     79 void ArrayBufferTracker::FreeDeadInNewSpace(Heap* heap) {
     80   DCHECK_EQ(heap->gc_state(), Heap::HeapState::SCAVENGE);
     81   for (Page* page : NewSpacePageRange(heap->new_space()->FromSpaceStart(),
     82                                       heap->new_space()->FromSpaceEnd())) {
     83     bool empty = ProcessBuffers(page, kUpdateForwardedRemoveOthers);
     84     CHECK(empty);
     85   }
     86   heap->account_external_memory_concurrently_freed();
     87 }
     88 
     89 void ArrayBufferTracker::FreeDead(Page* page) {
     90   // Callers need to ensure having the page lock.
     91   LocalArrayBufferTracker* tracker = page->local_tracker();
     92   if (tracker == nullptr) return;
     93   DCHECK(!page->SweepingDone());
     94   tracker->Free<LocalArrayBufferTracker::kFreeDead>();
     95   if (tracker->IsEmpty()) {
     96     page->ReleaseLocalTracker();
     97   }
     98 }
     99 
    100 void ArrayBufferTracker::FreeAll(Page* page) {
    101   LocalArrayBufferTracker* tracker = page->local_tracker();
    102   if (tracker == nullptr) return;
    103   tracker->Free<LocalArrayBufferTracker::kFreeAll>();
    104   if (tracker->IsEmpty()) {
    105     page->ReleaseLocalTracker();
    106   }
    107 }
    108 
    109 bool ArrayBufferTracker::ProcessBuffers(Page* page, ProcessingMode mode) {
    110   LocalArrayBufferTracker* tracker = page->local_tracker();
    111   if (tracker == nullptr) return true;
    112 
    113   DCHECK(page->SweepingDone());
    114   tracker->Process(
    115       [mode](JSArrayBuffer* old_buffer, JSArrayBuffer** new_buffer) {
    116         MapWord map_word = old_buffer->map_word();
    117         if (map_word.IsForwardingAddress()) {
    118           *new_buffer = JSArrayBuffer::cast(map_word.ToForwardingAddress());
    119           return LocalArrayBufferTracker::kUpdateEntry;
    120         }
    121         return mode == kUpdateForwardedKeepOthers
    122                    ? LocalArrayBufferTracker::kKeepEntry
    123                    : LocalArrayBufferTracker::kRemoveEntry;
    124       });
    125   return tracker->IsEmpty();
    126 }
    127 
    128 bool ArrayBufferTracker::IsTracked(JSArrayBuffer* buffer) {
    129   Page* page = Page::FromAddress(buffer->address());
    130   {
    131     base::LockGuard<base::Mutex> guard(page->mutex());
    132     LocalArrayBufferTracker* tracker = page->local_tracker();
    133     if (tracker == nullptr) return false;
    134     return tracker->IsTracked(buffer);
    135   }
    136 }
    137 
    138 }  // namespace internal
    139 }  // namespace v8
    140