11cb0ef41Sopenharmony_ci// Copyright 2018 the V8 project authors. All rights reserved.
21cb0ef41Sopenharmony_ci// Use of this source code is governed by a BSD-style license that can be
31cb0ef41Sopenharmony_ci// found in the LICENSE file.
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ci#include "src/execution/microtask-queue.h"
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ci#include <algorithm>
81cb0ef41Sopenharmony_ci#include <cstddef>
91cb0ef41Sopenharmony_ci
101cb0ef41Sopenharmony_ci#include "src/api/api-inl.h"
111cb0ef41Sopenharmony_ci#include "src/base/logging.h"
121cb0ef41Sopenharmony_ci#include "src/execution/isolate.h"
131cb0ef41Sopenharmony_ci#include "src/handles/handles-inl.h"
141cb0ef41Sopenharmony_ci#include "src/objects/microtask-inl.h"
151cb0ef41Sopenharmony_ci#include "src/objects/visitors.h"
161cb0ef41Sopenharmony_ci#include "src/roots/roots-inl.h"
171cb0ef41Sopenharmony_ci#include "src/tracing/trace-event.h"
181cb0ef41Sopenharmony_ci
191cb0ef41Sopenharmony_cinamespace v8 {
201cb0ef41Sopenharmony_cinamespace internal {
211cb0ef41Sopenharmony_ci
221cb0ef41Sopenharmony_ciconst size_t MicrotaskQueue::kRingBufferOffset =
231cb0ef41Sopenharmony_ci    OFFSET_OF(MicrotaskQueue, ring_buffer_);
241cb0ef41Sopenharmony_ciconst size_t MicrotaskQueue::kCapacityOffset =
251cb0ef41Sopenharmony_ci    OFFSET_OF(MicrotaskQueue, capacity_);
261cb0ef41Sopenharmony_ciconst size_t MicrotaskQueue::kSizeOffset = OFFSET_OF(MicrotaskQueue, size_);
271cb0ef41Sopenharmony_ciconst size_t MicrotaskQueue::kStartOffset = OFFSET_OF(MicrotaskQueue, start_);
281cb0ef41Sopenharmony_ciconst size_t MicrotaskQueue::kFinishedMicrotaskCountOffset =
291cb0ef41Sopenharmony_ci    OFFSET_OF(MicrotaskQueue, finished_microtask_count_);
301cb0ef41Sopenharmony_ci
311cb0ef41Sopenharmony_ciconst intptr_t MicrotaskQueue::kMinimumCapacity = 8;
321cb0ef41Sopenharmony_ci
331cb0ef41Sopenharmony_ci// static
341cb0ef41Sopenharmony_civoid MicrotaskQueue::SetUpDefaultMicrotaskQueue(Isolate* isolate) {
351cb0ef41Sopenharmony_ci  DCHECK_NULL(isolate->default_microtask_queue());
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci  MicrotaskQueue* microtask_queue = new MicrotaskQueue;
381cb0ef41Sopenharmony_ci  microtask_queue->next_ = microtask_queue;
391cb0ef41Sopenharmony_ci  microtask_queue->prev_ = microtask_queue;
401cb0ef41Sopenharmony_ci  isolate->set_default_microtask_queue(microtask_queue);
411cb0ef41Sopenharmony_ci}
421cb0ef41Sopenharmony_ci
431cb0ef41Sopenharmony_ci// static
441cb0ef41Sopenharmony_cistd::unique_ptr<MicrotaskQueue> MicrotaskQueue::New(Isolate* isolate) {
451cb0ef41Sopenharmony_ci  DCHECK_NOT_NULL(isolate->default_microtask_queue());
461cb0ef41Sopenharmony_ci
471cb0ef41Sopenharmony_ci  std::unique_ptr<MicrotaskQueue> microtask_queue(new MicrotaskQueue);
481cb0ef41Sopenharmony_ci
491cb0ef41Sopenharmony_ci  // Insert the new instance to the next of last MicrotaskQueue instance.
501cb0ef41Sopenharmony_ci  MicrotaskQueue* last = isolate->default_microtask_queue()->prev_;
511cb0ef41Sopenharmony_ci  microtask_queue->next_ = last->next_;
521cb0ef41Sopenharmony_ci  microtask_queue->prev_ = last;
531cb0ef41Sopenharmony_ci  last->next_->prev_ = microtask_queue.get();
541cb0ef41Sopenharmony_ci  last->next_ = microtask_queue.get();
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci  return microtask_queue;
571cb0ef41Sopenharmony_ci}
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ciMicrotaskQueue::MicrotaskQueue() = default;
601cb0ef41Sopenharmony_ci
611cb0ef41Sopenharmony_ciMicrotaskQueue::~MicrotaskQueue() {
621cb0ef41Sopenharmony_ci  if (next_ != this) {
631cb0ef41Sopenharmony_ci    DCHECK_NE(prev_, this);
641cb0ef41Sopenharmony_ci    next_->prev_ = prev_;
651cb0ef41Sopenharmony_ci    prev_->next_ = next_;
661cb0ef41Sopenharmony_ci  }
671cb0ef41Sopenharmony_ci  delete[] ring_buffer_;
681cb0ef41Sopenharmony_ci}
691cb0ef41Sopenharmony_ci
701cb0ef41Sopenharmony_ci// static
711cb0ef41Sopenharmony_ciAddress MicrotaskQueue::CallEnqueueMicrotask(Isolate* isolate,
721cb0ef41Sopenharmony_ci                                             intptr_t microtask_queue_pointer,
731cb0ef41Sopenharmony_ci                                             Address raw_microtask) {
741cb0ef41Sopenharmony_ci  Microtask microtask = Microtask::cast(Object(raw_microtask));
751cb0ef41Sopenharmony_ci  reinterpret_cast<MicrotaskQueue*>(microtask_queue_pointer)
761cb0ef41Sopenharmony_ci      ->EnqueueMicrotask(microtask);
771cb0ef41Sopenharmony_ci  return Smi::zero().ptr();
781cb0ef41Sopenharmony_ci}
791cb0ef41Sopenharmony_ci
801cb0ef41Sopenharmony_civoid MicrotaskQueue::EnqueueMicrotask(v8::Isolate* v8_isolate,
811cb0ef41Sopenharmony_ci                                      v8::Local<Function> function) {
821cb0ef41Sopenharmony_ci  Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
831cb0ef41Sopenharmony_ci  HandleScope scope(isolate);
841cb0ef41Sopenharmony_ci  Handle<CallableTask> microtask = isolate->factory()->NewCallableTask(
851cb0ef41Sopenharmony_ci      Utils::OpenHandle(*function), isolate->native_context());
861cb0ef41Sopenharmony_ci  EnqueueMicrotask(*microtask);
871cb0ef41Sopenharmony_ci}
881cb0ef41Sopenharmony_ci
891cb0ef41Sopenharmony_civoid MicrotaskQueue::EnqueueMicrotask(v8::Isolate* v8_isolate,
901cb0ef41Sopenharmony_ci                                      v8::MicrotaskCallback callback,
911cb0ef41Sopenharmony_ci                                      void* data) {
921cb0ef41Sopenharmony_ci  Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
931cb0ef41Sopenharmony_ci  HandleScope scope(isolate);
941cb0ef41Sopenharmony_ci  Handle<CallbackTask> microtask = isolate->factory()->NewCallbackTask(
951cb0ef41Sopenharmony_ci      isolate->factory()->NewForeign(reinterpret_cast<Address>(callback)),
961cb0ef41Sopenharmony_ci      isolate->factory()->NewForeign(reinterpret_cast<Address>(data)));
971cb0ef41Sopenharmony_ci  EnqueueMicrotask(*microtask);
981cb0ef41Sopenharmony_ci}
991cb0ef41Sopenharmony_ci
1001cb0ef41Sopenharmony_civoid MicrotaskQueue::EnqueueMicrotask(Microtask microtask) {
1011cb0ef41Sopenharmony_ci  if (size_ == capacity_) {
1021cb0ef41Sopenharmony_ci    // Keep the capacity of |ring_buffer_| power of 2, so that the JIT
1031cb0ef41Sopenharmony_ci    // implementation can calculate the modulo easily.
1041cb0ef41Sopenharmony_ci    intptr_t new_capacity = std::max(kMinimumCapacity, capacity_ << 1);
1051cb0ef41Sopenharmony_ci    ResizeBuffer(new_capacity);
1061cb0ef41Sopenharmony_ci  }
1071cb0ef41Sopenharmony_ci
1081cb0ef41Sopenharmony_ci  DCHECK_LT(size_, capacity_);
1091cb0ef41Sopenharmony_ci  ring_buffer_[(start_ + size_) % capacity_] = microtask.ptr();
1101cb0ef41Sopenharmony_ci  ++size_;
1111cb0ef41Sopenharmony_ci}
1121cb0ef41Sopenharmony_ci
1131cb0ef41Sopenharmony_civoid MicrotaskQueue::PerformCheckpointInternal(v8::Isolate* v8_isolate) {
1141cb0ef41Sopenharmony_ci  DCHECK(ShouldPerfomCheckpoint());
1151cb0ef41Sopenharmony_ci  std::unique_ptr<MicrotasksScope> microtasks_scope;
1161cb0ef41Sopenharmony_ci  if (microtasks_policy_ == v8::MicrotasksPolicy::kScoped) {
1171cb0ef41Sopenharmony_ci    // If we're using microtask scopes to schedule microtask execution, V8
1181cb0ef41Sopenharmony_ci    // API calls will check that there's always a microtask scope on the
1191cb0ef41Sopenharmony_ci    // stack. As the microtasks we're about to execute could invoke embedder
1201cb0ef41Sopenharmony_ci    // callbacks which then calls back into V8, we create an artificial
1211cb0ef41Sopenharmony_ci    // microtask scope here to avoid running into the CallDepthScope check.
1221cb0ef41Sopenharmony_ci    microtasks_scope.reset(new v8::MicrotasksScope(
1231cb0ef41Sopenharmony_ci        v8_isolate, this, v8::MicrotasksScope::kDoNotRunMicrotasks));
1241cb0ef41Sopenharmony_ci  }
1251cb0ef41Sopenharmony_ci  Isolate* isolate = reinterpret_cast<Isolate*>(v8_isolate);
1261cb0ef41Sopenharmony_ci  RunMicrotasks(isolate);
1271cb0ef41Sopenharmony_ci  isolate->ClearKeptObjects();
1281cb0ef41Sopenharmony_ci}
1291cb0ef41Sopenharmony_ci
1301cb0ef41Sopenharmony_cinamespace {
1311cb0ef41Sopenharmony_ci
1321cb0ef41Sopenharmony_ciclass SetIsRunningMicrotasks {
1331cb0ef41Sopenharmony_ci public:
1341cb0ef41Sopenharmony_ci  explicit SetIsRunningMicrotasks(bool* flag) : flag_(flag) {
1351cb0ef41Sopenharmony_ci    DCHECK(!*flag_);
1361cb0ef41Sopenharmony_ci    *flag_ = true;
1371cb0ef41Sopenharmony_ci  }
1381cb0ef41Sopenharmony_ci
1391cb0ef41Sopenharmony_ci  ~SetIsRunningMicrotasks() {
1401cb0ef41Sopenharmony_ci    DCHECK(*flag_);
1411cb0ef41Sopenharmony_ci    *flag_ = false;
1421cb0ef41Sopenharmony_ci  }
1431cb0ef41Sopenharmony_ci
1441cb0ef41Sopenharmony_ci private:
1451cb0ef41Sopenharmony_ci  bool* flag_;
1461cb0ef41Sopenharmony_ci};
1471cb0ef41Sopenharmony_ci
1481cb0ef41Sopenharmony_ci}  // namespace
1491cb0ef41Sopenharmony_ci
1501cb0ef41Sopenharmony_ciint MicrotaskQueue::RunMicrotasks(Isolate* isolate) {
1511cb0ef41Sopenharmony_ci  if (!size()) {
1521cb0ef41Sopenharmony_ci    OnCompleted(isolate);
1531cb0ef41Sopenharmony_ci    return 0;
1541cb0ef41Sopenharmony_ci  }
1551cb0ef41Sopenharmony_ci
1561cb0ef41Sopenharmony_ci  intptr_t base_count = finished_microtask_count_;
1571cb0ef41Sopenharmony_ci
1581cb0ef41Sopenharmony_ci  HandleScope handle_scope(isolate);
1591cb0ef41Sopenharmony_ci  MaybeHandle<Object> maybe_exception;
1601cb0ef41Sopenharmony_ci
1611cb0ef41Sopenharmony_ci  MaybeHandle<Object> maybe_result;
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci  int processed_microtask_count;
1641cb0ef41Sopenharmony_ci  {
1651cb0ef41Sopenharmony_ci    SetIsRunningMicrotasks scope(&is_running_microtasks_);
1661cb0ef41Sopenharmony_ci    v8::Isolate::SuppressMicrotaskExecutionScope suppress(
1671cb0ef41Sopenharmony_ci        reinterpret_cast<v8::Isolate*>(isolate));
1681cb0ef41Sopenharmony_ci    HandleScopeImplementer::EnteredContextRewindScope rewind_scope(
1691cb0ef41Sopenharmony_ci        isolate->handle_scope_implementer());
1701cb0ef41Sopenharmony_ci    TRACE_EVENT_BEGIN0("v8.execute", "RunMicrotasks");
1711cb0ef41Sopenharmony_ci    {
1721cb0ef41Sopenharmony_ci      TRACE_EVENT_CALL_STATS_SCOPED(isolate, "v8", "V8.RunMicrotasks");
1731cb0ef41Sopenharmony_ci      maybe_result = Execution::TryRunMicrotasks(isolate, this,
1741cb0ef41Sopenharmony_ci                                                 &maybe_exception);
1751cb0ef41Sopenharmony_ci      processed_microtask_count =
1761cb0ef41Sopenharmony_ci          static_cast<int>(finished_microtask_count_ - base_count);
1771cb0ef41Sopenharmony_ci    }
1781cb0ef41Sopenharmony_ci    TRACE_EVENT_END1("v8.execute", "RunMicrotasks", "microtask_count",
1791cb0ef41Sopenharmony_ci                     processed_microtask_count);
1801cb0ef41Sopenharmony_ci  }
1811cb0ef41Sopenharmony_ci
1821cb0ef41Sopenharmony_ci  // If execution is terminating, clean up and propagate that to TryCatch scope.
1831cb0ef41Sopenharmony_ci  if (maybe_result.is_null() && maybe_exception.is_null()) {
1841cb0ef41Sopenharmony_ci    delete[] ring_buffer_;
1851cb0ef41Sopenharmony_ci    ring_buffer_ = nullptr;
1861cb0ef41Sopenharmony_ci    capacity_ = 0;
1871cb0ef41Sopenharmony_ci    size_ = 0;
1881cb0ef41Sopenharmony_ci    start_ = 0;
1891cb0ef41Sopenharmony_ci    DCHECK(isolate->has_scheduled_exception());
1901cb0ef41Sopenharmony_ci    isolate->OnTerminationDuringRunMicrotasks();
1911cb0ef41Sopenharmony_ci    OnCompleted(isolate);
1921cb0ef41Sopenharmony_ci    return -1;
1931cb0ef41Sopenharmony_ci  }
1941cb0ef41Sopenharmony_ci  DCHECK_EQ(0, size());
1951cb0ef41Sopenharmony_ci  OnCompleted(isolate);
1961cb0ef41Sopenharmony_ci
1971cb0ef41Sopenharmony_ci  return processed_microtask_count;
1981cb0ef41Sopenharmony_ci}
1991cb0ef41Sopenharmony_ci
2001cb0ef41Sopenharmony_civoid MicrotaskQueue::IterateMicrotasks(RootVisitor* visitor) {
2011cb0ef41Sopenharmony_ci  if (size_) {
2021cb0ef41Sopenharmony_ci    // Iterate pending Microtasks as root objects to avoid the write barrier for
2031cb0ef41Sopenharmony_ci    // all single Microtask. If this hurts the GC performance, use a FixedArray.
2041cb0ef41Sopenharmony_ci    visitor->VisitRootPointers(
2051cb0ef41Sopenharmony_ci        Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_ + start_),
2061cb0ef41Sopenharmony_ci        FullObjectSlot(ring_buffer_ + std::min(start_ + size_, capacity_)));
2071cb0ef41Sopenharmony_ci    visitor->VisitRootPointers(
2081cb0ef41Sopenharmony_ci        Root::kStrongRoots, nullptr, FullObjectSlot(ring_buffer_),
2091cb0ef41Sopenharmony_ci        FullObjectSlot(ring_buffer_ + std::max(start_ + size_ - capacity_,
2101cb0ef41Sopenharmony_ci                                               static_cast<intptr_t>(0))));
2111cb0ef41Sopenharmony_ci  }
2121cb0ef41Sopenharmony_ci
2131cb0ef41Sopenharmony_ci  if (capacity_ <= kMinimumCapacity) {
2141cb0ef41Sopenharmony_ci    return;
2151cb0ef41Sopenharmony_ci  }
2161cb0ef41Sopenharmony_ci
2171cb0ef41Sopenharmony_ci  intptr_t new_capacity = capacity_;
2181cb0ef41Sopenharmony_ci  while (new_capacity > 2 * size_) {
2191cb0ef41Sopenharmony_ci    new_capacity >>= 1;
2201cb0ef41Sopenharmony_ci  }
2211cb0ef41Sopenharmony_ci  new_capacity = std::max(new_capacity, kMinimumCapacity);
2221cb0ef41Sopenharmony_ci  if (new_capacity < capacity_) {
2231cb0ef41Sopenharmony_ci    ResizeBuffer(new_capacity);
2241cb0ef41Sopenharmony_ci  }
2251cb0ef41Sopenharmony_ci}
2261cb0ef41Sopenharmony_ci
2271cb0ef41Sopenharmony_civoid MicrotaskQueue::AddMicrotasksCompletedCallback(
2281cb0ef41Sopenharmony_ci    MicrotasksCompletedCallbackWithData callback, void* data) {
2291cb0ef41Sopenharmony_ci  CallbackWithData callback_with_data(callback, data);
2301cb0ef41Sopenharmony_ci  auto pos =
2311cb0ef41Sopenharmony_ci      std::find(microtasks_completed_callbacks_.begin(),
2321cb0ef41Sopenharmony_ci                microtasks_completed_callbacks_.end(), callback_with_data);
2331cb0ef41Sopenharmony_ci  if (pos != microtasks_completed_callbacks_.end()) return;
2341cb0ef41Sopenharmony_ci  microtasks_completed_callbacks_.push_back(callback_with_data);
2351cb0ef41Sopenharmony_ci}
2361cb0ef41Sopenharmony_ci
2371cb0ef41Sopenharmony_civoid MicrotaskQueue::RemoveMicrotasksCompletedCallback(
2381cb0ef41Sopenharmony_ci    MicrotasksCompletedCallbackWithData callback, void* data) {
2391cb0ef41Sopenharmony_ci  CallbackWithData callback_with_data(callback, data);
2401cb0ef41Sopenharmony_ci  auto pos =
2411cb0ef41Sopenharmony_ci      std::find(microtasks_completed_callbacks_.begin(),
2421cb0ef41Sopenharmony_ci                microtasks_completed_callbacks_.end(), callback_with_data);
2431cb0ef41Sopenharmony_ci  if (pos == microtasks_completed_callbacks_.end()) return;
2441cb0ef41Sopenharmony_ci  microtasks_completed_callbacks_.erase(pos);
2451cb0ef41Sopenharmony_ci}
2461cb0ef41Sopenharmony_ci
2471cb0ef41Sopenharmony_civoid MicrotaskQueue::OnCompleted(Isolate* isolate) const {
2481cb0ef41Sopenharmony_ci  std::vector<CallbackWithData> callbacks(microtasks_completed_callbacks_);
2491cb0ef41Sopenharmony_ci  for (auto& callback : callbacks) {
2501cb0ef41Sopenharmony_ci    callback.first(reinterpret_cast<v8::Isolate*>(isolate), callback.second);
2511cb0ef41Sopenharmony_ci  }
2521cb0ef41Sopenharmony_ci}
2531cb0ef41Sopenharmony_ci
2541cb0ef41Sopenharmony_ciMicrotask MicrotaskQueue::get(intptr_t index) const {
2551cb0ef41Sopenharmony_ci  DCHECK_LT(index, size_);
2561cb0ef41Sopenharmony_ci  Object microtask(ring_buffer_[(index + start_) % capacity_]);
2571cb0ef41Sopenharmony_ci  return Microtask::cast(microtask);
2581cb0ef41Sopenharmony_ci}
2591cb0ef41Sopenharmony_ci
2601cb0ef41Sopenharmony_civoid MicrotaskQueue::ResizeBuffer(intptr_t new_capacity) {
2611cb0ef41Sopenharmony_ci  DCHECK_LE(size_, new_capacity);
2621cb0ef41Sopenharmony_ci  Address* new_ring_buffer = new Address[new_capacity];
2631cb0ef41Sopenharmony_ci  for (intptr_t i = 0; i < size_; ++i) {
2641cb0ef41Sopenharmony_ci    new_ring_buffer[i] = ring_buffer_[(start_ + i) % capacity_];
2651cb0ef41Sopenharmony_ci  }
2661cb0ef41Sopenharmony_ci
2671cb0ef41Sopenharmony_ci  delete[] ring_buffer_;
2681cb0ef41Sopenharmony_ci  ring_buffer_ = new_ring_buffer;
2691cb0ef41Sopenharmony_ci  capacity_ = new_capacity;
2701cb0ef41Sopenharmony_ci  start_ = 0;
2711cb0ef41Sopenharmony_ci}
2721cb0ef41Sopenharmony_ci
2731cb0ef41Sopenharmony_ci}  // namespace internal
2741cb0ef41Sopenharmony_ci}  // namespace v8
275