Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions deps/v8/include/v8-microtask-queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,28 @@ class V8_EXPORT MicrotaskQueue {
*/
virtual int GetMicrotasksScopeDepth() const = 0;

/**
* Qualia: the bookkeeping that says "a checkpoint is in progress on this
* queue". V8 assumes the checkpoint runs to completion on one native stack;
* node-fibers can suspend a fiber mid-checkpoint, so it saves and restores
* this state per fiber on every switch.
*/
struct RunState {
bool is_running = false;
int scope_depth = 0;
int suppressions = 0;
// Set when a fiber is suspended mid-checkpoint: once it resumes, its drain
// loop stops after the current microtask (another stack has taken over).
bool stop_draining = false;
};
RunState GetRunState() const;
void SetRunState(const RunState& state);

/**
* Qualia: number of microtasks currently queued.
*/
size_t Size() const;

MicrotaskQueue(const MicrotaskQueue&) = delete;
MicrotaskQueue& operator=(const MicrotaskQueue&) = delete;

Expand Down
12 changes: 12 additions & 0 deletions deps/v8/src/api/api.cc
Original file line number Diff line number Diff line change
Expand Up @@ -9555,6 +9555,18 @@ std::unique_ptr<MicrotaskQueue> MicrotaskQueue::New(Isolate* isolate,
return ret;
}

MicrotaskQueue::RunState MicrotaskQueue::GetRunState() const {
return static_cast<const i::MicrotaskQueue*>(this)->GetInternalRunState();
}

void MicrotaskQueue::SetRunState(const RunState& state) {
static_cast<i::MicrotaskQueue*>(this)->SetInternalRunState(state);
}

size_t MicrotaskQueue::Size() const {
return static_cast<size_t>(static_cast<const i::MicrotaskQueue*>(this)->size());
}

MicrotasksScope::MicrotasksScope(Isolate* isolate, MicrotasksScope::Type type)
: MicrotasksScope(isolate, nullptr, type) {}

Expand Down
9 changes: 9 additions & 0 deletions deps/v8/src/builtins/builtins-microtask-queue-gen.cc
Original file line number Diff line number Diff line change
Expand Up @@ -597,6 +597,15 @@ TF_BUILTIN(RunMicrotasks, MicrotaskQueueBuiltinsAssembler) {
Goto(&loop);
BIND(&loop);

// Qualia: exit if this stack was suspended mid-drain and has since been
// resumed; another stack took over draining the queue.
GotoIf(WordNotEqual(
Load<IntPtrT>(microtask_queue,
IntPtrConstant(
MicrotaskQueue::kQualiaStopDrainingOffset)),
IntPtrConstant(0)),
&done);

TNode<IntPtrT> size = GetMicrotaskQueueSize(microtask_queue);

// Exit if the queue is empty.
Expand Down
6 changes: 5 additions & 1 deletion deps/v8/src/execution/microtask-queue.cc
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const size_t MicrotaskQueue::kCapacityOffset =
OFFSET_OF(MicrotaskQueue, capacity_);
const size_t MicrotaskQueue::kSizeOffset = OFFSET_OF(MicrotaskQueue, size_);
const size_t MicrotaskQueue::kStartOffset = OFFSET_OF(MicrotaskQueue, start_);
const size_t MicrotaskQueue::kQualiaStopDrainingOffset =
OFFSET_OF(MicrotaskQueue, qualia_stop_draining_);
const size_t MicrotaskQueue::kFinishedMicrotaskCountOffset =
OFFSET_OF(MicrotaskQueue, finished_microtask_count_);

Expand Down Expand Up @@ -174,6 +176,9 @@ int MicrotaskQueue::RunMicrotasks(Isolate* isolate) {
&maybe_exception);
processed_microtask_count =
static_cast<int>(finished_microtask_count_ - base_count);
// Qualia: the loop may have stopped early for a resumed fiber; the
// stack that took over the drain owns whatever is left.
qualia_stop_draining_ = 0;
}
TRACE_EVENT_END1("v8.execute", "RunMicrotasks", "microtask_count",
processed_microtask_count);
Expand All @@ -191,7 +196,6 @@ int MicrotaskQueue::RunMicrotasks(Isolate* isolate) {
OnCompleted(isolate);
return -1;
}
DCHECK_EQ(0, size());
OnCompleted(isolate);

return processed_microtask_count;
Expand Down
20 changes: 20 additions & 0 deletions deps/v8/src/execution/microtask-queue.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,22 @@ class V8_EXPORT_PRIVATE MicrotaskQueue final : public v8::MicrotaskQueue {
intptr_t size() const { return size_; }
intptr_t start() const { return start_; }

// Qualia: see v8::MicrotaskQueue::RunState.
v8::MicrotaskQueue::RunState GetInternalRunState() const {
v8::MicrotaskQueue::RunState state;
state.is_running = is_running_microtasks_;
state.scope_depth = microtasks_depth_;
state.suppressions = microtasks_suppressions_;
state.stop_draining = qualia_stop_draining_ != 0;
return state;
}
void SetInternalRunState(const v8::MicrotaskQueue::RunState& state) {
is_running_microtasks_ = state.is_running;
microtasks_depth_ = state.scope_depth;
microtasks_suppressions_ = state.suppressions;
qualia_stop_draining_ = state.stop_draining ? 1 : 0;
}

Microtask get(intptr_t index) const;

MicrotaskQueue* next() const { return next_; }
Expand All @@ -110,6 +126,7 @@ class V8_EXPORT_PRIVATE MicrotaskQueue final : public v8::MicrotaskQueue {
static const size_t kSizeOffset;
static const size_t kStartOffset;
static const size_t kFinishedMicrotaskCountOffset;
static const size_t kQualiaStopDrainingOffset;

static const intptr_t kMinimumCapacity;

Expand Down Expand Up @@ -146,6 +163,9 @@ class V8_EXPORT_PRIVATE MicrotaskQueue final : public v8::MicrotaskQueue {
v8::MicrotasksPolicy microtasks_policy_ = v8::MicrotasksPolicy::kAuto;

bool is_running_microtasks_ = false;

// Qualia: checked by the RunMicrotasks builtin before each microtask.
intptr_t qualia_stop_draining_ = 0;
using CallbackWithData =
std::pair<MicrotasksCompletedCallbackWithData, void*>;
std::vector<CallbackWithData> microtasks_completed_callbacks_;
Expand Down
24 changes: 23 additions & 1 deletion src/api/callback.cc
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "node.h"
#include "async_wrap-inl.h"
#include "env-inl.h"
#include "node_internals.h"
#include "v8.h"

namespace node {
Expand All @@ -16,6 +17,20 @@ using v8::Object;
using v8::String;
using v8::Value;

typedef void (*QualiaMicrotaskCheckpointHook)(Isolate* isolate,
Local<Context> context,
v8::MicrotaskQueue* queue);
static QualiaMicrotaskCheckpointHook qualia_microtask_checkpoint_hook = nullptr;

void PerformMicrotaskCheckpoint(Isolate* isolate, Local<Context> context) {
v8::MicrotaskQueue* queue = context->GetMicrotaskQueue();
if (qualia_microtask_checkpoint_hook != nullptr) {
qualia_microtask_checkpoint_hook(isolate, context, queue);
} else {
queue->PerformCheckpoint(isolate);
}
}

CallbackScope::CallbackScope(Isolate* isolate,
Local<Object> object,
async_context async_context)
Expand Down Expand Up @@ -134,7 +149,7 @@ void InternalCallbackScope::Close() {

Local<Context> context = env_->context();
if (!tick_info->has_tick_scheduled()) {
context->GetMicrotaskQueue()->PerformCheckpoint(isolate);
PerformMicrotaskCheckpoint(isolate, context);

perform_stopping_check();
}
Expand Down Expand Up @@ -358,3 +373,10 @@ Local<Value> MakeCallback(Isolate* isolate,
}

} // namespace node

// Qualia: looked up with dlsym() by node-fibers, so addons still load on a
// stock node. Passing nullptr restores the default behaviour.
extern "C" NODE_EXTERN void node_qualia_set_microtask_checkpoint_hook(
node::QualiaMicrotaskCheckpointHook hook) {
node::qualia_microtask_checkpoint_hook = hook;
}
6 changes: 6 additions & 0 deletions src/node_internals.h
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,12 @@ std::string FormatCaughtException(v8::Isolate* isolate,
const v8::TryCatch& try_catch);

void ResetStdio(); // Safe to call more than once and from signal handlers.

// Qualia: drains a context's microtask queue, through the hook registered with
// node_qualia_set_microtask_checkpoint_hook() if there is one (node-fibers uses
// it to drain on a fiber, so `await` continuations can Fiber.yield()).
void PerformMicrotaskCheckpoint(v8::Isolate* isolate,
v8::Local<v8::Context> context);
#ifdef __POSIX__
void SignalExit(int signal, siginfo_t* info, void* ucontext);
#endif
Expand Down
2 changes: 1 addition & 1 deletion src/node_task_queue.cc
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ static void EnqueueMicrotask(const FunctionCallbackInfo<Value>& args) {

static void RunMicrotasks(const FunctionCallbackInfo<Value>& args) {
Environment* env = Environment::GetCurrent(args);
env->context()->GetMicrotaskQueue()->PerformCheckpoint(env->isolate());
PerformMicrotaskCheckpoint(env->isolate(), env->context());
}

static void SetTickCallback(const FunctionCallbackInfo<Value>& args) {
Expand Down
Loading