⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 287421 in webkit


Ignore:
Timestamp:
Dec 23, 2021, 9:58:47 PM (5 years ago)
Author:
mark.lam@apple.com
Message:

Make DeferredWorkTimer::addPendingWork() return a Ticket.
​https://bugs.webkit.org/show_bug.cgi?id=234628
rdar://84260429

Reviewed by Yusuke Suzuki.

  1. Make Ticket a unique token instead of the JSObject* target object. The Ticket is now a pointer to the TicketData in the pending work list.
  1. Instead of taking a Ticket argument, DeferredWorkTimer::addPendingWork() now takes a JSObject* target argument explicitly, and returns the Ticket for the added TicketData instead.

All the relevant DeferredWorkTimer APIS already take a Ticket as an argument.
This ensures that addPendingWork() is called before we start doing work with
these APIs (especially scheduleWorkSoon()).

  1. Previously, addPendingWork() will only save one instance of TicketData for a given JSObject* key. With this patch, we'll register a new TicketData instance for every call to addPendingWork(), and return a unique Ticket for it.

This is needed because it may be possible for 2 different clients to call
addPendingWork() and scheduleWorkSoon() with the same target JSObject* but with
different sets of dependencies.

Secondly, even is the both sets of dependencies are identical, a client may
call addPendingWork() and scheduleWorkSoon() with the same JSObject* target
more than once because it intended to schedule more than 1 task to run.

Note that DeferredWorkTimer::doWork() consumes the corresponding TicketData
(i.e. removes it from the m_pendingTickets list) for each task as it is run.
To ensure that the dependencies for each task is protected, we'll either need
to ref count the TicketData for the same target object (and hold off on removing
it from the list), or we'll need to register a different TicketData instance
for each task. Ref counting can solve the second issue above, but does not
solve the first. So, this patch goes with the more generic solution to allow
each task to have its own TicketData instance (and, its own unique Ticket).

  1. Previously, if the client cancels pending work, we would remove the TicketData immediately from the m_pendingTickets list. This opens up an opportunity for the same TicketData memory to be re-allocated by another client. This, in turn, would make the Ticket token not unique and potentially allow a cancelled ticket to be reused before DeferredWorkTimer::doWork() is called.

This patch changes DeferredWorkTimer::cancelPendingWork() to only clear the
contents of the TicketData instead. TicketData::scriptExecutionOwner being
null is used as an indication that the ticket has been cancelled. Since the
TicketData itself is not "freed" yet, all TicketData will remain unique until
DeferredWorkTimer::doWork().

Consequently, DeferredWorkTimer::doWork() will now check for cancelled tickets
and remove them from the m_pendingTickets list.

  1. JSFinalizationRegistry was previously calling DeferredWorkTimer::hasPendingWork() to check if it has already scheduled a task, so as not to reschedule again until after the previously scheduled task has been run. This does not play nice with the new Ticket API, because this hasPendingWork() check needs to be done before calling addPendingWork(), and hence, the Ticket is not available yet.

Fortunately, JSFinalizationRegistry should know if it has already scheduled
a task itself. This patch adds a m_hasAlreadyScheduledWork flag to
JSFinalizationRegistry that can be used for this check instead.

  • jsc.cpp:

(JSC_DEFINE_HOST_FUNCTION):

  • runtime/DeferredWorkTimer.cpp:

(JSC::DeferredWorkTimer::TicketData::TicketData):
(JSC::DeferredWorkTimer::TicketData::vm):
(JSC::DeferredWorkTimer::TicketData::cancel):
(JSC::DeferredWorkTimer::doWork):
(JSC::DeferredWorkTimer::addPendingWork):
(JSC::DeferredWorkTimer::hasPendingWork):
(JSC::DeferredWorkTimer::hasDependancyInPendingWork):
(JSC::DeferredWorkTimer::cancelPendingWork):

  • runtime/DeferredWorkTimer.h:

(JSC::DeferredWorkTimer::TicketData::target):

  • runtime/JSFinalizationRegistry.cpp:

(JSC::JSFinalizationRegistry::finalizeUnconditionally):

  • runtime/JSFinalizationRegistry.h:
  • wasm/WasmStreamingCompiler.cpp:

(JSC::Wasm::StreamingCompiler::StreamingCompiler):
(JSC::Wasm::StreamingCompiler::~StreamingCompiler):
(JSC::Wasm::StreamingCompiler::didComplete):
(JSC::Wasm::StreamingCompiler::fail):
(JSC::Wasm::StreamingCompiler::cancel):

  • wasm/WasmStreamingCompiler.h:
  • wasm/js/JSWebAssembly.cpp:

