forked from oven-sh/bun
-
Notifications
You must be signed in to change notification settings - Fork 0
/
JSCTaskScheduler.cpp
103 lines (84 loc) · 3.12 KB
/
JSCTaskScheduler.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
#include "config.h"
#include <JavaScriptCore/VM.h>
#include "JSCTaskScheduler.h"
#include "BunClientData.h"
using Ticket = JSC::DeferredWorkTimer::Ticket;
using Task = JSC::DeferredWorkTimer::Task;
using TicketData = JSC::DeferredWorkTimer::TicketData;
namespace Bun {
using namespace JSC;
extern "C" void Bun__queueJSCDeferredWorkTaskConcurrently(void* bunVM, void* task);
extern "C" void Bun__eventLoop__incrementRefConcurrently(void* bunVM, int delta);
class JSCDeferredWorkTask {
public:
JSCDeferredWorkTask(Ticket ticket, Task&& task)
: ticket(ticket)
, task(WTFMove(task))
{
}
Ticket ticket;
Task task;
WTF_MAKE_ISO_ALLOCATED(JSCDeferredWorkTask);
};
WTF_MAKE_ISO_ALLOCATED_IMPL(JSCDeferredWorkTask);
static JSC::VM& getVM(Ticket ticket)
{
return ticket->scriptExecutionOwner.get()->vm();
}
void JSCTaskScheduler::onAddPendingWork(std::unique_ptr<TicketData> ticket, JSC::DeferredWorkTimer::WorkKind kind)
{
JSC::VM& vm = getVM(ticket.get());
auto clientData = WebCore::clientData(vm);
auto& scheduler = clientData->deferredWorkTimer;
LockHolder holder { scheduler.m_lock };
if (kind != DeferredWorkTimer::WorkKind::Other) {
Bun__eventLoop__incrementRefConcurrently(clientData->bunVM, 1);
scheduler.m_pendingTicketsKeepingEventLoopAlive.add(WTFMove(ticket));
} else {
scheduler.m_pendingTicketsOther.add(WTFMove(ticket));
}
}
void JSCTaskScheduler::onScheduleWorkSoon(Ticket ticket, Task&& task)
{
auto* job = new JSCDeferredWorkTask(ticket, WTFMove(task));
Bun__queueJSCDeferredWorkTaskConcurrently(WebCore::clientData(getVM(ticket))->bunVM, job);
}
void JSCTaskScheduler::onCancelPendingWork(Ticket ticket)
{
auto& scheduler = WebCore::clientData(getVM(ticket))->deferredWorkTimer;
LockHolder holder { scheduler.m_lock };
bool isKeepingEventLoopAlive = scheduler.m_pendingTicketsKeepingEventLoopAlive.removeIf([ticket](const auto& pendingTicket) {
return pendingTicket.get() == ticket;
});
if (isKeepingEventLoopAlive) {
holder.unlockEarly();
JSC::VM& vm = getVM(ticket);
Bun__eventLoop__incrementRefConcurrently(WebCore::clientData(vm)->bunVM, -1);
} else {
scheduler.m_pendingTicketsOther.removeIf([ticket](const auto& pendingTicket) {
return pendingTicket.get() == ticket;
});
}
}
static void runPendingWork(void* bunVM, Bun::JSCTaskScheduler& scheduler, JSCDeferredWorkTask* job)
{
LockHolder holder { scheduler.m_lock };
auto pendingTicket = scheduler.m_pendingTicketsKeepingEventLoopAlive.take(job->ticket);
if (!pendingTicket) {
pendingTicket = scheduler.m_pendingTicketsOther.take(job->ticket);
} else {
Bun__eventLoop__incrementRefConcurrently(bunVM, -1);
}
holder.unlockEarly();
if (pendingTicket && !pendingTicket->isCancelled()) {
job->task(job->ticket);
}
delete job;
}
extern "C" void Bun__runDeferredWork(Bun::JSCDeferredWorkTask* job)
{
auto& vm = getVM(job->ticket);
auto clientData = WebCore::clientData(vm);
runPendingWork(clientData->bunVM, clientData->deferredWorkTimer, job);
}
}