LCOV - code coverage report
Current view: top level - src - microtask-queue.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 89 91 97.8 %
Date: 2019-02-19 Functions: 13 14 92.9 %

          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.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       61048 : void MicrotaskQueue::SetUpDefaultMicrotaskQueue(Isolate* isolate) {
      35             :   DCHECK_NULL(isolate->default_microtask_queue());
      36             : 
      37       61048 :   MicrotaskQueue* microtask_queue = new MicrotaskQueue;
      38       61049 :   microtask_queue->next_ = microtask_queue;
      39       61049 :   microtask_queue->prev_ = microtask_queue;
      40             :   isolate->set_default_microtask_queue(microtask_queue);
      41       61049 : }
      42             : 
      43             : // static
      44          12 : std::unique_ptr<MicrotaskQueue> MicrotaskQueue::New(Isolate* isolate) {
      45             :   DCHECK_NOT_NULL(isolate->default_microtask_queue());
      46             : 
      47           6 :   std::unique_ptr<MicrotaskQueue> microtask_queue(new MicrotaskQueue);
      48             : 
      49             :   // Insert the new instance to the next of last MicrotaskQueue instance.
      50           6 :   MicrotaskQueue* last = isolate->default_microtask_queue()->prev_;
      51           6 :   microtask_queue->next_ = last->next_;
      52           6 :   microtask_queue->prev_ = last;
      53          12 :   last->next_->prev_ = microtask_queue.get();
      54           6 :   last->next_ = microtask_queue.get();
      55             : 
      56           6 :   return microtask_queue;
      57             : }
      58             : 
      59             : MicrotaskQueue::MicrotaskQueue() = default;
      60             : 
      61       61040 : MicrotaskQueue::~MicrotaskQueue() {
      62       61040 :   if (next_ != this) {
      63             :     DCHECK_NE(prev_, this);
      64           6 :     next_->prev_ = prev_;
      65           6 :     prev_->next_ = next_;
      66             :   }
      67       61040 :   delete[] ring_buffer_;
      68       61040 : }
      69             : 
      70             : // static
      71        2654 : Address MicrotaskQueue::CallEnqueueMicrotask(Isolate* isolate,
      72             :                                              intptr_t microtask_queue_pointer,
      73             :                                              Address raw_microtask) {
      74        2654 :   Microtask microtask = Microtask::cast(Object(raw_microtask));
      75             :   reinterpret_cast<MicrotaskQueue*>(microtask_queue_pointer)
      76        2654 :       ->EnqueueMicrotask(microtask);
      77        2654 :   return ReadOnlyRoots(isolate).undefined_value().ptr();
      78             : }
      79             : 
      80       14090 : void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) {
      81       14090 :   if (size_ == capacity_) {
      82             :     // Keep the capacity of |ring_buffer_| power of 2, so that the JIT
      83             :     // implementation can calculate the modulo easily.
      84        6198 :     intptr_t new_capacity = std::max(kMinimumCapacity, capacity_ << 1);
      85        3099 :     ResizeBuffer(new_capacity);
      86             :   }
      87             : 
      88             :   DCHECK_LT(size_, capacity_);
      89       28180 :   ring_buffer_[(start_ + size_) % capacity_] = microtask.ptr();
      90       14090 :   ++size_;
      91       14090 : }
      92             : 
      93             : namespace {
      94             : 
      95             : class SetIsRunningMicrotasks {
      96             :  public:
      97             :   explicit SetIsRunningMicrotasks(bool* flag) : flag_(flag) {
      98             :     DCHECK(!*flag_);
      99       51534 :     *flag_ = true;
     100             :   }
     101             : 
     102             :   ~SetIsRunningMicrotasks() {
     103             :     DCHECK(*flag_);
     104       51534 :     *flag_ = false;
     105             :   }
     106             : 
     107             :  private:
     108             :   bool* flag_;
     109             : };
     110             : 
     111             : }  // namespace
     112             : 
     113      193279 : int MicrotaskQueue::RunMicrotasks(Isolate* isolate) {
     114      141745 :   if (!size()) {
     115             :     OnCompleted(isolate);
     116       90209 :     return 0;
     117             :   }
     118             : 
     119       51534 :   intptr_t base_count = finished_microtask_count_;
     120             : 
     121             :   HandleScope handle_scope(isolate);
     122       51534 :   MaybeHandle<Object> maybe_exception;
     123             : 
     124             :   MaybeHandle<Object> maybe_result;
     125             : 
     126             :   int processed_microtask_count;
     127             :   {
     128       51534 :     SetIsRunningMicrotasks scope(&is_running_microtasks_);
     129             :     v8::Isolate::SuppressMicrotaskExecutionScope suppress(
     130      103068 :         reinterpret_cast<v8::Isolate*>(isolate));
     131             :     HandleScopeImplementer::EnteredContextRewindScope rewind_scope(
     132             :         isolate->handle_scope_implementer());
     133      103068 :     TRACE_EVENT_BEGIN0("v8.execute", "RunMicrotasks");
     134      154602 :     TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.RunMicrotasks");
     135       51534 :     maybe_result = Execution::TryRunMicrotasks(isolate, this, &maybe_exception);
     136             :     processed_microtask_count =
     137       51534 :         static_cast<int>(finished_microtask_count_ - base_count);
     138      103068 :     TRACE_EVENT_END1("v8.execute", "RunMicrotasks", "microtask_count",
     139             :                      processed_microtask_count);
     140             :   }
     141             : 
     142             :   // If execution is terminating, clean up and propagate that to TryCatch scope.
     143       51554 :   if (maybe_result.is_null() && maybe_exception.is_null()) {
     144          20 :     delete[] ring_buffer_;
     145          20 :     ring_buffer_ = nullptr;
     146          20 :     capacity_ = 0;
     147          20 :     size_ = 0;
     148          20 :     start_ = 0;
     149          20 :     isolate->SetTerminationOnExternalTryCatch();
     150             :     OnCompleted(isolate);
     151          20 :     return -1;
     152             :   }
     153             :   DCHECK_EQ(0, size());
     154             :   OnCompleted(isolate);
     155             : 
     156       51514 :   return processed_microtask_count;
     157             : }
     158             : 
     159      280838 : void MicrotaskQueue::IterateMicrotasks(RootVisitor* visitor) {
     160      280838 :   if (size_) {
     161             :     // Iterate pending Microtasks as root objects to avoid the write barrier for
     162             :     // all single Microtask. If this hurts the GC performance, use a FixedArray.
     163             :     visitor->VisitRootPointers(
     164             :         Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_ + start_),
     165        2392 :         FullObjectSlot(ring_buffer_ + std::min(start_ + size_, capacity_)));
     166             :     visitor->VisitRootPointers(
     167             :         Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_),
     168         598 :         FullObjectSlot(ring_buffer_ + std::max(start_ + size_ - capacity_,
     169        2392 :                                                static_cast<intptr_t>(0))));
     170             :   }
     171             : 
     172      280838 :   if (capacity_ <= kMinimumCapacity) {
     173      280703 :     return;
     174             :   }
     175             : 
     176         135 :   intptr_t new_capacity = capacity_;
     177         676 :   while (new_capacity > 2 * size_) {
     178         406 :     new_capacity >>= 1;
     179             :   }
     180         135 :   new_capacity = std::max(new_capacity, kMinimumCapacity);
     181         135 :   if (new_capacity < capacity_) {
     182          50 :     ResizeBuffer(new_capacity);
     183             :   }
     184             : }
     185             : 
     186          45 : void MicrotaskQueue::AddMicrotasksCompletedCallback(
     187             :     MicrotasksCompletedCallback callback) {
     188             :   auto pos = std::find(microtasks_completed_callbacks_.begin(),
     189             :                        microtasks_completed_callbacks_.end(), callback);
     190          90 :   if (pos != microtasks_completed_callbacks_.end()) return;
     191          45 :   microtasks_completed_callbacks_.push_back(callback);
     192             : }
     193             : 
     194        3683 : void MicrotaskQueue::RemoveMicrotasksCompletedCallback(
     195             :     MicrotasksCompletedCallback callback) {
     196             :   auto pos = std::find(microtasks_completed_callbacks_.begin(),
     197             :                        microtasks_completed_callbacks_.end(), callback);
     198        7366 :   if (pos == microtasks_completed_callbacks_.end()) return;
     199          45 :   microtasks_completed_callbacks_.erase(pos);
     200             : }
     201             : 
     202      141742 : void MicrotaskQueue::FireMicrotasksCompletedCallback(Isolate* isolate) const {
     203             :   std::vector<MicrotasksCompletedCallback> callbacks(
     204      141742 :       microtasks_completed_callbacks_);
     205      283534 :   for (auto& callback : callbacks) {
     206          50 :     callback(reinterpret_cast<v8::Isolate*>(isolate));
     207             :   }
     208      141742 : }
     209             : 
     210           0 : void MicrotaskQueue::OnCompleted(Isolate* isolate) {
     211             :   // TODO(marja): (spec) The discussion about when to clear the KeepDuringJob
     212             :   // set is still open (whether to clear it after every microtask or once
     213             :   // during a microtask checkpoint). See also
     214             :   // https://github.com/tc39/proposal-weakrefs/issues/39 .
     215      141745 :   isolate->heap()->ClearKeepDuringJobSet();
     216             : 
     217      141744 :   FireMicrotasksCompletedCallback(isolate);
     218           0 : }
     219             : 
     220        3149 : void MicrotaskQueue::ResizeBuffer(intptr_t new_capacity) {
     221             :   DCHECK_LE(size_, new_capacity);
     222        3149 :   Address* new_ring_buffer = new Address[new_capacity];
     223      599119 :   for (intptr_t i = 0; i < size_; ++i) {
     224      595970 :     new_ring_buffer[i] = ring_buffer_[(start_ + i) % capacity_];
     225             :   }
     226             : 
     227        3149 :   delete[] ring_buffer_;
     228        3149 :   ring_buffer_ = new_ring_buffer;
     229        3149 :   capacity_ = new_capacity;
     230        3149 :   start_ = 0;
     231        3149 : }
     232             : 
     233             : }  // namespace internal
     234      178779 : }  // namespace v8

Generated by: LCOV version 1.10