(JSC::JSWebAssembly::webAssemblyModuleValidateAsync):
(JSC::instantiate):
(JSC::compileAndInstantiate):
(JSC::JSWebAssembly::webAssemblyModuleInstantinateAsync):

Location:
trunk/Source/JavaScriptCore
Files:
9 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r287379 r287421  
     12021-12-23  Mark Lam  <mark.lam@apple.com>
     2
     3        Make DeferredWorkTimer::addPendingWork() return a Ticket.
     4        https://bugs.webkit.org/show_bug.cgi?id=234628
     5        rdar://84260429
     6
     7        Reviewed by Yusuke Suzuki.
     8
     9        1. Make Ticket a unique token instead of the JSObject* target object.
     10           The Ticket is now a pointer to the TicketData in the pending work list.
     11
     12        2. Instead of taking a Ticket argument, DeferredWorkTimer::addPendingWork() now
     13           takes a JSObject* `target` argument explicitly, and returns the Ticket for the
     14           added TicketData instead.
     15
     16           All the relevant DeferredWorkTimer APIS already take a Ticket as an argument.
     17           This ensures that addPendingWork() is called before we start doing work with
     18           these APIs (especially scheduleWorkSoon()).
     19
     20        3. Previously, addPendingWork() will only save one instance of TicketData for
     21           a given JSObject* key.  With this patch, we'll register a new TicketData
     22           instance for every call to addPendingWork(), and return a unique Ticket for it.
     23
     24           This is needed because it may be possible for 2 different clients to call
     25           addPendingWork() and scheduleWorkSoon() with the same target JSObject* but with
     26           different sets of dependencies.
     27
     28           Secondly, even is the both sets of dependencies are identical, a client may
     29           call addPendingWork() and scheduleWorkSoon() with the same JSObject* target
     30           more than once because it intended to schedule more than 1 task to run.
     31
     32           Note that DeferredWorkTimer::doWork() consumes the corresponding TicketData
     33           (i.e. removes it from the m_pendingTickets list) for each task as it is run.
     34           To ensure that the dependencies for each task is protected, we'll either need
     35           to ref count the TicketData for the same target object (and hold off on removing
     36           it from the list), or we'll need to register a different TicketData instance
     37           for each task.  Ref counting can solve the second issue above, but does not
     38           solve the first.  So, this patch goes with the more generic solution to allow
     39           each task to have its own TicketData instance (and, its own unique Ticket).
     40
     41        4. Previously, if the client cancels pending work, we would remove the TicketData
     42           immediately from the m_pendingTickets list.  This opens up an opportunity for
     43           the same TicketData memory to be re-allocated by another client.  This, in turn,
     44           would make the Ticket token not unique and potentially allow a cancelled ticket
     45           to be reused before DeferredWorkTimer::doWork() is called.
     46
     47           This patch changes DeferredWorkTimer::cancelPendingWork() to only clear the
     48           contents of the TicketData instead.  TicketData::scriptExecutionOwner being
     49           null is used as an indication that the ticket has been cancelled.  Since the
     50           TicketData itself is not "freed" yet, all TicketData will remain unique until
     51           DeferredWorkTimer::doWork().
     52
     53           Consequently, DeferredWorkTimer::doWork() will now check for cancelled tickets
     54           and remove them from the m_pendingTickets list.
     55
     56        5. JSFinalizationRegistry was previously calling DeferredWorkTimer::hasPendingWork()
     57           to check if it has already scheduled a task, so as not to reschedule again until
     58           after the previously scheduled task has been run.  This does not play nice
     59           with the new Ticket API, because this hasPendingWork() check needs to be done
     60           before calling addPendingWork(), and hence, the Ticket is not available yet.
     61
     62           Fortunately, JSFinalizationRegistry should know if it has already scheduled
     63           a task itself.  This patch adds a m_hasAlreadyScheduledWork flag to
     64           JSFinalizationRegistry that can be used for this check instead.
     65
     66        * jsc.cpp:
     67        (JSC_DEFINE_HOST_FUNCTION):
     68        * runtime/DeferredWorkTimer.cpp:
     69        (JSC::DeferredWorkTimer::TicketData::TicketData):
     70        (JSC::DeferredWorkTimer::TicketData::vm):
     71        (JSC::DeferredWorkTimer::TicketData::cancel):
     72        (JSC::DeferredWorkTimer::doWork):
     73        (JSC::DeferredWorkTimer::addPendingWork):
     74        (JSC::DeferredWorkTimer::hasPendingWork):
     75        (JSC::DeferredWorkTimer::hasDependancyInPendingWork):
     76        (JSC::DeferredWorkTimer::cancelPendingWork):
     77        * runtime/DeferredWorkTimer.h:
     78        (JSC::DeferredWorkTimer::TicketData::target):
     79        * runtime/JSFinalizationRegistry.cpp:
     80        (JSC::JSFinalizationRegistry::finalizeUnconditionally):
     81        * runtime/JSFinalizationRegistry.h:
     82        * wasm/WasmStreamingCompiler.cpp:
     83        (JSC::Wasm::StreamingCompiler::StreamingCompiler):
     84        (JSC::Wasm::StreamingCompiler::~StreamingCompiler):
     85        (JSC::Wasm::StreamingCompiler::didComplete):
     86        (JSC::Wasm::StreamingCompiler::fail):
     87        (JSC::Wasm::StreamingCompiler::cancel):
     88        * wasm/WasmStreamingCompiler.h:
     89        * wasm/js/JSWebAssembly.cpp:
     90        (JSC::JSWebAssembly::webAssemblyModuleValidateAsync):
     91        (JSC::instantiate):
     92        (JSC::compileAndInstantiate):
     93        (JSC::JSWebAssembly::webAssemblyModuleInstantinateAsync):
     94
    1952021-12-22  Saam Barati  <sbarati@apple.com>
    296
  • trunk/Source/JavaScriptCore/jsc.cpp

    r287379 r287421  
    25052505
    25062506    // FIXME: We don't look at the timeout parameter because we don't have a schedule work later API.
    2507     vm.deferredWorkTimer->addPendingWork(vm, callback, { });
    2508     vm.deferredWorkTimer->scheduleWorkSoon(callback, [callback](DeferredWorkTimer::Ticket, DeferredWorkTimer::TicketData&&) {
     2507    auto ticket = vm.deferredWorkTimer->addPendingWork(vm, callback, { });
     2508    vm.deferredWorkTimer->scheduleWorkSoon(ticket, [callback](DeferredWorkTimer::Ticket) {
    25092509        JSGlobalObject* globalObject = callback->globalObject();
    25102510        MarkedArgumentBuffer args;
  • trunk/Source/JavaScriptCore/runtime/DeferredWorkTimer.cpp

    r277920 r287421  
    11/*
    2  * Copyright (C) 2017-2020 Apple Inc. All rights reserved.
     2 * Copyright (C) 2017-2021 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
    … …  
    3838}
    3939
     40inline DeferredWorkTimer::TicketData::TicketData(VM& vm, JSObject* scriptExecutionOwner, Vector<Strong<JSCell>>&& dependencies)
     41    : dependencies(WTFMove(dependencies))
     42    , scriptExecutionOwner(vm, scriptExecutionOwner)
     43{
     44}
     45
     46inline VM& DeferredWorkTimer::TicketData::vm()
     47{
     48    ASSERT(!isCancelled());
     49    return target()->vm();
     50}
     51
     52inline void DeferredWorkTimer::TicketData::cancel()
     53{
     54    scriptExecutionOwner.clear();
     55    dependencies.clear();
     56}
     57
     58
    4059DeferredWorkTimer::DeferredWorkTimer(VM& vm)
    4160    : Base(vm)
    … …  
    5574    while (!m_tasks.isEmpty()) {
    5675        auto [ticket, task] = m_tasks.takeFirst();
    57         auto globalObject = ticket->structure(vm)->globalObject();
    5876        dataLogLnIf(DeferredWorkTimerInternal::verbose, "Doing work on: ", RawPointer(ticket));
    5977
    … …  
    6280        if (pendingTicket == m_pendingTickets.end())
    6381            continue;
    64 
    65         switch (globalObject->globalObjectMethodTable()->scriptExecutionStatus(globalObject, pendingTicket->value.scriptExecutionOwner.get())) {
     82        ASSERT(ticket == pendingTicket->get());
     83
     84        if (ticket->isCancelled()) {
     85            m_pendingTickets.remove(pendingTicket);
     86            continue;
     87        }
     88
     89        // We shouldn't access the TicketData to get this globalObject until
     90        // after we confirm that the ticket is still valid (which we did above).
     91        auto globalObject = ticket->target()->structure(vm)->globalObject();
     92        switch (globalObject->globalObjectMethodTable()->scriptExecutionStatus(globalObject, ticket->scriptExecutionOwner.get())) {
    6693        case ScriptExecutionStatus::Suspended:
    6794            suspendedTasks.append(std::make_tuple(ticket, WTFMove(task)));
    … …  
    76103        // Remove ticket from m_pendingTickets since we are going to run it.
    77104        // But we want to keep ticketData while running task since it ensures dependencies are strongly held.
    78         auto ticketData = WTFMove(pendingTicket->value);
    79         m_pendingTickets.remove(pendingTicket);
     105        std::unique_ptr<TicketData> ticketData = m_pendingTickets.take(pendingTicket);
    80106
    81107        // Allow tasks we are about to run to schedule work.
    … …  
    88114
    89115            auto scope = DECLARE_CATCH_SCOPE(vm);
    90             task(ticket, WTFMove(ticketData));
     116            task(ticket);
     117            ticketData = nullptr;
    91118            if (Exception* exception = scope.exception()) {
    92                 auto* globalObject = ticket->globalObject();
    93119                scope.clearException();
    94120                globalObject->globalObjectMethodTable()->reportUncaughtExceptionAtEventLoop(globalObject, exception);
    … …  
    104130        m_tasks.prepend(suspendedTasks.takeLast());
    105131
     132    // It is theoretically possible that a client may cancel a pending ticket and
     133    // never call scheduleWorkSoon() on it. As such, it would not be found when
     134    // we iterated m_tasks above. We'll need to make sure to purge them here.
     135    m_pendingTickets.removeIf([] (auto& ticket) {
     136        return ticket->isCancelled();
     137    });
     138
    106139    if (m_pendingTickets.isEmpty() && m_shouldStopRunLoopWhenAllTicketsFinish) {
    107140        ASSERT(m_tasks.isEmpty());
    … …  
    119152}
    120153
    121 void DeferredWorkTimer::addPendingWork(VM& vm, Ticket ticket, Vector<Strong<JSCell>>&& dependencies)
     154DeferredWorkTimer::Ticket DeferredWorkTimer::addPendingWork(VM& vm, JSObject* target, Vector<Strong<JSCell>>&& dependencies)
    122155{
    123156    ASSERT(vm.currentThreadIsHoldingAPILock() || (Thread::mayBeGCThread() && vm.heap.worldIsStopped()));
    124157    for (unsigned i = 0; i < dependencies.size(); ++i)
    125         ASSERT(dependencies[i].get() != ticket);
    126 
    127     auto globalObject = ticket->globalObject();
    128     auto result = m_pendingTickets.ensure(ticket, [&] {
    129         dataLogLnIf(DeferredWorkTimerInternal::verbose, "Adding new pending ticket: ", RawPointer(ticket));
    130         JSObject* scriptExecutionOwner = globalObject->globalObjectMethodTable()->currentScriptExecutionOwner(globalObject);
    131         dependencies.append(Strong<JSCell>(vm, ticket));
    132         return TicketData { WTFMove(dependencies), Strong<JSObject>(vm, scriptExecutionOwner) };
    133     });
    134     if (!result.isNewEntry) {
    135         dataLogLnIf(DeferredWorkTimerInternal::verbose, "Adding new dependencies for ticket: ", RawPointer(ticket));
    136         result.iterator->value.dependencies.appendVector(WTFMove(dependencies));
    137     }
     158        ASSERT(dependencies[i].get() != target);
     159
     160    auto* globalObject = target->globalObject();
     161    JSObject* scriptExecutionOwner = globalObject->globalObjectMethodTable()->currentScriptExecutionOwner(globalObject);
     162    dependencies.append(Strong<JSCell>(vm, target));
     163
     164    auto ticketData = makeUnique<TicketData>(vm, scriptExecutionOwner, WTFMove(dependencies));
     165    Ticket ticket = ticketData.get();
     166
     167    dataLogLnIf(DeferredWorkTimerInternal::verbose, "Adding new pending ticket: ", RawPointer(ticket));
     168    auto result = m_pendingTickets.add(WTFMove(ticketData));
     169    RELEASE_ASSERT(result.isNewEntry);
     170
     171    return ticket;
    138172}
    139173
    140174bool DeferredWorkTimer::hasPendingWork(Ticket ticket)
    141175{
     176    auto result = m_pendingTickets.find(ticket);
     177    if (result == m_pendingTickets.end() || ticket->isCancelled())
     178        return false;
    142179    ASSERT(ticket->vm().currentThreadIsHoldingAPILock() || (Thread::mayBeGCThread() && ticket->vm().heap.worldIsStopped()));
    143     return m_pendingTickets.contains(ticket);
     180    return true;
    144181}
    145182
    146183bool DeferredWorkTimer::hasDependancyInPendingWork(Ticket ticket, JSCell* dependency)
    147184{
     185    auto result = m_pendingTickets.find(ticket);
     186    if (result == m_pendingTickets.end() || ticket->isCancelled())
     187        return false;
    148188    ASSERT(ticket->vm().currentThreadIsHoldingAPILock() || (Thread::mayBeGCThread() && ticket->vm().heap.worldIsStopped()));
    149     ASSERT(m_pendingTickets.contains(ticket));
    150 
    151     auto result = m_pendingTickets.get(ticket);
    152     return result.dependencies.contains(dependency);
     189    return (*result)->dependencies.contains(dependency);
    153190}
    154191
    … …  
    163200bool DeferredWorkTimer::cancelPendingWork(Ticket ticket)
    164201{
    165     ASSERT(ticket->vm().currentThreadIsHoldingAPILock() || (Thread::mayBeGCThread() && ticket->vm().heap.worldIsStopped()));
    166     bool result = m_pendingTickets.remove(ticket);
    167 
    168     if (result)
     202    ASSERT(m_pendingTickets.contains(ticket));
     203    ASSERT(ticket->isCancelled() || ticket->vm().currentThreadIsHoldingAPILock() || (Thread::mayBeGCThread() && ticket->vm().heap.worldIsStopped()));
     204
     205    bool result = false;
     206    if (!ticket->isCancelled()) {
    169207        dataLogLnIf(DeferredWorkTimerInternal::verbose, "Canceling ticket: ", RawPointer(ticket));
     208        ticket->cancel();
     209        result = true;
     210    }
    170211
    171212    return result;
  • trunk/Source/JavaScriptCore/runtime/DeferredWorkTimer.h

    r278093 r287421  
    11/*
    2  * Copyright (C) 2017-2020 Apple Inc. All rights reserved.
     2 * Copyright (C) 2017-2021 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
    … …  
    3030
    3131#include <wtf/Deque.h>
    32 #include <wtf/HashMap.h>
     32#include <wtf/HashSet.h>
    3333#include <wtf/Lock.h>
    3434#include <wtf/Vector.h>
    … …  
    4545
    4646    struct TicketData {
     47    private:
     48        WTF_MAKE_FAST_ALLOCATED;
     49    public:
     50        TicketData(VM&, JSObject* scriptExecutionOwner, Vector<Strong<JSCell>>&& dependencies);
     51
     52        VM& vm();
     53        JSObject* target();
     54
     55        void cancel();
     56        bool isCancelled() const { return !scriptExecutionOwner.get(); }
     57
    4758        Vector<Strong<JSCell>> dependencies;
    4859        Strong<JSObject> scriptExecutionOwner;
    4960    };
    5061
     62    using Ticket = TicketData*;
     63
    5164    void doWork(VM&) final;
    5265
    53     using Ticket = JSObject*;
    54     void addPendingWork(VM&, Ticket, Vector<Strong<JSCell>>&& dependencies);
     66    Ticket addPendingWork(VM&, JSObject* target, Vector<Strong<JSCell>>&& dependencies);
    5567    bool hasPendingWork(Ticket);
    5668    bool hasDependancyInPendingWork(Ticket, JSCell* dependency);
    … …  
    6274    // this occurs. The easiest way is to make sure everything is either owned
    6375    // by a GC'd value in dependencies or by the Task lambda.
    64     using Task = Function<void(Ticket, TicketData&&)>;
     76    using Task = Function<void(Ticket)>;
    6577    void scheduleWorkSoon(Ticket, Task&&);
    6678    void didResumeScriptExecutionOwner();
    … …  
    7890    bool m_currentlyRunningTask { false };
    7991    Deque<std::tuple<Ticket, Task>> m_tasks WTF_GUARDED_BY_LOCK(m_taskLock);
    80     HashMap<Ticket, TicketData> m_pendingTickets;
     92    HashSet<std::unique_ptr<TicketData>> m_pendingTickets;
    8193};
    8294
     95inline JSObject* DeferredWorkTimer::TicketData::target()
     96{
     97    ASSERT(!isCancelled());
     98    return jsCast<JSObject*>(dependencies.last().get());
     99}
     100
    83101} // namespace JSC
  • trunk/Source/JavaScriptCore/runtime/JSFinalizationRegistry.cpp

    r285730 r287421  
    150150    });
    151151
    152     if (!vm.deferredWorkTimer->hasPendingWork(this) && (readiedCell || deadCount(locker))) {
    153         vm.deferredWorkTimer->addPendingWork(vm, this, { });
    154         ASSERT(vm.deferredWorkTimer->hasPendingWork(this));
    155         vm.deferredWorkTimer->scheduleWorkSoon(this, [this](DeferredWorkTimer::Ticket, DeferredWorkTimer::TicketData&&) {
     152    if (!m_hasAlreadyScheduledWork && (readiedCell || deadCount(locker))) {
     153        auto ticket = vm.deferredWorkTimer->addPendingWork(vm, this, { });
     154        ASSERT(vm.deferredWorkTimer->hasPendingWork(ticket));
     155        vm.deferredWorkTimer->scheduleWorkSoon(ticket, [this](DeferredWorkTimer::Ticket) {
    156156            JSGlobalObject* globalObject = this->globalObject();
     157            this->m_hasAlreadyScheduledWork = false;
    157158            this->runFinalizationCleanup(globalObject);
    158159        });
     160        m_hasAlreadyScheduledWork = true;
    159161    }
    160162}
  • trunk/Source/JavaScriptCore/runtime/JSFinalizationRegistry.h

    r275788 r287421  
    107107    LiveRegistrations m_noUnregistrationLive;
    108108    DeadRegistrations m_noUnregistrationDead;
     109    bool m_hasAlreadyScheduledWork { false };
    109110};
    110111
  • trunk/Source/JavaScriptCore/wasm/WasmStreamingCompiler.cpp

    r285654 r287421  
    2727#include "WasmStreamingCompiler.h"
    2828
    29 #include "DeferredWorkTimer.h"
    3029#include "JSBigInt.h"
    3130#include "JSWebAssembly.h"
    … …  
    4645    : m_vm(vm)
    4746    , m_compilerMode(compilerMode)
    48     , m_promise(promise)
    4947    , m_info(Wasm::ModuleInformation::create())
    5048    , m_parser(m_info.get(), *this)
    … …  
    5452    if (importObject)
    5553        dependencies.append(Strong<JSCell>(vm, importObject));
    56     vm.deferredWorkTimer->addPendingWork(vm, promise, WTFMove(dependencies));
    57     ASSERT(vm.deferredWorkTimer->hasPendingWork(promise));
    58     ASSERT(vm.deferredWorkTimer->hasDependancyInPendingWork(promise, globalObject));
    59     ASSERT(!importObject || vm.deferredWorkTimer->hasDependancyInPendingWork(promise, importObject));
     54    m_ticket = vm.deferredWorkTimer->addPendingWork(vm, promise, WTFMove(dependencies));
     55    ASSERT(vm.deferredWorkTimer->hasPendingWork(m_ticket));
     56    ASSERT(vm.deferredWorkTimer->hasDependancyInPendingWork(m_ticket, globalObject));
     57    ASSERT(!importObject || vm.deferredWorkTimer->hasDependancyInPendingWork(m_ticket, importObject));
    6058}
    6159
    6260StreamingCompiler::~StreamingCompiler()
    6361{
    64     if (m_promise) {
    65         auto* promise = std::exchange(m_promise, nullptr);
    66         m_vm.deferredWorkTimer->scheduleWorkSoon(promise, [](DeferredWorkTimer::Ticket, DeferredWorkTimer::TicketData&&) mutable { });
     62    if (m_ticket) {
     63        auto ticket = std::exchange(m_ticket, nullptr);
     64        m_vm.deferredWorkTimer->scheduleWorkSoon(ticket, [](DeferredWorkTimer::Ticket) mutable { });
    6765    }
    6866}
    … …  
    139137
    140138    auto result = makeValidationResult(*m_plan);
    141     auto* promise = std::exchange(m_promise, nullptr);
     139    auto ticket = std::exchange(m_ticket, nullptr);
    142140    switch (m_compilerMode) {
    143141    case CompilerMode::Validation: {
    144         m_vm.deferredWorkTimer->scheduleWorkSoon(promise, [result = WTFMove(result)](DeferredWorkTimer::Ticket ticket, DeferredWorkTimer::TicketData&& ticketData) mutable {
    145             JSPromise* promise = jsCast<JSPromise*>(ticket);
    146             JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(ticketData.dependencies[0].get());
     142        m_vm.deferredWorkTimer->scheduleWorkSoon(ticket, [result = WTFMove(result)](DeferredWorkTimer::Ticket ticket) mutable {
     143            JSPromise* promise = jsCast<JSPromise*>(ticket->target());
     144            JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(ticket->dependencies[0].get());
    147145            VM& vm = globalObject->vm();
    148146            auto scope = DECLARE_THROW_SCOPE(vm);
    … …  
    161159
    162160    case CompilerMode::FullCompile: {
    163         m_vm.deferredWorkTimer->scheduleWorkSoon(promise, [result = WTFMove(result)](DeferredWorkTimer::Ticket ticket, DeferredWorkTimer::TicketData&& ticketData) mutable {
    164             JSPromise* promise = jsCast<JSPromise*>(ticket);
    165             JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(ticketData.dependencies[0].get());
    166             JSObject* importObject = jsCast<JSObject*>(ticketData.dependencies[1].get());
     161        m_vm.deferredWorkTimer->scheduleWorkSoon(ticket, [result = WTFMove(result)](DeferredWorkTimer::Ticket ticket) mutable {
     162            JSPromise* promise = jsCast<JSPromise*>(ticket->target());
     163            JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(ticket->dependencies[0].get());
     164            JSObject* importObject = jsCast<JSObject*>(ticket->dependencies[1].get());
    167165            VM& vm = globalObject->vm();
    168166            auto scope = DECLARE_THROW_SCOPE(vm);
    … …  
    208206        m_eagerFailed = true;
    209207    }
    210     auto* promise = std::exchange(m_promise, nullptr);
    211     m_vm.deferredWorkTimer->cancelPendingWork(promise);
     208    auto ticket = std::exchange(m_ticket, nullptr);
     209    JSPromise* promise = jsCast<JSPromise*>(ticket->target());
     210    // The pending work TicketData was keeping the promise alive. We need to
     211    // make sure it is reachable from the stack before we remove it from the
     212    // pending work list. Note: m_ticket stores it as a PackedPtr, which is not
     213    // scannable by the GC.
     214    WTF::compilerFence();
     215    m_vm.deferredWorkTimer->cancelPendingWork(ticket);
    212216    promise->reject(globalObject, error);
    213217}
    … …  
    222226        m_eagerFailed = true;
    223227    }
    224     auto* promise = std::exchange(m_promise, nullptr);
    225     m_vm.deferredWorkTimer->cancelPendingWork(promise);
     228    auto ticket = std::exchange(m_ticket, nullptr);
     229    m_vm.deferredWorkTimer->cancelPendingWork(ticket);
    226230}
    227231
  • trunk/Source/JavaScriptCore/wasm/WasmStreamingCompiler.h

    r278093 r287421  
    3030#if ENABLE(WEBASSEMBLY)
    3131
     32#include "DeferredWorkTimer.h"
    3233#include "JSCJSValue.h"
    3334
    … …  
    7374    Lock m_lock;
    7475    unsigned m_remainingCompilationRequests { 0 };
    75     JSPromise* m_promise; // Raw pointer, but held by DeferredWorkTimer.
     76    DeferredWorkTimer::Ticket m_ticket;
    7677    Ref<Wasm::ModuleInformation> m_info;
    7778    StreamingParser m_parser;
  • trunk/Source/JavaScriptCore/wasm/js/JSWebAssembly.cpp

    r287122 r287421  
    171171    dependencies.append(Strong<JSCell>(vm, globalObject));
    172172
    173     vm.deferredWorkTimer->addPendingWork(vm, promise, WTFMove(dependencies));
    174     Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([promise, globalObject, &vm] (Wasm::Module::ValidationResult&& result) mutable {
    175         vm.deferredWorkTimer->scheduleWorkSoon(promise, [promise, globalObject, result = WTFMove(result), &vm](DeferredWorkTimer::Ticket, DeferredWorkTimer::TicketData&&) mutable {
     173    auto ticket = vm.deferredWorkTimer->addPendingWork(vm, promise, WTFMove(dependencies));
     174    Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([ticket, promise, globalObject, &vm] (Wasm::Module::ValidationResult&& result) mutable {
     175        vm.deferredWorkTimer->scheduleWorkSoon(ticket, [promise, globalObject, result = WTFMove(result), &vm](DeferredWorkTimer::Ticket) mutable {
    176176            auto scope = DECLARE_THROW_SCOPE(vm);
    177177            JSValue module = JSWebAssemblyModule::createStub(vm, globalObject, globalObject->webAssemblyModuleStructure(), WTFMove(result));
    … …  
    203203    dependencies.append(Strong<JSCell>(vm, importObject));
    204204
    205     vm.deferredWorkTimer->addPendingWork(vm, instance, WTFMove(dependencies));
     205    auto ticket = vm.deferredWorkTimer->addPendingWork(vm, instance, WTFMove(dependencies));
    206206    // Note: This completion task may or may not get called immediately.
    207     module->module().compileAsync(&vm.wasmContext, instance->memoryMode(), createSharedTask<Wasm::CalleeGroup::CallbackType>([promise, instance, module, importObject, resolveKind, creationMode, &vm] (Ref<Wasm::CalleeGroup>&& refCalleeGroup) mutable {
     207    module->module().compileAsync(&vm.wasmContext, instance->memoryMode(), createSharedTask<Wasm::CalleeGroup::CallbackType>([ticket, promise, instance, module, importObject, resolveKind, creationMode, &vm] (Ref<Wasm::CalleeGroup>&& refCalleeGroup) mutable {
    208208        RefPtr<Wasm::CalleeGroup> calleeGroup = WTFMove(refCalleeGroup);
    209         vm.deferredWorkTimer->scheduleWorkSoon(instance, [promise, instance, module, importObject, resolveKind, creationMode, &vm, calleeGroup = WTFMove(calleeGroup)](DeferredWorkTimer::Ticket, DeferredWorkTimer::TicketData&&) mutable {
     209        vm.deferredWorkTimer->scheduleWorkSoon(ticket, [promise, instance, module, importObject, resolveKind, creationMode, &vm, calleeGroup = WTFMove(calleeGroup)](DeferredWorkTimer::Ticket) mutable {
    210210            JSGlobalObject* globalObject = instance->globalObject();
    211211            resolve(vm, globalObject, promise, instance, module, importObject, calleeGroup.releaseNonNull(), resolveKind, creationMode);
    … …  
    228228    dependencies.append(Strong<JSCell>(vm, importObject));
    229229    dependencies.append(Strong<JSCell>(vm, moduleKeyCell));
    230     vm.deferredWorkTimer->addPendingWork(vm, promise, WTFMove(dependencies));
    231     Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([promise, importObject, moduleKeyCell, globalObject, resolveKind, creationMode, &vm] (Wasm::Module::ValidationResult&& result) mutable {
    232         vm.deferredWorkTimer->scheduleWorkSoon(promise, [promise, importObject, moduleKeyCell, globalObject, result = WTFMove(result), resolveKind, creationMode, &vm](DeferredWorkTimer::Ticket, DeferredWorkTimer::TicketData&&) mutable {
     230    auto ticket = vm.deferredWorkTimer->addPendingWork(vm, promise, WTFMove(dependencies));
     231    Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([ticket, promise, importObject, moduleKeyCell, globalObject, resolveKind, creationMode, &vm] (Wasm::Module::ValidationResult&& result) mutable {
     232        vm.deferredWorkTimer->scheduleWorkSoon(ticket, [promise, importObject, moduleKeyCell, globalObject, result = WTFMove(result), resolveKind, creationMode, &vm](DeferredWorkTimer::Ticket) mutable {
    233233            auto scope = DECLARE_THROW_SCOPE(vm);
    234234            JSWebAssemblyModule* module = JSWebAssemblyModule::createStub(vm, globalObject, globalObject->webAssemblyModuleStructure(), WTFMove(result));
    … …  
    272272    dependencies.append(Strong<JSCell>(vm, importObject));
    273273    dependencies.append(Strong<JSCell>(vm, globalObject));
    274     vm.deferredWorkTimer->addPendingWork(vm, promise, WTFMove(dependencies));
    275     Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([promise, importObject, globalObject, &vm] (Wasm::Module::ValidationResult&& result) mutable {
    276         vm.deferredWorkTimer->scheduleWorkSoon(promise, [promise, importObject, globalObject, result = WTFMove(result), &vm](DeferredWorkTimer::Ticket, DeferredWorkTimer::TicketData&&) mutable {
     274    auto ticket = vm.deferredWorkTimer->addPendingWork(vm, promise, WTFMove(dependencies));
     275    Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([ticket, promise, importObject, globalObject, &vm] (Wasm::Module::ValidationResult&& result) mutable {
     276        vm.deferredWorkTimer->scheduleWorkSoon(ticket, [promise, importObject, globalObject, result = WTFMove(result), &vm](DeferredWorkTimer::Ticket) mutable {
    277277            auto scope = DECLARE_THROW_SCOPE(vm);
    278278            JSWebAssemblyModule* module = JSWebAssemblyModule::createStub(vm, globalObject, globalObject->webAssemblyModuleStructure(), WTFMove(result));
Note: See TracChangeset for help on using the changeset viewer.