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