mirror of
https://github.com/oven-sh/bun
synced 2026-02-10 19:08:50 +00:00
Add a way to schedule microtasks from C++
This commit is contained in:
committed by
Jarred Sumner
parent
759bfadb93
commit
92aeb0af55
@@ -14,8 +14,12 @@
|
||||
namespace uWS {
|
||||
template<bool isServer, bool isClient, typename UserData>
|
||||
struct WebSocketContext;
|
||||
|
||||
}
|
||||
|
||||
#ifndef ZIG_GLOBAL_OBJECT_DEFINED
|
||||
#include "ZigGlobalObject.h"
|
||||
#endif
|
||||
|
||||
struct us_socket_t;
|
||||
struct us_socket_context_t;
|
||||
struct us_loop_t;
|
||||
@@ -24,42 +28,48 @@ namespace WebCore {
|
||||
|
||||
class WebSocket;
|
||||
|
||||
class ScriptExecutionContext;
|
||||
|
||||
class EventLoopTask {
|
||||
WTF_MAKE_FAST_ALLOCATED;
|
||||
|
||||
public:
|
||||
enum CleanupTaskTag { CleanupTask };
|
||||
|
||||
template<typename T, typename = typename std::enable_if<!std::is_base_of<EventLoopTask, T>::value && std::is_convertible<T, Function<void(ScriptExecutionContext&)>>::value>::type>
|
||||
EventLoopTask(T task)
|
||||
: m_task(WTFMove(task))
|
||||
, m_isCleanupTask(false)
|
||||
{
|
||||
}
|
||||
|
||||
EventLoopTask(Function<void()>&& task)
|
||||
: m_task([task = WTFMove(task)](ScriptExecutionContext&) { task(); })
|
||||
, m_isCleanupTask(false)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T, typename = typename std::enable_if<std::is_convertible<T, Function<void(ScriptExecutionContext&)>>::value>::type>
|
||||
EventLoopTask(CleanupTaskTag, T task)
|
||||
: m_task(WTFMove(task))
|
||||
, m_isCleanupTask(true)
|
||||
{
|
||||
}
|
||||
|
||||
void performTask(ScriptExecutionContext& context)
|
||||
{
|
||||
m_task(context);
|
||||
delete this;
|
||||
}
|
||||
bool isCleanupTask() const { return m_isCleanupTask; }
|
||||
|
||||
protected:
|
||||
Function<void(ScriptExecutionContext&)> m_task;
|
||||
bool m_isCleanupTask;
|
||||
};
|
||||
|
||||
class ScriptExecutionContext : public CanMakeWeakPtr<ScriptExecutionContext> {
|
||||
public:
|
||||
class Task {
|
||||
WTF_MAKE_FAST_ALLOCATED;
|
||||
|
||||
public:
|
||||
enum CleanupTaskTag { CleanupTask };
|
||||
|
||||
template<typename T, typename = typename std::enable_if<!std::is_base_of<Task, T>::value && std::is_convertible<T, Function<void(ScriptExecutionContext&)>>::value>::type>
|
||||
Task(T task)
|
||||
: m_task(WTFMove(task))
|
||||
, m_isCleanupTask(false)
|
||||
{
|
||||
}
|
||||
|
||||
Task(Function<void()>&& task)
|
||||
: m_task([task = WTFMove(task)](ScriptExecutionContext&) { task(); })
|
||||
, m_isCleanupTask(false)
|
||||
{
|
||||
}
|
||||
|
||||
template<typename T, typename = typename std::enable_if<std::is_convertible<T, Function<void(ScriptExecutionContext&)>>::value>::type>
|
||||
Task(CleanupTaskTag, T task)
|
||||
: m_task(WTFMove(task))
|
||||
, m_isCleanupTask(true)
|
||||
{
|
||||
}
|
||||
|
||||
void performTask(ScriptExecutionContext& context) { m_task(context); }
|
||||
bool isCleanupTask() const { return m_isCleanupTask; }
|
||||
|
||||
protected:
|
||||
Function<void(ScriptExecutionContext&)> m_task;
|
||||
bool m_isCleanupTask;
|
||||
};
|
||||
|
||||
public:
|
||||
ScriptExecutionContext(JSC::VM* vm, JSC::JSGlobalObject* globalObject)
|
||||
: m_vm(vm)
|
||||
@@ -96,9 +106,10 @@ public:
|
||||
// {
|
||||
// }
|
||||
|
||||
void postTask(Task&& task)
|
||||
void postTask(Function<void(ScriptExecutionContext&)>&& lambda)
|
||||
{
|
||||
|
||||
auto* task = new EventLoopTask(WTFMove(lambda));
|
||||
reinterpret_cast<Zig::GlobalObject*>(m_globalObject)->queueTask(task);
|
||||
} // Executes the task on context's thread asynchronously.
|
||||
|
||||
template<typename... Arguments>
|
||||
|
||||
@@ -2107,6 +2107,17 @@ void GlobalObject::visitChildrenImpl(JSCell* cell, Visitor& visitor)
|
||||
visitor.addOpaqueRoot(context);
|
||||
}
|
||||
|
||||
extern "C" void Bun__queueMicrotask(JSC__JSGlobalObject*, WebCore::EventLoopTask* task);
|
||||
extern "C" void Bun__performTask(Zig::GlobalObject* globalObject, WebCore::EventLoopTask* task)
|
||||
{
|
||||
task->performTask(*globalObject->scriptExecutionContext());
|
||||
}
|
||||
|
||||
void GlobalObject::queueTask(WebCore::EventLoopTask* task)
|
||||
{
|
||||
Bun__queueMicrotask(this, task);
|
||||
}
|
||||
|
||||
DEFINE_VISIT_CHILDREN(GlobalObject);
|
||||
|
||||
// void GlobalObject::destroy(JSCell* cell)
|
||||
|
||||
@@ -13,6 +13,7 @@ class LazyClassStructure;
|
||||
namespace WebCore {
|
||||
class ScriptExecutionContext;
|
||||
class DOMGuardedObject;
|
||||
class EventLoopTask;
|
||||
}
|
||||
|
||||
#include "root.h"
|
||||
@@ -37,6 +38,8 @@ namespace Zig {
|
||||
using JSDOMStructureMap = HashMap<const JSC::ClassInfo*, JSC::WriteBarrier<JSC::Structure>>;
|
||||
using DOMGuardedObjectSet = HashSet<WebCore::DOMGuardedObject*>;
|
||||
|
||||
#define ZIG_GLOBAL_OBJECT_DEFINED
|
||||
|
||||
class GlobalObject : public JSC::JSGlobalObject {
|
||||
using Base = JSC::JSGlobalObject;
|
||||
|
||||
@@ -111,6 +114,8 @@ public:
|
||||
WebCore::ScriptExecutionContext* scriptExecutionContext();
|
||||
WebCore::ScriptExecutionContext* scriptExecutionContext() const;
|
||||
|
||||
void queueTask(WebCore::EventLoopTask* task);
|
||||
|
||||
JSDOMStructureMap& structures() WTF_REQUIRES_LOCK(m_gcLock) { return m_structures; }
|
||||
JSDOMStructureMap& structures(NoLockingNecessaryTag) WTF_IGNORES_THREAD_SAFETY_ANALYSIS
|
||||
{
|
||||
|
||||
@@ -109,11 +109,11 @@ private:
|
||||
JSC::Weak<JSC::JSObject> m_callback;
|
||||
};
|
||||
|
||||
class DeleteCallbackDataTask : public ScriptExecutionContext::Task {
|
||||
class DeleteCallbackDataTask : public EventLoopTask {
|
||||
public:
|
||||
template<typename CallbackDataType>
|
||||
explicit DeleteCallbackDataTask(CallbackDataType* data)
|
||||
: ScriptExecutionContext::Task(ScriptExecutionContext::Task::CleanupTask, [data = std::unique_ptr<CallbackDataType>(data)](ScriptExecutionContext&) {
|
||||
: EventLoopTask(EventLoopTask::CleanupTask, [data = std::unique_ptr<CallbackDataType>(data)](ScriptExecutionContext&) {
|
||||
})
|
||||
{
|
||||
}
|
||||
|
||||
@@ -259,6 +259,14 @@ pub const AnyTask = struct {
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
pub const CppTask = opaque {
|
||||
extern fn Bun__performTask(globalObject: *JSGlobalObject, task: *CppTask) void;
|
||||
pub fn run(this: *CppTask, global: *JSGlobalObject) void {
|
||||
JSC.markBinding();
|
||||
Bun__performTask(global, this);
|
||||
}
|
||||
};
|
||||
const ThreadSafeFunction = JSC.napi.ThreadSafeFunction;
|
||||
const MicrotaskForDefaultGlobalObject = JSC.MicrotaskForDefaultGlobalObject;
|
||||
// const PromiseTask = JSInternalPromise.Completion.PromiseTask;
|
||||
@@ -274,6 +282,7 @@ pub const Task = TaggedPointerUnion(.{
|
||||
AnyTask,
|
||||
napi_async_work,
|
||||
ThreadSafeFunction,
|
||||
CppTask,
|
||||
// PromiseTask,
|
||||
// TimeoutTasklet,
|
||||
});
|
||||
@@ -287,7 +296,7 @@ pub const EventLoop = struct {
|
||||
concurrent_lock: Lock = Lock.init(),
|
||||
global: *JSGlobalObject = undefined,
|
||||
virtual_machine: *VirtualMachine = undefined,
|
||||
pub const Queue = bun.LinearFifo(Task, .Dynamic);
|
||||
pub const Queue = std.fifo.LinearFifo(Task, .Dynamic);
|
||||
|
||||
pub fn tickWithCount(this: *EventLoop) u32 {
|
||||
var finished: u32 = 0;
|
||||
@@ -357,6 +366,12 @@ pub const EventLoop = struct {
|
||||
finished += 1;
|
||||
vm_.active_tasks -|= 1;
|
||||
},
|
||||
@field(Task.Tag, @typeName(CppTask)) => {
|
||||
var any: *CppTask = task.get(CppTask).?;
|
||||
any.run(global);
|
||||
finished += 1;
|
||||
vm_.active_tasks -|= 1;
|
||||
},
|
||||
else => if (Environment.allow_assert) {
|
||||
bun.Output.prettyln("\nUnexpected tag: {s}\n", .{@tagName(task.tag())});
|
||||
} else unreachable,
|
||||
|
||||
Reference in New Issue
Block a user