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