LCOV - code coverage report
Current view: top level - src - microtask-queue.cc (source / functions) Hit Total Coverage
Test: app.info Lines: 103 109 94.5 %
Date: 2019-03-21 Functions: 16 18 88.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-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       61534 : void MicrotaskQueue::SetUpDefaultMicrotaskQueue(Isolate* isolate) {
      35             :   DCHECK_NULL(isolate->default_microtask_queue());
      36             : 
      37       61534 :   MicrotaskQueue* microtask_queue = new MicrotaskQueue;
      38       61534 :   microtask_queue->next_ = microtask_queue;
      39       61534 :   microtask_queue->prev_ = microtask_queue;
      40             :   isolate->set_default_microtask_queue(microtask_queue);
      41       61534 : }
      42             : 
      43             : // static
      44          12 : std::unique_ptr<MicrotaskQueue> MicrotaskQueue::New(Isolate* isolate) {
      45             :   DCHECK_NOT_NULL(isolate->default_microtask_queue());
      46             : 
      47          12 :   std::unique_ptr<MicrotaskQueue> microtask_queue(new MicrotaskQueue);
      48             : 
      49             :   // Insert the new instance to the next of last MicrotaskQueue instance.
      50          12 :   MicrotaskQueue* last = isolate->default_microtask_queue()->prev_;
      51          12 :   microtask_queue->next_ = last->next_;
      52          12 :   microtask_queue->prev_ = last;
      53          12 :   last->next_->prev_ = microtask_queue.get();
      54          12 :   last->next_ = microtask_queue.get();
      55             : 
      56          12 :   return microtask_queue;
      57             : }
      58             : 
      59             : MicrotaskQueue::MicrotaskQueue() = default;
      60             : 
      61      184587 : MicrotaskQueue::~MicrotaskQueue() {
      62       61529 :   if (next_ != this) {
      63             :     DCHECK_NE(prev_, this);
      64          12 :     next_->prev_ = prev_;
      65          12 :     prev_->next_ = next_;
      66             :   }
      67       61529 :   delete[] ring_buffer_;
      68      123058 : }
      69             : 
      70             : // static
      71        2686 : Address MicrotaskQueue::CallEnqueueMicrotask(Isolate* isolate,
      72             :                                              intptr_t microtask_queue_pointer,
      73             :                                              Address raw_microtask) {
      74        2686 :   Microtask microtask = Microtask::cast(Object(raw_microtask));
      75             :   reinterpret_cast<MicrotaskQueue*>(microtask_queue_pointer)
      76        2686 :       ->EnqueueMicrotask(microtask);
      77        2686 :   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       13859 : void MicrotaskQueue::EnqueueMicrotask(Microtask microtask) {
     101       13859 :   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        6264 :     intptr_t new_capacity = std::max(kMinimumCapacity, capacity_ << 1);
     105        3132 :     ResizeBuffer(new_capacity);
     106             :   }
     107             : 
     108             :   DCHECK_LT(size_, capacity_);
     109       27718 :   ring_buffer_[(start_ + size_) % capacity_] = microtask.ptr();
     110       13859 :   ++size_;
     111       13859 : }
     112             : 
     113      124075 : void MicrotaskQueue::PerformCheckpoint(v8::Isolate* v8_isolate) {
     114      124075 :   if (!IsRunningMicrotasks() && !GetMicrotasksScopeDepth() &&
     115             :       !HasMicrotasksSuppressions()) {
     116             :     Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
     117      122321 :     RunMicrotasks(isolate);
     118             :   }
     119      124075 : }
     120             : 
     121             : namespace {
     122             : 
     123             : class SetIsRunningMicrotasks {
     124             :  public:
     125             :   explicit SetIsRunningMicrotasks(bool* flag) : flag_(flag) {
     126             :     DCHECK(!*flag_);
     127       37785 :     *flag_ = true;
     128             :   }
     129             : 
     130             :   ~SetIsRunningMicrotasks() {
     131             :     DCHECK(*flag_);
     132       37785 :     *flag_ = false;
     133             :   }
     134             : 
     135             :  private:
     136             :   bool* flag_;
     137             : };
     138             : 
     139             : }  // namespace
     140             : 
     141      124447 : int MicrotaskQueue::RunMicrotasks(Isolate* isolate) {
     142      124447 :   if (!size()) {
     143             :     OnCompleted(isolate);
     144       86660 :     return 0;
     145             :   }
     146             : 
     147       37785 :   intptr_t base_count = finished_microtask_count_;
     148             : 
     149             :   HandleScope handle_scope(isolate);
     150       37785 :   MaybeHandle<Object> maybe_exception;
     151             : 
     152             :   MaybeHandle<Object> maybe_result;
     153             : 
     154             :   int processed_microtask_count;
     155             :   {
     156       37785 :     SetIsRunningMicrotasks scope(&is_running_microtasks_);
     157             :     v8::Isolate::SuppressMicrotaskExecutionScope suppress(
     158       75570 :         reinterpret_cast<v8::Isolate*>(isolate));
     159             :     HandleScopeImplementer::EnteredContextRewindScope rewind_scope(
     160             :         isolate->handle_scope_implementer());
     161       75570 :     TRACE_EVENT_BEGIN0("v8.execute", "RunMicrotasks");
     162       75570 :     TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.RunMicrotasks");
     163       37785 :     maybe_result = Execution::TryRunMicrotasks(isolate, this, &maybe_exception);
     164             :     processed_microtask_count =
     165       37785 :         static_cast<int>(finished_microtask_count_ - base_count);
     166       75570 :     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       37805 :   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       37765 :   return processed_microtask_count;
     185             : }
     186             : 
     187      276891 : void MicrotaskQueue::IterateMicrotasks(RootVisitor* visitor) {
     188      276891 :   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        1530 :     visitor->VisitRootPointers(
     192             :         Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_ + start_),
     193        2040 :         FullObjectSlot(ring_buffer_ + std::min(start_ + size_, capacity_)));
     194        1020 :     visitor->VisitRootPointers(
     195             :         Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_),
     196        1530 :         FullObjectSlot(ring_buffer_ + std::max(start_ + size_ - capacity_,
     197        2550 :                                                static_cast<intptr_t>(0))));
     198             :   }
     199             : 
     200      276891 :   if (capacity_ <= kMinimumCapacity) {
     201      276783 :     return;
     202             :   }
     203             : 
     204         108 :   intptr_t new_capacity = capacity_;
     205         890 :   while (new_capacity > 2 * size_) {
     206         391 :     new_capacity >>= 1;
     207             :   }
     208         108 :   new_capacity = std::max(new_capacity, kMinimumCapacity);
     209         108 :   if (new_capacity < capacity_) {
     210          47 :     ResizeBuffer(new_capacity);
     211             :   }
     212             : }
     213             : 
     214          45 : void MicrotaskQueue::AddMicrotasksCompletedCallback(
     215             :     MicrotasksCompletedCallbackWithData callback, void* data) {
     216             :   CallbackWithData callback_with_data(callback, data);
     217             :   auto pos =
     218             :       std::find(microtasks_completed_callbacks_.begin(),
     219          45 :                 microtasks_completed_callbacks_.end(), callback_with_data);
     220          45 :   if (pos != microtasks_completed_callbacks_.end()) return;
     221          45 :   microtasks_completed_callbacks_.push_back(callback_with_data);
     222             : }
     223             : 
     224        3717 : void MicrotaskQueue::RemoveMicrotasksCompletedCallback(
     225             :     MicrotasksCompletedCallbackWithData callback, void* data) {
     226             :   CallbackWithData callback_with_data(callback, data);
     227             :   auto pos =
     228             :       std::find(microtasks_completed_callbacks_.begin(),
     229        3717 :                 microtasks_completed_callbacks_.end(), callback_with_data);
     230        7389 :   if (pos == microtasks_completed_callbacks_.end()) return;
     231             :   microtasks_completed_callbacks_.erase(pos);
     232             : }
     233             : 
     234      124444 : void MicrotaskQueue::FireMicrotasksCompletedCallback(Isolate* isolate) const {
     235      124444 :   std::vector<CallbackWithData> callbacks(microtasks_completed_callbacks_);
     236      124494 :   for (auto& callback : callbacks) {
     237          50 :     callback.first(reinterpret_cast<v8::Isolate*>(isolate), callback.second);
     238             :   }
     239      124444 : }
     240             : 
     241           4 : Microtask MicrotaskQueue::get(intptr_t index) const {
     242             :   DCHECK_LT(index, size_);
     243           4 :   Object microtask(ring_buffer_[(index + start_) % capacity_]);
     244           4 :   return Microtask::cast(microtask);
     245             : }
     246             : 
     247           0 : void MicrotaskQueue::OnCompleted(Isolate* isolate) {
     248             :   // TODO(marja): (spec) The discussion about when to clear the KeepDuringJob
     249             :   // set is still open (whether to clear it after every microtask or once
     250             :   // during a microtask checkpoint). See also
     251             :   // https://github.com/tc39/proposal-weakrefs/issues/39 .
     252      124447 :   isolate->heap()->ClearKeepDuringJobSet();
     253             : 
     254      124446 :   FireMicrotasksCompletedCallback(isolate);
     255           0 : }
     256             : 
     257        3179 : void MicrotaskQueue::ResizeBuffer(intptr_t new_capacity) {
     258             :   DCHECK_LE(size_, new_capacity);
     259        3179 :   Address* new_ring_buffer = new Address[new_capacity];
     260     1203665 :   for (intptr_t i = 0; i < size_; ++i) {
     261      600243 :     new_ring_buffer[i] = ring_buffer_[(start_ + i) % capacity_];
     262             :   }
     263             : 
     264        3179 :   delete[] ring_buffer_;
     265        3179 :   ring_buffer_ = new_ring_buffer;
     266        3179 :   capacity_ = new_capacity;
     267        3179 :   start_ = 0;
     268        3179 : }
     269             : 
     270             : }  // namespace internal
     271      120216 : }  // namespace v8

Generated by: LCOV version 1.10