Files
bun.sh/src/bun.js/bindings/JSCTaskScheduler.cpp
Jarred Sumner 1db119ec11 Fix memory leak (#3887)
* Fix memory leak

* Remove an extra copy

* Further fixes

---------

Co-authored-by: Jarred Sumner <709451+Jarred-Sumner@users.noreply.github.com>
2023-07-30 02:03:32 -07:00

104 lines
3.1 KiB
C++

#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);
}
}