LCOV - code coverage report
Current view: top level - src - microtask-queue.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 104 111 93.7 %
Date: 2019-04-18 Functions: 16 19 84.2 %

          Line data    Source code
       1             : // Copyright 2018 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/microtask-queue.h"
       6             : 
       7             : #include <stddef.h>
       8             : #include <algorithm>
       9             : 
      10             : #include "src/api-inl.h"
      11             : #include "src/base/logging.h"
      12             : #include "src/handles-inl.h"
      13             : #include "src/isolate.h"
      14             : #include "src/objects/microtask-inl.h"
      15             : #include "src/roots-inl.h"
      16             : #include "src/tracing/trace-event.h"
      17             : #include "src/visitors.h"
      18             : 
      19             : namespace v8 {
      20             : namespace internal {
      21             : 
      22             : const size_t MicrotaskQueue::kRingBufferOffset =
      23             :     OFFSET_OF(MicrotaskQueue, ring_buffer_);
      24             : const size_t MicrotaskQueue::kCapacityOffset =
      25             :     OFFSET_OF(MicrotaskQueue, capacity_);
      26             : const size_t MicrotaskQueue::kSizeOffset = OFFSET_OF(MicrotaskQueue, size_);
      27             : const size_t MicrotaskQueue::kStartOffset = OFFSET_OF(MicrotaskQueue, start_);
      28             : const size_t MicrotaskQueue::kFinishedMicrotaskCountOffset =
      29             :     OFFSET_OF(MicrotaskQueue, finished_microtask_count_);
      30             : 
      31             : const intptr_t MicrotaskQueue::kMinimumCapacity = 8;
      32             : 
      33             : // static
      34       62440 : void MicrotaskQueue::SetUpDefaultMicrotaskQueue(Isolate* isolate) {
      35             :   DCHECK_NULL(isolate->default_microtask_queue());
      36             : 
      37       62440 :   MicrotaskQueue* microtask_queue = new MicrotaskQueue;
      38       62440 :   microtask_queue->next_ = microtask_queue;
      39       62440 :   microtask_queue->prev_ = microtask_queue;
      40             :   isolate->set_default_microtask_queue(microtask_queue);
      41       62440 : }
      42             : 
      43             : // static
      44          15 : std::unique_ptr<MicrotaskQueue> MicrotaskQueue::New(Isolate* isolate) {
      45             :   DCHECK_NOT_NULL(isolate->default_microtask_queue());
      46             : 
      47          15 :   std::unique_ptr<MicrotaskQueue> microtask_queue(new MicrotaskQueue);
      48             : 
      49             :   // Insert the new instance to the next of last MicrotaskQueue instance.
      50          15 :   MicrotaskQueue* last = isolate->default_microtask_queue()->prev_;
      51          15 :   microtask_queue->next_ = last->next_;
      52          15 :   microtask_queue->prev_ = last;
      53          15 :   last->next_->prev_ = microtask_queue.get();
      54          15 :   last->next_ = microtask_queue.get();
      55             : 
      56          15 :   return microtask_queue;
      57             : }
      58             : 
      59             : MicrotaskQueue::MicrotaskQueue() = default;
      60             : 
      61      187326 : MicrotaskQueue::~MicrotaskQueue() {
      62       62442 :   if (next_ != this) {
      63             :     DCHECK_NE(prev_, this);
      64          15 :     next_->prev_ = prev_;
      65          15 :     prev_->next_ = next_;
      66             :   }
      67       62442 :   delete[] ring_buffer_;
      68      124884 : }
      69             : 
      70             : // static
      71        2720 : Address MicrotaskQueue::CallEnqueueMicrotask(Isolate* isolate,
      72             :                                              intptr_t microtask_queue_pointer,
      73             :                                              Address raw_microtask) {
      74        2720 :   Microtask microtask = Microtask::cast(Object(raw_microtask));
      75             :   reinterpret_cast<MicrotaskQueue*>(microtask_queue_pointer)
      76        2720 :       ->EnqueueMicrotask(microtask);
      77        2720 :   return ReadOnlyRoots(isolate).undefined_value().ptr();
      78             : }
      79             : 
      80         120 : void MicrotaskQueue::EnqueueMicrotask(v8::Isolate* v8_isolate,
      81             :                                       v8::Local<Function> function) {
      82             :   Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
      83             :   HandleScope scope(isolate);
      84             :   Handle<CallableTask> microtask = isolate->factory()->NewCallableTask(
      85         240 :       Utils::OpenHandle(*function), isolate->native_context());
      86         120 :   EnqueueMicrotask(*microtask);
      87         120 : }
      88             : 
      89           0 : void MicrotaskQueue::EnqueueMicrotask(v8::Isolate* v8_isolate,
      90             :                                       v8::MicrotaskCallback callback,
      91             :                                       void* data) {
      92             :   Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
      93             :   HandleScope scope(isolate);
      94             :   Handle<CallbackTask> microtask = isolate->factory()->NewCallbackTask(
      95             :       isolate->factory()->NewForeign(reinterpret_cast<Address>(callback)),
      96           0 :       isolate->factory()->NewForeign(reinterpret_cast<Address>(data)));
      97           0 :   EnqueueMicrotask(*microtask);
      98           0 : }
      99             : 
     100       14092 : void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) {
     101       14092 :   if (size_ == capacity_) {
     102             :     // Keep the capacity of |ring_buffer_| power of 2, so that the JIT
     103             :     // implementation can calculate the modulo easily.
     104        6370 :     intptr_t new_capacity = std::max(kMinimumCapacity, capacity_ << 1);
     105        3185 :     ResizeBuffer(new_capacity);
     106             :   }
     107             : 
     108             :   DCHECK_LT(size_, capacity_);
     109       28184 :   ring_buffer_[(start_ + size_) % capacity_] = microtask.ptr();
     110       14092 :   ++size_;
     111       14092 : }
     112             : 
     113      120970 : void MicrotaskQueue::PerformCheckpoint(v8::Isolate* v8_isolate) {
     114      241907 :   if (!IsRunningMicrotasks() && !GetMicrotasksScopeDepth() &&
     115             :       !HasMicrotasksSuppressions()) {
     116             :     Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
     117      119220 :     RunMicrotasks(isolate);
     118             :   }
     119      120970 : }
     120             : 
     121             : namespace {
     122             : 
     123             : class SetIsRunningMicrotasks {
     124             :  public:
     125             :   explicit SetIsRunningMicrotasks(bool* flag) : flag_(flag) {
     126             :     DCHECK(!*flag_);
     127       38068 :     *flag_ = true;
     128             :   }
     129             : 
     130             :   ~SetIsRunningMicrotasks() {
     131             :     DCHECK(*flag_);
     132       38068 :     *flag_ = false;
     133             :   }
     134             : 
     135             :  private:
     136             :   bool* flag_;
     137             : };
     138             : 
     139             : }  // namespace
     140             : 
     141      121391 : int MicrotaskQueue::RunMicrotasks(Isolate* isolate) {
     142      121391 :   if (!size()) {
     143             :     OnCompleted(isolate);
     144       83321 :     return 0;
     145             :   }
     146             : 
     147       38068 :   intptr_t base_count = finished_microtask_count_;
     148             : 
     149             :   HandleScope handle_scope(isolate);
     150       38068 :   MaybeHandle<Object> maybe_exception;
     151             : 
     152             :   MaybeHandle<Object> maybe_result;
     153             : 
     154             :   int processed_microtask_count;
     155             :   {
     156       38068 :     SetIsRunningMicrotasks scope(&is_running_microtasks_);
     157             :     v8::Isolate::SuppressMicrotaskExecutionScope suppress(
     158       76136 :         reinterpret_cast<v8::Isolate*>(isolate));
     159             :     HandleScopeImplementer::EnteredContextRewindScope rewind_scope(
     160             :         isolate->handle_scope_implementer());
     161       76136 :     TRACE_EVENT_BEGIN0("v8.execute", "RunMicrotasks");
     162       76136 :     TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.RunMicrotasks");
     163       38068 :     maybe_result = Execution::TryRunMicrotasks(isolate, this, &maybe_exception);
     164             :     processed_microtask_count =
     165       38068 :         static_cast<int>(finished_microtask_count_ - base_count);
     166       76136 :     TRACE_EVENT_END1("v8.execute", "RunMicrotasks", "microtask_count",
     167             :                      processed_microtask_count);
     168             :   }
     169             : 
     170             :   // If execution is terminating, clean up and propagate that to TryCatch scope.
     171       38088 :   if (maybe_result.is_null() && maybe_exception.is_null()) {
     172          20 :     delete[] ring_buffer_;
     173          20 :     ring_buffer_ = nullptr;
     174          20 :     capacity_ = 0;
     175          20 :     size_ = 0;
     176          20 :     start_ = 0;
     177          20 :     isolate->SetTerminationOnExternalTryCatch();
     178             :     OnCompleted(isolate);
     179          20 :     return -1;
     180             :   }
     181             :   DCHECK_EQ(0, size());
     182             :   OnCompleted(isolate);
     183             : 
     184       38048 :   return processed_microtask_count;
     185             : }
     186             : 
     187      278162 : void MicrotaskQueue::IterateMicrotasks(RootVisitor* visitor) {
     188      278162 :   if (size_) {
     189             :     // Iterate pending Microtasks as root objects to avoid the write barrier for
     190             :     // all single Microtask. If this hurts the GC performance, use a FixedArray.
     191        1692 :     visitor->VisitRootPointers(
     192             :         Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_ + start_),
     193        2256 :         FullObjectSlot(ring_buffer_ + std::min(start_ + size_, capacity_)));
     194        1128 :     visitor->VisitRootPointers(
     195             :         Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_),
     196        1692 :         FullObjectSlot(ring_buffer_ + std::max(start_ + size_ - capacity_,
     197        2820 :                                                static_cast<intptr_t>(0))));
     198             :   }
     199             : 
     200      278162 :   if (capacity_ <= kMinimumCapacity) {
     201      278030 :     return;
     202             :   }
     203             : 
     204         132 :   intptr_t new_capacity = capacity_;
     205         954 :   while (new_capacity > 2 * size_) {
     206         411 :     new_capacity >>= 1;
     207             :   }
     208         132 :   new_capacity = std::max(new_capacity, kMinimumCapacity);
     209         132 :   if (new_capacity < capacity_) {
     210          54 :     ResizeBuffer(new_capacity);
     211             :   }
     212             : }
     213             : 
     214           0 : int MicrotaskQueue::GetMicrotasksScopeDepth() const {
     215      120937 :   return microtasks_depth_;
     216             : }
     217             : 
     218          45 : void MicrotaskQueue::AddMicrotasksCompletedCallback(
     219             :     MicrotasksCompletedCallbackWithData callback, void* data) {
     220             :   CallbackWithData callback_with_data(callback, data);
     221             :   auto pos =
     222             :       std::find(microtasks_completed_callbacks_.begin(),
     223          45 :                 microtasks_completed_callbacks_.end(), callback_with_data);
     224          45 :   if (pos != microtasks_completed_callbacks_.end()) return;
     225          45 :   microtasks_completed_callbacks_.push_back(callback_with_data);
     226             : }
     227             : 
     228        3730 : void MicrotaskQueue::RemoveMicrotasksCompletedCallback(
     229             :     MicrotasksCompletedCallbackWithData callback, void* data) {
     230             :   CallbackWithData callback_with_data(callback, data);
     231             :   auto pos =
     232             :       std::find(microtasks_completed_callbacks_.begin(),
     233        3730 :                 microtasks_completed_callbacks_.end(), callback_with_data);
     234        7415 :   if (pos == microtasks_completed_callbacks_.end()) return;
     235             :   microtasks_completed_callbacks_.erase(pos);
     236             : }
     237             : 
     238      121389 : void MicrotaskQueue::FireMicrotasksCompletedCallback(Isolate* isolate) const {
     239      121389 :   std::vector<CallbackWithData> callbacks(microtasks_completed_callbacks_);
     240      121437 :   for (auto& callback : callbacks) {
     241          50 :     callback.first(reinterpret_cast<v8::Isolate*>(isolate), callback.second);
     242             :   }
     243      121387 : }
     244             : 
     245           4 : Microtask MicrotaskQueue::get(intptr_t index) const {
     246             :   DCHECK_LT(index, size_);
     247           4 :   Object microtask(ring_buffer_[(index + start_) % capacity_]);
     248           4 :   return Microtask::cast(microtask);
     249             : }
     250             : 
     251           0 : void MicrotaskQueue::OnCompleted(Isolate* isolate) {
     252             :   // TODO(marja): (spec) The discussion about when to clear the KeepDuringJob
     253             :   // set is still open (whether to clear it after every microtask or once
     254             :   // during a microtask checkpoint). See also
     255             :   // https://github.com/tc39/proposal-weakrefs/issues/39 .
     256      121391 :   isolate->heap()->ClearKeepDuringJobSet();
     257             : 
     258      121389 :   FireMicrotasksCompletedCallback(isolate);
     259           0 : }
     260             : 
     261        3239 : void MicrotaskQueue::ResizeBuffer(intptr_t new_capacity) {
     262             :   DCHECK_LE(size_, new_capacity);
     263        3239 :   Address* new_ring_buffer = new Address[new_capacity];
     264     1072613 :   for (intptr_t i = 0; i < size_; ++i) {
     265      534687 :     new_ring_buffer[i] = ring_buffer_[(start_ + i) % capacity_];
     266             :   }
     267             : 
     268        3239 :   delete[] ring_buffer_;
     269        3239 :   ring_buffer_ = new_ring_buffer;
     270        3239 :   capacity_ = new_capacity;
     271        3239 :   start_ = 0;
     272        3239 : }
     273             : 
     274             : }  // namespace internal
     275      122036 : }  // namespace v8

Generated by: LCOV version 1.10