aboutsummaryrefslogtreecommitdiff
path: root/src/bun.js/bindings/JSCTaskScheduler.cpp
blob: b97b2e9d26078874701631b71e5f0972bc01f7aa (plain) (blame)
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);
}

}