Changeset 235261 in webkit


Ignore:
Timestamp:
Aug 23, 2018 4:57:03 PM (6 years ago)
Author:
sbarati@apple.com
Message:

JSRunLoopTimer may run part of a member function after it's destroyed
https://bugs.webkit.org/show_bug.cgi?id=188426

Reviewed by Mark Lam.

Source/JavaScriptCore:

When I was reading the JSRunLoopTimer code, I noticed that it is possible
to end up running timer code after the class had been destroyed.

The issue I spotted was in this function:
`
void JSRunLoopTimer::timerDidFire()
{

JSLock* apiLock = m_apiLock.get();
if (!apiLock) {

Likely a buggy usage: the timer fired while JSRunLoopTimer was being destroyed.
return;

}
HERE
std::lock_guard<JSLock> lock(*apiLock);
RefPtr<VM> vm = apiLock->vm();
if (!vm) {

The VM has been destroyed, so we should just give up.
return;

}

doWork();

}
`

Look at the comment 'HERE'. Let's say that the timer callback thread gets context
switched before grabbing the API lock. Then, some other thread destroys the VM.
And let's say that the VM owns (perhaps transitively) this timer. Then, the
timer would run code and access member variables after it was destroyed.

This patch fixes this issue by introducing a new timer manager class.
This class manages timers on a per VM basis. When a timer is scheduled,
this class refs the timer. It also calls the timer callback while actively
maintaining a +1 ref to it. So, it's no longer possible to call the timer
callback after the timer has been destroyed. However, calling a timer callback
can still race with the VM being destroyed. We continue to detect this case and
bail out of the callback early.

This patch also removes a lot of duplicate code between GCActivityCallback
and JSRunLoopTimer.

  • heap/EdenGCActivityCallback.cpp:

(JSC::EdenGCActivityCallback::doCollection):
(JSC::EdenGCActivityCallback::lastGCLength):
(JSC::EdenGCActivityCallback::deathRate):

  • heap/EdenGCActivityCallback.h:
  • heap/FullGCActivityCallback.cpp:

(JSC::FullGCActivityCallback::doCollection):
(JSC::FullGCActivityCallback::lastGCLength):
(JSC::FullGCActivityCallback::deathRate):

  • heap/FullGCActivityCallback.h:
  • heap/GCActivityCallback.cpp:

(JSC::GCActivityCallback::doWork):
(JSC::GCActivityCallback::scheduleTimer):
(JSC::GCActivityCallback::didAllocate):
(JSC::GCActivityCallback::willCollect):
(JSC::GCActivityCallback::cancel):
(JSC::GCActivityCallback::cancelTimer): Deleted.
(JSC::GCActivityCallback::nextFireTime): Deleted.

  • heap/GCActivityCallback.h:
  • heap/Heap.cpp:

(JSC::Heap::reportAbandonedObjectGraph):
(JSC::Heap::notifyIncrementalSweeper):
(JSC::Heap::updateAllocationLimits):
(JSC::Heap::didAllocate):

  • heap/IncrementalSweeper.cpp:

(JSC::IncrementalSweeper::scheduleTimer):
(JSC::IncrementalSweeper::doWork):
(JSC::IncrementalSweeper::doSweep):
(JSC::IncrementalSweeper::sweepNextBlock):
(JSC::IncrementalSweeper::startSweeping):
(JSC::IncrementalSweeper::stopSweeping):

  • heap/IncrementalSweeper.h:
  • heap/StopIfNecessaryTimer.cpp:

(JSC::StopIfNecessaryTimer::doWork):
(JSC::StopIfNecessaryTimer::scheduleSoon):

  • heap/StopIfNecessaryTimer.h:
  • runtime/JSRunLoopTimer.cpp:

(JSC::epochTime):
(JSC::JSRunLoopTimer::Manager::timerDidFireCallback):
(JSC::JSRunLoopTimer::Manager::PerVMData::setRunLoop):
(JSC::JSRunLoopTimer::Manager::PerVMData::PerVMData):
(JSC::JSRunLoopTimer::Manager::PerVMData::~PerVMData):
(JSC::JSRunLoopTimer::Manager::timerDidFire):
(JSC::JSRunLoopTimer::Manager::shared):
(JSC::JSRunLoopTimer::Manager::registerVM):
(JSC::JSRunLoopTimer::Manager::unregisterVM):
(JSC::JSRunLoopTimer::Manager::scheduleTimer):
(JSC::JSRunLoopTimer::Manager::cancelTimer):
(JSC::JSRunLoopTimer::Manager::timeUntilFire):
(JSC::JSRunLoopTimer::Manager::didChangeRunLoop):
(JSC::JSRunLoopTimer::timerDidFire):
(JSC::JSRunLoopTimer::JSRunLoopTimer):
(JSC::JSRunLoopTimer::timeUntilFire):
(JSC::JSRunLoopTimer::setTimeUntilFire):
(JSC::JSRunLoopTimer::cancelTimer):
(JSC::JSRunLoopTimer::setRunLoop): Deleted.
(JSC::JSRunLoopTimer::timerDidFireCallback): Deleted.
(JSC::JSRunLoopTimer::scheduleTimer): Deleted.

  • runtime/JSRunLoopTimer.h:

(JSC::JSRunLoopTimer::Manager::PerVMData::PerVMData):

  • runtime/PromiseDeferredTimer.cpp:

(JSC::PromiseDeferredTimer::doWork):
(JSC::PromiseDeferredTimer::runRunLoop):
(JSC::PromiseDeferredTimer::addPendingPromise):
(JSC::PromiseDeferredTimer::hasPendingPromise):
(JSC::PromiseDeferredTimer::hasDependancyInPendingPromise):
(JSC::PromiseDeferredTimer::cancelPendingPromise):
(JSC::PromiseDeferredTimer::scheduleWorkSoon):

  • runtime/PromiseDeferredTimer.h:
  • runtime/VM.cpp:

(JSC::VM::VM):
(JSC::VM::~VM):
(JSC::VM::setRunLoop):
(JSC::VM::registerRunLoopTimer): Deleted.
(JSC::VM::unregisterRunLoopTimer): Deleted.

  • runtime/VM.h:

(JSC::VM::runLoop const):

  • wasm/js/WebAssemblyPrototype.cpp:

(JSC::webAssemblyModuleValidateAsyncInternal):
(JSC::instantiate):
(JSC::compileAndInstantiate):
(JSC::webAssemblyModuleInstantinateAsyncInternal):
(JSC::webAssemblyCompileStreamingInternal):
(JSC::webAssemblyInstantiateStreamingInternal):

Source/WebCore:

  • page/cocoa/ResourceUsageThreadCocoa.mm:

(WebCore::ResourceUsageThread::platformThreadBody):

  • page/linux/ResourceUsageThreadLinux.cpp:

(WebCore::ResourceUsageThread::platformThreadBody):

Location:
trunk/Source
Files:
22 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r235254 r235261  
     12018-08-23  Saam barati  <sbarati@apple.com>
     2
     3        JSRunLoopTimer may run part of a member function after it's destroyed
     4        https://bugs.webkit.org/show_bug.cgi?id=188426
     5
     6        Reviewed by Mark Lam.
     7
     8        When I was reading the JSRunLoopTimer code, I noticed that it is possible
     9        to end up running timer code after the class had been destroyed.
     10       
     11        The issue I spotted was in this function:
     12        ```
     13        void JSRunLoopTimer::timerDidFire()
     14        {
     15            JSLock* apiLock = m_apiLock.get();
     16            if (!apiLock) {
     17                // Likely a buggy usage: the timer fired while JSRunLoopTimer was being destroyed.
     18                return;
     19            }
     20            // HERE
     21            std::lock_guard<JSLock> lock(*apiLock);
     22            RefPtr<VM> vm = apiLock->vm();
     23            if (!vm) {
     24                // The VM has been destroyed, so we should just give up.
     25                return;
     26            }
     27       
     28            doWork();
     29        }
     30        ```
     31       
     32        Look at the comment 'HERE'. Let's say that the timer callback thread gets context
     33        switched before grabbing the API lock. Then, some other thread destroys the VM.
     34        And let's say that the VM owns (perhaps transitively) this timer. Then, the
     35        timer would run code and access member variables after it was destroyed.
     36       
     37        This patch fixes this issue by introducing a new timer manager class.
     38        This class manages timers on a per VM basis. When a timer is scheduled,
     39        this class refs the timer. It also calls the timer callback while actively
     40        maintaining a +1 ref to it. So, it's no longer possible to call the timer
     41        callback after the timer has been destroyed. However, calling a timer callback
     42        can still race with the VM being destroyed. We continue to detect this case and
     43        bail out of the callback early.
     44       
     45        This patch also removes a lot of duplicate code between GCActivityCallback
     46        and JSRunLoopTimer.
     47
     48        * heap/EdenGCActivityCallback.cpp:
     49        (JSC::EdenGCActivityCallback::doCollection):
     50        (JSC::EdenGCActivityCallback::lastGCLength):
     51        (JSC::EdenGCActivityCallback::deathRate):
     52        * heap/EdenGCActivityCallback.h:
     53        * heap/FullGCActivityCallback.cpp:
     54        (JSC::FullGCActivityCallback::doCollection):
     55        (JSC::FullGCActivityCallback::lastGCLength):
     56        (JSC::FullGCActivityCallback::deathRate):
     57        * heap/FullGCActivityCallback.h:
     58        * heap/GCActivityCallback.cpp:
     59        (JSC::GCActivityCallback::doWork):
     60        (JSC::GCActivityCallback::scheduleTimer):
     61        (JSC::GCActivityCallback::didAllocate):
     62        (JSC::GCActivityCallback::willCollect):
     63        (JSC::GCActivityCallback::cancel):
     64        (JSC::GCActivityCallback::cancelTimer): Deleted.
     65        (JSC::GCActivityCallback::nextFireTime): Deleted.
     66        * heap/GCActivityCallback.h:
     67        * heap/Heap.cpp:
     68        (JSC::Heap::reportAbandonedObjectGraph):
     69        (JSC::Heap::notifyIncrementalSweeper):
     70        (JSC::Heap::updateAllocationLimits):
     71        (JSC::Heap::didAllocate):
     72        * heap/IncrementalSweeper.cpp:
     73        (JSC::IncrementalSweeper::scheduleTimer):
     74        (JSC::IncrementalSweeper::doWork):
     75        (JSC::IncrementalSweeper::doSweep):
     76        (JSC::IncrementalSweeper::sweepNextBlock):
     77        (JSC::IncrementalSweeper::startSweeping):
     78        (JSC::IncrementalSweeper::stopSweeping):
     79        * heap/IncrementalSweeper.h:
     80        * heap/StopIfNecessaryTimer.cpp:
     81        (JSC::StopIfNecessaryTimer::doWork):
     82        (JSC::StopIfNecessaryTimer::scheduleSoon):
     83        * heap/StopIfNecessaryTimer.h:
     84        * runtime/JSRunLoopTimer.cpp:
     85        (JSC::epochTime):
     86        (JSC::JSRunLoopTimer::Manager::timerDidFireCallback):
     87        (JSC::JSRunLoopTimer::Manager::PerVMData::setRunLoop):
     88        (JSC::JSRunLoopTimer::Manager::PerVMData::PerVMData):
     89        (JSC::JSRunLoopTimer::Manager::PerVMData::~PerVMData):
     90        (JSC::JSRunLoopTimer::Manager::timerDidFire):
     91        (JSC::JSRunLoopTimer::Manager::shared):
     92        (JSC::JSRunLoopTimer::Manager::registerVM):
     93        (JSC::JSRunLoopTimer::Manager::unregisterVM):
     94        (JSC::JSRunLoopTimer::Manager::scheduleTimer):
     95        (JSC::JSRunLoopTimer::Manager::cancelTimer):
     96        (JSC::JSRunLoopTimer::Manager::timeUntilFire):
     97        (JSC::JSRunLoopTimer::Manager::didChangeRunLoop):
     98        (JSC::JSRunLoopTimer::timerDidFire):
     99        (JSC::JSRunLoopTimer::JSRunLoopTimer):
     100        (JSC::JSRunLoopTimer::timeUntilFire):
     101        (JSC::JSRunLoopTimer::setTimeUntilFire):
     102        (JSC::JSRunLoopTimer::cancelTimer):
     103        (JSC::JSRunLoopTimer::setRunLoop): Deleted.
     104        (JSC::JSRunLoopTimer::timerDidFireCallback): Deleted.
     105        (JSC::JSRunLoopTimer::scheduleTimer): Deleted.
     106        * runtime/JSRunLoopTimer.h:
     107        (JSC::JSRunLoopTimer::Manager::PerVMData::PerVMData):
     108        * runtime/PromiseDeferredTimer.cpp:
     109        (JSC::PromiseDeferredTimer::doWork):
     110        (JSC::PromiseDeferredTimer::runRunLoop):
     111        (JSC::PromiseDeferredTimer::addPendingPromise):
     112        (JSC::PromiseDeferredTimer::hasPendingPromise):
     113        (JSC::PromiseDeferredTimer::hasDependancyInPendingPromise):
     114        (JSC::PromiseDeferredTimer::cancelPendingPromise):
     115        (JSC::PromiseDeferredTimer::scheduleWorkSoon):
     116        * runtime/PromiseDeferredTimer.h:
     117        * runtime/VM.cpp:
     118        (JSC::VM::VM):
     119        (JSC::VM::~VM):
     120        (JSC::VM::setRunLoop):
     121        (JSC::VM::registerRunLoopTimer): Deleted.
     122        (JSC::VM::unregisterRunLoopTimer): Deleted.
     123        * runtime/VM.h:
     124        (JSC::VM::runLoop const):
     125        * wasm/js/WebAssemblyPrototype.cpp:
     126        (JSC::webAssemblyModuleValidateAsyncInternal):
     127        (JSC::instantiate):
     128        (JSC::compileAndInstantiate):
     129        (JSC::webAssemblyModuleInstantinateAsyncInternal):
     130        (JSC::webAssemblyCompileStreamingInternal):
     131        (JSC::webAssemblyInstantiateStreamingInternal):
     132
    11332018-08-23  Mark Lam  <mark.lam@apple.com>
    2134
  • trunk/Source/JavaScriptCore/heap/EdenGCActivityCallback.cpp

    r235152 r235261  
    3737}
    3838
    39 void EdenGCActivityCallback::doCollection()
     39void EdenGCActivityCallback::doCollection(VM& vm)
    4040{
    41     m_vm->heap.collectAsync(CollectionScope::Eden);
     41    vm.heap.collectAsync(CollectionScope::Eden);
    4242}
    4343
    44 Seconds EdenGCActivityCallback::lastGCLength()
     44Seconds EdenGCActivityCallback::lastGCLength(Heap& heap)
    4545{
    46     return m_vm->heap.lastEdenGCLength();
     46    return heap.lastEdenGCLength();
    4747}
    4848
    49 double EdenGCActivityCallback::deathRate()
     49double EdenGCActivityCallback::deathRate(Heap& heap)
    5050{
    51     Heap* heap = &m_vm->heap;
    52     size_t sizeBefore = heap->sizeBeforeLastEdenCollection();
    53     size_t sizeAfter = heap->sizeAfterLastEdenCollection();
     51    size_t sizeBefore = heap.sizeBeforeLastEdenCollection();
     52    size_t sizeAfter = heap.sizeAfterLastEdenCollection();
    5453    if (!sizeBefore)
    5554        return 1.0;
  • trunk/Source/JavaScriptCore/heap/EdenGCActivityCallback.h

    r235152 r235261  
    3434    EdenGCActivityCallback(Heap*);
    3535
    36     void doCollection() override;
     36    void doCollection(VM&) override;
    3737
    3838protected:
    39     Seconds lastGCLength() override;
     39    Seconds lastGCLength(Heap&) override;
    4040    double gcTimeSlice(size_t bytes) override;
    41     double deathRate() override;
     41    double deathRate(Heap&) override;
    4242};
    4343
  • trunk/Source/JavaScriptCore/heap/FullGCActivityCallback.cpp

    r235152 r235261  
    4040}
    4141
    42 void FullGCActivityCallback::doCollection()
     42void FullGCActivityCallback::doCollection(VM& vm)
    4343{
    44     Heap& heap = m_vm->heap;
     44    Heap& heap = vm.heap;
    4545    m_didGCRecently = false;
    4646
     
    5757}
    5858
    59 Seconds FullGCActivityCallback::lastGCLength()
     59Seconds FullGCActivityCallback::lastGCLength(Heap& heap)
    6060{
    61     return m_vm->heap.lastFullGCLength();
     61    return heap.lastFullGCLength();
    6262}
    6363
    64 double FullGCActivityCallback::deathRate()
     64double FullGCActivityCallback::deathRate(Heap& heap)
    6565{
    66     Heap* heap = &m_vm->heap;
    67     size_t sizeBefore = heap->sizeBeforeLastFullCollection();
    68     size_t sizeAfter = heap->sizeAfterLastFullCollection();
     66    size_t sizeBefore = heap.sizeBeforeLastFullCollection();
     67    size_t sizeAfter = heap.sizeAfterLastFullCollection();
    6968    if (!sizeBefore)
    7069        return 1.0;
  • trunk/Source/JavaScriptCore/heap/FullGCActivityCallback.h

    r235152 r235261  
    3434    FullGCActivityCallback(Heap*);
    3535
    36     void doCollection() override;
     36    void doCollection(VM&) override;
    3737
    3838    bool didGCRecently() const { return m_didGCRecently; }
     
    4040
    4141protected:
    42     Seconds lastGCLength() override;
     42    Seconds lastGCLength(Heap&) override;
    4343    double gcTimeSlice(size_t bytes) override;
    44     double deathRate() override;
     44    double deathRate(Heap&) override;
    4545
    4646    bool m_didGCRecently { false };
  • trunk/Source/JavaScriptCore/heap/GCActivityCallback.cpp

    r235152 r235261  
    11/*
    2  * Copyright (C) 2010-2017 Apple Inc. All rights reserved.
     2 * Copyright (C) 2010-2018 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    4646}
    4747
    48 void GCActivityCallback::doWork()
     48void GCActivityCallback::doWork(VM& vm)
    4949{
    50     Heap* heap = &m_vm->heap;
    5150    if (!isEnabled())
    5251        return;
    5352   
    54     JSLockHolder locker(m_vm);
    55     if (heap->isDeferred()) {
     53    ASSERT(vm.currentThreadIsHoldingAPILock());
     54    Heap& heap = vm.heap;
     55    if (heap.isDeferred()) {
    5656        scheduleTimer(0_s);
    5757        return;
    5858    }
    5959
    60     doCollection();
     60    doCollection(vm);
    6161}
    6262
    63 #if USE(CF)
    6463void GCActivityCallback::scheduleTimer(Seconds newDelay)
    6564{
     
    6867    Seconds delta = m_delay - newDelay;
    6968    m_delay = newDelay;
    70     CFRunLoopTimerSetNextFireDate(m_timer.get(), CFRunLoopTimerGetNextFireDate(m_timer.get()) - delta.seconds());
     69    if (auto timeUntilFire = this->timeUntilFire())
     70        setTimeUntilFire(*timeUntilFire - delta);
     71    else
     72        setTimeUntilFire(newDelay);
    7173}
    7274
    73 void GCActivityCallback::cancelTimer()
    74 {
    75     m_delay = s_decade;
    76     CFRunLoopTimerSetNextFireDate(m_timer.get(), CFAbsoluteTimeGetCurrent() + s_decade.seconds());
    77 }
    78 
    79 MonotonicTime GCActivityCallback::nextFireTime()
    80 {
    81     return MonotonicTime::now() + Seconds(CFRunLoopTimerGetNextFireDate(m_timer.get()) - CFAbsoluteTimeGetCurrent());
    82 }
    83 #else
    84 void GCActivityCallback::scheduleTimer(Seconds newDelay)
    85 {
    86     if (newDelay * timerSlop > m_delay)
    87         return;
    88     Seconds delta = m_delay - newDelay;
    89     m_delay = newDelay;
    90 
    91     Seconds secondsUntilFire = m_timer.secondsUntilFire();
    92     m_timer.startOneShot(std::max<Seconds>(secondsUntilFire - delta, 0_s));
    93 }
    94 
    95 void GCActivityCallback::cancelTimer()
    96 {
    97     m_delay = s_decade;
    98     m_timer.startOneShot(s_decade);
    99 }
    100 
    101 MonotonicTime GCActivityCallback::nextFireTime()
    102 {
    103     return MonotonicTime::now() + m_timer.secondsUntilFire();
    104 }
    105 #endif
    106 
    107 void GCActivityCallback::didAllocate(size_t bytes)
     75void GCActivityCallback::didAllocate(Heap& heap, size_t bytes)
    10876{
    10977    // The first byte allocated in an allocation cycle will report 0 bytes to didAllocate.
     
    11179    if (!bytes)
    11280        bytes = 1;
    113     double bytesExpectedToReclaim = static_cast<double>(bytes) * deathRate();
    114     Seconds newDelay = lastGCLength() / gcTimeSlice(bytesExpectedToReclaim);
     81    double bytesExpectedToReclaim = static_cast<double>(bytes) * deathRate(heap);
     82    Seconds newDelay = lastGCLength(heap) / gcTimeSlice(bytesExpectedToReclaim);
    11583    scheduleTimer(newDelay);
    11684}
     
    11886void GCActivityCallback::willCollect()
    11987{
    120     cancelTimer();
     88    cancel();
    12189}
    12290
    12391void GCActivityCallback::cancel()
    12492{
     93    m_delay = s_decade;
    12594    cancelTimer();
    12695}
  • trunk/Source/JavaScriptCore/heap/GCActivityCallback.h

    r235152 r235261  
    4949    GCActivityCallback(Heap*);
    5050
    51     void doWork() override;
     51    void doWork(VM&) override;
    5252
    53     virtual void doCollection() = 0;
     53    virtual void doCollection(VM&) = 0;
    5454
    55     virtual void didAllocate(size_t);
    56     virtual void willCollect();
    57     virtual void cancel();
     55    void didAllocate(Heap&, size_t);
     56    void willCollect();
     57    void cancel();
    5858    bool isEnabled() const { return m_enabled; }
    5959    void setEnabled(bool enabled) { m_enabled = enabled; }
     
    6161    static bool s_shouldCreateGCTimer;
    6262
    63     MonotonicTime nextFireTime();
    64 
    6563protected:
    66     virtual Seconds lastGCLength() = 0;
     64    virtual Seconds lastGCLength(Heap&) = 0;
    6765    virtual double gcTimeSlice(size_t bytes) = 0;
    68     virtual double deathRate() = 0;
     66    virtual double deathRate(Heap&) = 0;
    6967
    7068    GCActivityCallback(VM* vm)
     
    7876
    7977protected:
    80     void cancelTimer();
    8178    void scheduleTimer(Seconds);
    8279
  • trunk/Source/JavaScriptCore/heap/Heap.cpp

    r235152 r235261  
    540540    // we hasten the next collection by pretending that we've allocated more memory.
    541541    if (m_fullActivityCallback) {
    542         m_fullActivityCallback->didAllocate(
     542        m_fullActivityCallback->didAllocate(*this,
    543543            m_sizeAfterLastCollect - m_sizeAfterLastFullCollect + m_bytesAllocatedThisCycle + m_bytesAbandonedSinceLastFullCollect);
    544544    }
     
    21952195    }
    21962196
    2197     m_sweeper->startSweeping();
     2197    m_sweeper->startSweeping(*this);
    21982198}
    21992199
     
    22742274        if (m_fullActivityCallback) {
    22752275            ASSERT(currentHeapSize >= m_sizeAfterLastFullCollect);
    2276             m_fullActivityCallback->didAllocate(currentHeapSize - m_sizeAfterLastFullCollect);
     2276            m_fullActivityCallback->didAllocate(*this, currentHeapSize - m_sizeAfterLastFullCollect);
    22772277        }
    22782278    }
     
    23552355{
    23562356    if (m_edenActivityCallback)
    2357         m_edenActivityCallback->didAllocate(m_bytesAllocatedThisCycle + m_bytesAbandonedSinceLastFullCollect);
     2357        m_edenActivityCallback->didAllocate(*this, m_bytesAllocatedThisCycle + m_bytesAbandonedSinceLastFullCollect);
    23582358    m_bytesAllocatedThisCycle += bytes;
    23592359    performIncrement(bytes);
  • trunk/Source/JavaScriptCore/heap/IncrementalSweeper.cpp

    r235152 r235261  
    11/*
    2  * Copyright (C) 2012, 2016 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3535namespace JSC {
    3636
    37 static const Seconds sweepTimeSlice = 10_ms; // seconds
     37static const Seconds sweepTimeSlice = 10_ms;
    3838static const double sweepTimeTotal = .10;
    3939static const double sweepTimeMultiplier = 1.0 / sweepTimeTotal;
     
    4141void IncrementalSweeper::scheduleTimer()
    4242{
    43     Base::scheduleTimer(sweepTimeSlice * sweepTimeMultiplier);
     43    setTimeUntilFire(sweepTimeSlice * sweepTimeMultiplier);
    4444}
    4545
     
    5050}
    5151
    52 void IncrementalSweeper::doWork()
     52void IncrementalSweeper::doWork(VM& vm)
    5353{
    54     doSweep(MonotonicTime::now());
     54    doSweep(vm, MonotonicTime::now());
    5555}
    5656
    57 void IncrementalSweeper::doSweep(MonotonicTime sweepBeginTime)
     57void IncrementalSweeper::doSweep(VM& vm, MonotonicTime sweepBeginTime)
    5858{
    59     while (sweepNextBlock()) {
     59    while (sweepNextBlock(vm)) {
    6060        Seconds elapsedTime = MonotonicTime::now() - sweepBeginTime;
    6161        if (elapsedTime < sweepTimeSlice)
     
    7373}
    7474
    75 bool IncrementalSweeper::sweepNextBlock()
     75bool IncrementalSweeper::sweepNextBlock(VM& vm)
    7676{
    77     m_vm->heap.stopIfNecessary();
     77    vm.heap.stopIfNecessary();
    7878
    7979    MarkedBlock::Handle* block = nullptr;
     
    8686   
    8787    if (block) {
    88         DeferGCForAWhile deferGC(m_vm->heap);
     88        DeferGCForAWhile deferGC(vm.heap);
    8989        block->sweep(nullptr);
    90         m_vm->heap.objectSpace().freeOrShrinkBlock(block);
     90        vm.heap.objectSpace().freeOrShrinkBlock(block);
    9191        return true;
    9292    }
    9393
    94     return m_vm->heap.sweepNextLogicallyEmptyWeakBlock();
     94    return vm.heap.sweepNextLogicallyEmptyWeakBlock();
    9595}
    9696
    97 void IncrementalSweeper::startSweeping()
     97void IncrementalSweeper::startSweeping(Heap& heap)
    9898{
    9999    scheduleTimer();
    100     m_currentDirectory = m_vm->heap.objectSpace().firstDirectory();
     100    m_currentDirectory = heap.objectSpace().firstDirectory();
    101101}
    102102
     
    104104{
    105105    m_currentDirectory = nullptr;
    106     if (m_vm)
    107         cancelTimer();
     106    cancelTimer();
    108107}
    109108
  • trunk/Source/JavaScriptCore/heap/IncrementalSweeper.h

    r235152 r235261  
    3838    JS_EXPORT_PRIVATE explicit IncrementalSweeper(Heap*);
    3939
    40     JS_EXPORT_PRIVATE void startSweeping();
     40    JS_EXPORT_PRIVATE void startSweeping(Heap&);
    4141    void freeFastMallocMemoryAfterSweeping() { m_shouldFreeFastMallocMemoryAfterSweeping = true; }
    4242
    43     JS_EXPORT_PRIVATE void doWork() override;
    44     bool sweepNextBlock();
    45     JS_EXPORT_PRIVATE void stopSweeping();
     43    void doWork(VM&) override;
     44    void stopSweeping();
    4645
    4746private:
    48     void doSweep(MonotonicTime startTime);
     47    bool sweepNextBlock(VM&);
     48    void doSweep(VM&, MonotonicTime startTime);
    4949    void scheduleTimer();
    5050   
  • trunk/Source/JavaScriptCore/heap/StopIfNecessaryTimer.cpp

    r235152 r235261  
    11/*
    2  * Copyright (C) 2016 Apple Inc. All rights reserved.
     2 * Copyright (C) 2016-2018 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    3636}
    3737
    38 void StopIfNecessaryTimer::doWork()
     38void StopIfNecessaryTimer::doWork(VM& vm)
    3939{
    4040    cancelTimer();
    4141    WTF::storeStoreFence();
    42     m_vm->heap.stopIfNecessary();
     42    vm.heap.stopIfNecessary();
    4343}
    4444
     
    4949        return;
    5050    }
    51     scheduleTimer(0_s);
     51    setTimeUntilFire(0_s);
    5252}
    5353
  • trunk/Source/JavaScriptCore/heap/StopIfNecessaryTimer.h

    r235152 r235261  
    3737    explicit StopIfNecessaryTimer(VM*);
    3838   
    39     void doWork() override;
     39    void doWork(VM&) override;
    4040   
    4141    void scheduleSoon();
  • trunk/Source/JavaScriptCore/runtime/JSRunLoopTimer.cpp

    r235152 r235261  
    11/*
    2  * Copyright (C) 2012-2017 Apple Inc. All rights reserved.
     2 * Copyright (C) 2012-2018 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2727#include "JSRunLoopTimer.h"
    2828
    29 #include "GCActivityCallback.h"
    3029#include "IncrementalSweeper.h"
    3130#include "JSCInlines.h"
     
    3433
    3534#include <wtf/MainThread.h>
     35#include <wtf/NoTailCalls.h>
    3636#include <wtf/Threading.h>
    3737
     
    4747const Seconds JSRunLoopTimer::s_decade { 60 * 60 * 24 * 365 * 10 };
    4848
     49static inline JSRunLoopTimer::Manager::EpochTime epochTime(Seconds delay)
     50{
     51#if USE(CF)
     52    return Seconds { CFAbsoluteTimeGetCurrent() + delay.value() };
     53#else
     54    return MonotonicTime::now().secondsSinceEpoch() + delay;
     55#endif
     56}
     57
     58#if USE(CF)
     59void JSRunLoopTimer::Manager::timerDidFireCallback(CFRunLoopTimerRef, void* contextPtr)
     60{
     61    static_cast<JSRunLoopTimer::Manager*>(contextPtr)->timerDidFire();
     62}
     63
     64void JSRunLoopTimer::Manager::PerVMData::setRunLoop(Manager* manager, CFRunLoopRef newRunLoop)
     65{
     66    if (runLoop) {
     67        CFRunLoopRemoveTimer(runLoop.get(), timer.get(), kCFRunLoopCommonModes);
     68        CFRunLoopTimerInvalidate(timer.get());
     69        runLoop.clear();
     70        timer.clear();
     71    }
     72
     73    if (newRunLoop) {
     74        runLoop = newRunLoop;
     75        memset(&context, 0, sizeof(CFRunLoopTimerContext));
     76        RELEASE_ASSERT(manager);
     77        context.info = manager;
     78        timer = adoptCF(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + s_decade.seconds(), CFAbsoluteTimeGetCurrent() + s_decade.seconds(), 0, 0, JSRunLoopTimer::Manager::timerDidFireCallback, &context));
     79        CFRunLoopAddTimer(runLoop.get(), timer.get(), kCFRunLoopCommonModes);
     80
     81        EpochTime scheduleTime = epochTime(s_decade);
     82        for (auto& pair : timers)
     83            scheduleTime = std::min(pair.second, scheduleTime);
     84        CFRunLoopTimerSetNextFireDate(timer.get(), scheduleTime.value());
     85    }
     86}
     87#else
     88JSRunLoopTimer::Manager::PerVMData::PerVMData(Manager& manager)
     89    : runLoop(&RunLoop::current())
     90    , timer(std::make_unique<RunLoop::Timer<Manager>>(*runLoop, &manager, &JSRunLoopTimer::Manager::timerDidFireCallback))
     91{
     92#if USE(GLIB_EVENT_LOOP)
     93    timer->setPriority(RunLoopSourcePriority::JavascriptTimer);
     94    timer->setName("[JavaScriptCore] JSRunLoopTimer");
     95#endif
     96}
     97
     98void JSRunLoopTimer::Manager::timerDidFireCallback()
     99{
     100    timerDidFire();
     101}
     102#endif
     103
     104JSRunLoopTimer::Manager::PerVMData::~PerVMData()
     105{
     106#if USE(CF)
     107    setRunLoop(nullptr, nullptr);
     108#endif
     109}
     110
     111void JSRunLoopTimer::Manager::timerDidFire()
     112{
     113    Vector<Ref<JSRunLoopTimer>> timersToFire;
     114
     115    {
     116        auto locker = holdLock(m_lock);
     117#if USE(CF)
     118        CFRunLoopRef currentRunLoop = CFRunLoopGetCurrent();
     119#else
     120        RunLoop* currentRunLoop = &RunLoop::current();
     121#endif
     122        EpochTime nowEpochTime = epochTime(0_s);
     123        for (auto& entry : m_mapping) {
     124            PerVMData& data = entry.value;
     125#if USE(CF)
     126            if (data.runLoop.get() != currentRunLoop)
     127                continue;
     128#else
     129            if (data.runLoop != currentRunLoop)
     130                continue;
     131#endif
     132           
     133            EpochTime scheduleTime = epochTime(s_decade);
     134            for (size_t i = 0; i < data.timers.size(); ++i) {
     135                {
     136                    auto& pair = data.timers[i];
     137                    if (pair.second > nowEpochTime) {
     138                        scheduleTime = std::min(pair.second, scheduleTime);
     139                        continue;
     140                    }
     141                    auto& last = data.timers.last();
     142                    if (&last != &pair)
     143                        std::swap(pair, last);
     144                    --i;
     145                }
     146
     147                auto pair = data.timers.takeLast();
     148                timersToFire.append(WTFMove(pair.first));
     149            }
     150
     151#if USE(CF)
     152            CFRunLoopTimerSetNextFireDate(data.timer.get(), scheduleTime.value());
     153#else
     154            data.timer->startOneShot(std::max(0_s, scheduleTime - MonotonicTime::now().secondsSinceEpoch()));
     155#endif
     156        }
     157    }
     158
     159    for (auto& timer : timersToFire)
     160        timer->timerDidFire();
     161}
     162
     163JSRunLoopTimer::Manager& JSRunLoopTimer::Manager::shared()
     164{
     165    static Manager* manager;
     166    static std::once_flag once;
     167    std::call_once(once, [&] {
     168        manager = new Manager;
     169    });
     170    return *manager;
     171}
     172
     173void JSRunLoopTimer::Manager::registerVM(VM& vm)
     174{
     175    PerVMData data { *this };
     176#if USE(CF)
     177    data.setRunLoop(this, vm.runLoop());
     178#endif
     179
     180    auto locker = holdLock(m_lock);
     181    auto addResult = m_mapping.add({ vm.apiLock() }, WTFMove(data));
     182    RELEASE_ASSERT(addResult.isNewEntry);
     183}
     184
     185void JSRunLoopTimer::Manager::unregisterVM(VM& vm)
     186{
     187    auto locker = holdLock(m_lock);
     188
     189    auto iter = m_mapping.find({ vm.apiLock() });
     190    RELEASE_ASSERT(iter != m_mapping.end());
     191    m_mapping.remove(iter);
     192}
     193
     194void JSRunLoopTimer::Manager::scheduleTimer(JSRunLoopTimer& timer, Seconds delay)
     195{
     196    EpochTime fireEpochTime = epochTime(delay);
     197
     198    auto locker = holdLock(m_lock);
     199    auto iter = m_mapping.find(timer.m_apiLock);
     200    RELEASE_ASSERT(iter != m_mapping.end()); // We don't allow calling this after the VM dies.
     201
     202    PerVMData& data = iter->value;
     203    EpochTime scheduleTime = fireEpochTime;
     204    bool found = false;
     205    for (auto& entry : data.timers) {
     206        if (entry.first.ptr() == &timer) {
     207            entry.second = fireEpochTime;
     208            found = true;
     209        }
     210        scheduleTime = std::min(scheduleTime, entry.second);
     211    }
     212
     213    if (!found)
     214        data.timers.append({ timer, fireEpochTime });
     215
     216#if USE(CF)
     217    CFRunLoopTimerSetNextFireDate(data.timer.get(), scheduleTime.value());
     218#else
     219    data.timer->startOneShot(std::max(0_s, scheduleTime - MonotonicTime::now().secondsSinceEpoch()));
     220#endif
     221}
     222
     223void JSRunLoopTimer::Manager::cancelTimer(JSRunLoopTimer& timer)
     224{
     225    auto locker = holdLock(m_lock);
     226    auto iter = m_mapping.find(timer.m_apiLock);
     227    if (iter == m_mapping.end()) {
     228        // It's trivial to allow this to be called after the VM dies, so we allow for it.
     229        return;
     230    }
     231
     232    PerVMData& data = iter->value;
     233    EpochTime scheduleTime = epochTime(s_decade);
     234    for (unsigned i = 0; i < data.timers.size(); ++i) {
     235        {
     236            auto& entry = data.timers[i];
     237            if (entry.first.ptr() == &timer) {
     238                RELEASE_ASSERT(timer.refCount() >= 2); // If we remove it from the entry below, we should not be the last thing pointing to it!
     239                auto& last = data.timers.last();
     240                if (&last != &entry)
     241                    std::swap(entry, last);
     242                data.timers.removeLast();
     243                i--;
     244                continue;
     245            }
     246        }
     247
     248        scheduleTime = std::min(scheduleTime, data.timers[i].second);
     249    }
     250
     251#if USE(CF)
     252    CFRunLoopTimerSetNextFireDate(data.timer.get(), scheduleTime.value());
     253#else
     254    data.timer->startOneShot(std::max(0_s, scheduleTime - MonotonicTime::now().secondsSinceEpoch()));
     255#endif
     256}
     257
     258std::optional<Seconds> JSRunLoopTimer::Manager::timeUntilFire(JSRunLoopTimer& timer)
     259{
     260    auto locker = holdLock(m_lock);
     261    auto iter = m_mapping.find(timer.m_apiLock);
     262    RELEASE_ASSERT(iter != m_mapping.end()); // We only allow this to be called with a live VM.
     263
     264    PerVMData& data = iter->value;
     265    for (auto& entry : data.timers) {
     266        if (entry.first.ptr() == &timer) {
     267            EpochTime nowEpochTime = epochTime(0_s);
     268            return entry.second - nowEpochTime;
     269        }
     270    }
     271
     272    return std::nullopt;
     273}
     274
     275#if USE(CF)
     276void JSRunLoopTimer::Manager::didChangeRunLoop(VM& vm, CFRunLoopRef newRunLoop)
     277{
     278    auto locker = holdLock(m_lock);
     279    auto iter = m_mapping.find({ vm.apiLock() });
     280    RELEASE_ASSERT(iter != m_mapping.end());
     281
     282    PerVMData& data = iter->value;
     283    data.setRunLoop(this, newRunLoop);
     284}
     285#endif
     286
    49287void JSRunLoopTimer::timerDidFire()
    50288{
    51     JSLock* apiLock = m_apiLock.get();
    52     if (!apiLock) {
    53         // Likely a buggy usage: the timer fired while JSRunLoopTimer was being destroyed.
    54         return;
    55     }
    56 
    57     std::lock_guard<JSLock> lock(*apiLock);
    58     RefPtr<VM> vm = apiLock->vm();
     289    NO_TAIL_CALLS();
     290
     291    {
     292        auto locker = holdLock(m_lock);
     293        if (!m_isScheduled) {
     294            // We raced between this callback being called and cancel() being called.
     295            // That's fine, we just don't do anything here.
     296            return;
     297        }
     298    }
     299
     300    std::lock_guard<JSLock> lock(m_apiLock.get());
     301    RefPtr<VM> vm = m_apiLock->vm();
    59302    if (!vm) {
    60303        // The VM has been destroyed, so we should just give up.
     
    62305    }
    63306
    64     doWork();
    65 }
    66 
    67 #if USE(CF)
     307    doWork(*vm);
     308}
    68309
    69310JSRunLoopTimer::JSRunLoopTimer(VM* vm)
    70     : m_vm(vm)
    71     , m_apiLock(&vm->apiLock())
    72 {
    73     m_vm->registerRunLoopTimer(this);
    74 }
    75 
    76 void JSRunLoopTimer::setRunLoop(CFRunLoopRef runLoop)
    77 {
    78     if (m_runLoop) {
    79         CFRunLoopRemoveTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes);
    80         CFRunLoopTimerInvalidate(m_timer.get());
    81         m_runLoop.clear();
    82         m_timer.clear();
    83     }
    84 
    85     m_runLoop = runLoop;
    86     if (runLoop) {
    87         memset(&m_context, 0, sizeof(CFRunLoopTimerContext));
    88         m_context.info = this;
    89         m_timer = adoptCF(CFRunLoopTimerCreate(kCFAllocatorDefault, CFAbsoluteTimeGetCurrent() + s_decade.seconds(), s_decade.seconds(), 0, 0, JSRunLoopTimer::timerDidFireCallback, &m_context));
    90         CFRunLoopAddTimer(m_runLoop.get(), m_timer.get(), kCFRunLoopCommonModes);
    91     }
     311    : m_apiLock(vm->apiLock())
     312{
    92313}
    93314
    94315JSRunLoopTimer::~JSRunLoopTimer()
    95316{
    96     JSLock* apiLock = m_apiLock.get();
    97     std::lock_guard<JSLock> lock(*apiLock);
    98     m_vm->unregisterRunLoopTimer(this);
    99     m_apiLock = nullptr;
    100 }
    101 
    102 void JSRunLoopTimer::timerDidFireCallback(CFRunLoopTimerRef, void* contextPtr)
    103 {
    104     static_cast<JSRunLoopTimer*>(contextPtr)->timerDidFire();
    105 }
    106 
    107 void JSRunLoopTimer::scheduleTimer(Seconds intervalInSeconds)
    108 {
    109     CFRunLoopTimerSetNextFireDate(m_timer.get(), CFAbsoluteTimeGetCurrent() + intervalInSeconds.seconds());
    110     m_isScheduled = true;
     317}
     318
     319std::optional<Seconds> JSRunLoopTimer::timeUntilFire()
     320{
     321    return Manager::shared().timeUntilFire(*this);
     322}
     323
     324void JSRunLoopTimer::setTimeUntilFire(Seconds intervalInSeconds)
     325{
     326    {
     327        auto locker = holdLock(m_lock);
     328        m_isScheduled = true;
     329        Manager::shared().scheduleTimer(*this, intervalInSeconds);
     330    }
     331
    111332    auto locker = holdLock(m_timerCallbacksLock);
    112333    for (auto& task : m_timerSetCallbacks)
     
    116337void JSRunLoopTimer::cancelTimer()
    117338{
    118     CFRunLoopTimerSetNextFireDate(m_timer.get(), CFAbsoluteTimeGetCurrent() + s_decade.seconds());
     339    auto locker = holdLock(m_lock);
    119340    m_isScheduled = false;
    120 }
    121 
    122 #else
    123 
    124 JSRunLoopTimer::JSRunLoopTimer(VM* vm)
    125     : m_vm(vm)
    126     , m_apiLock(&vm->apiLock())
    127     , m_timer(RunLoop::current(), this, &JSRunLoopTimer::timerDidFireCallback)
    128 {
    129 #if USE(GLIB_EVENT_LOOP)
    130     m_timer.setPriority(RunLoopSourcePriority::JavascriptTimer);
    131     m_timer.setName("[JavaScriptCore] JSRunLoopTimer");
    132 #endif
    133     m_timer.startOneShot(s_decade);
    134 }
    135 
    136 JSRunLoopTimer::~JSRunLoopTimer()
    137 {
    138 }
    139 
    140 void JSRunLoopTimer::timerDidFireCallback()
    141 {
    142     m_timer.startOneShot(s_decade);
    143     timerDidFire();
    144 }
    145 
    146 void JSRunLoopTimer::scheduleTimer(Seconds intervalInSeconds)
    147 {
    148     m_timer.startOneShot(intervalInSeconds);
    149     m_isScheduled = true;
    150 
    151     auto locker = holdLock(m_timerCallbacksLock);
    152     for (auto& task : m_timerSetCallbacks)
    153         task->run();
    154 }
    155 
    156 void JSRunLoopTimer::cancelTimer()
    157 {
    158     m_timer.startOneShot(s_decade);
    159     m_isScheduled = false;
    160 }
    161 
    162 #endif
     341    Manager::shared().cancelTimer(*this);
     342}
    163343
    164344void JSRunLoopTimer::addTimerSetNotification(TimerNotificationCallback callback)
  • trunk/Source/JavaScriptCore/runtime/JSRunLoopTimer.h

    r235152 r235261  
    4848    using TimerNotificationCallback = RefPtr<WTF::SharedTask<TimerNotificationType>>;
    4949
    50     JSRunLoopTimer(VM*);
     50    class Manager {
    5151#if USE(CF)
    52     static void timerDidFireCallback(CFRunLoopTimerRef, void*);
     52        static void timerDidFireCallback(CFRunLoopTimerRef, void*);
    5353#else
    54     void timerDidFireCallback();
     54        void timerDidFireCallback();
    5555#endif
    5656
     57        void timerDidFire();
     58
     59    public:
     60        using EpochTime = Seconds;
     61
     62        static Manager& shared();
     63        void registerVM(VM&);
     64        void unregisterVM(VM&);
     65        void scheduleTimer(JSRunLoopTimer&, Seconds nextFireTime);
     66        void cancelTimer(JSRunLoopTimer&);
     67
     68        std::optional<Seconds> timeUntilFire(JSRunLoopTimer&);
     69
     70#if USE(CF)
     71        void didChangeRunLoop(VM&, CFRunLoopRef newRunLoop);
     72#endif
     73
     74    private:
     75        Lock m_lock;
     76
     77        struct PerVMData {
     78            PerVMData() = default;
     79#if USE(CF)
     80            PerVMData(Manager&) { }
     81#else
     82            PerVMData(Manager&);
     83#endif
     84            PerVMData(PerVMData&&) = default;
     85            PerVMData& operator=(PerVMData&&) = default;
     86
     87            ~PerVMData();
     88
     89#if USE(CF)
     90            void setRunLoop(Manager*, CFRunLoopRef);
     91            RetainPtr<CFRunLoopTimerRef> timer;
     92            RetainPtr<CFRunLoopRef> runLoop;
     93            CFRunLoopTimerContext context;
     94#else
     95            RunLoop* runLoop;
     96            std::unique_ptr<RunLoop::Timer<Manager>> timer;
     97#endif
     98            Vector<std::pair<Ref<JSRunLoopTimer>, EpochTime>> timers;
     99        };
     100
     101        HashMap<Ref<JSLock>, PerVMData> m_mapping;
     102    };
     103
     104    JSRunLoopTimer(VM*);
    57105    JS_EXPORT_PRIVATE virtual ~JSRunLoopTimer();
    58     virtual void doWork() = 0;
     106    virtual void doWork(VM&) = 0;
    59107
    60     void scheduleTimer(Seconds intervalInSeconds);
     108    void setTimeUntilFire(Seconds intervalInSeconds);
    61109    void cancelTimer();
    62110    bool isScheduled() const { return m_isScheduled; }
    63111
    64112    // Note: The only thing the timer notification callback cannot do is
    65     // call scheduleTimer(). This will cause a deadlock. It would not
     113    // call setTimeUntilFire(). This will cause a deadlock. It would not
    66114    // be hard to make this work, however, there are no clients that need
    67115    // this behavior. We should implement it only if we find that we need it.
     
    69117    JS_EXPORT_PRIVATE void removeTimerSetNotification(TimerNotificationCallback);
    70118
    71 #if USE(CF)
    72     JS_EXPORT_PRIVATE void setRunLoop(CFRunLoopRef);
    73 #endif // USE(CF)
     119    JS_EXPORT_PRIVATE std::optional<Seconds> timeUntilFire();
    74120
    75121protected:
    76     VM* m_vm;
     122    static const Seconds s_decade;
     123    Ref<JSLock> m_apiLock;
    77124
    78     static const Seconds s_decade;
     125private:
     126    friend class Manager;
    79127
    80     RefPtr<JSLock> m_apiLock;
     128    void timerDidFire();
     129
     130    HashSet<TimerNotificationCallback> m_timerSetCallbacks;
     131    Lock m_timerCallbacksLock;
     132
     133    Lock m_lock;
    81134    bool m_isScheduled { false };
    82 #if USE(CF)
    83     RetainPtr<CFRunLoopTimerRef> m_timer;
    84     RetainPtr<CFRunLoopRef> m_runLoop;
    85 
    86     CFRunLoopTimerContext m_context;
    87 
    88     Lock m_shutdownMutex;
    89 #else
    90     RunLoop::Timer<JSRunLoopTimer> m_timer;
    91 #endif
    92 
    93     Lock m_timerCallbacksLock;
    94     HashSet<TimerNotificationCallback> m_timerSetCallbacks;
    95    
    96 private:
    97     void timerDidFire();
    98135};
    99136   
  • trunk/Source/JavaScriptCore/runtime/PromiseDeferredTimer.cpp

    r235152 r235261  
    11/*
    2  * Copyright (C) 2017 Apple Inc. All rights reserved.
     2 * Copyright (C) 2017-2018 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    4444}
    4545
    46 void PromiseDeferredTimer::doWork()
     46void PromiseDeferredTimer::doWork(VM& vm)
    4747{
    48     ASSERT(m_vm->currentThreadIsHoldingAPILock());
     48    ASSERT(vm.currentThreadIsHoldingAPILock());
    4949    m_taskLock.lock();
    5050    cancelTimer();
     
    6767
    6868            task();
    69             m_vm->drainMicrotasks();
     69            vm.drainMicrotasks();
    7070
    7171            m_taskLock.lock();
     
    7676    if (m_pendingPromises.isEmpty() && m_shouldStopRunLoopWhenAllPromisesFinish) {
    7777#if USE(CF)
    78         CFRunLoopStop(m_runLoop.get());
     78        CFRunLoopStop(vm.runLoop());
    7979#else
    8080        RunLoop::current().stop();
     
    8787void PromiseDeferredTimer::runRunLoop()
    8888{
    89     ASSERT(!m_vm->currentThreadIsHoldingAPILock());
     89    ASSERT(!m_apiLock->vm()->currentThreadIsHoldingAPILock());
    9090#if USE(CF)
    91     ASSERT(CFRunLoopGetCurrent() == m_runLoop.get());
     91    ASSERT(CFRunLoopGetCurrent() == m_apiLock->vm()->runLoop());
    9292#endif
    9393    m_shouldStopRunLoopWhenAllPromisesFinish = true;
    94     if (m_pendingPromises.size())
     94    if (m_pendingPromises.size()) {
    9595#if USE(CF)
    9696        CFRunLoopRun();
     
    9898        RunLoop::run();
    9999#endif
     100    }
    100101}
    101102
    102 void PromiseDeferredTimer::addPendingPromise(JSPromiseDeferred* ticket, Vector<Strong<JSCell>>&& dependencies)
     103void PromiseDeferredTimer::addPendingPromise(VM& vm, JSPromiseDeferred* ticket, Vector<Strong<JSCell>>&& dependencies)
    103104{
    104     ASSERT(m_vm->currentThreadIsHoldingAPILock());
     105    ASSERT(vm.currentThreadIsHoldingAPILock());
    105106    for (unsigned i = 0; i < dependencies.size(); ++i)
    106107        ASSERT(dependencies[i].get() != ticket);
     
    109110    if (result.isNewEntry) {
    110111        dataLogLnIf(PromiseDeferredTimerInternal::verbose, "Adding new pending promise: ", RawPointer(ticket));
    111         dependencies.append(Strong<JSCell>(*m_vm, ticket));
     112        dependencies.append(Strong<JSCell>(vm, ticket));
    112113        result.iterator->value = WTFMove(dependencies);
    113114    } else {
     
    123124bool PromiseDeferredTimer::hasPendingPromise(JSPromiseDeferred* ticket)
    124125{
    125     ASSERT(m_vm->currentThreadIsHoldingAPILock());
     126    ASSERT(ticket->vm()->currentThreadIsHoldingAPILock());
    126127    return m_pendingPromises.contains(ticket);
    127128}
     
    129130bool PromiseDeferredTimer::hasDependancyInPendingPromise(JSPromiseDeferred* ticket, JSCell* dependency)
    130131{
    131     ASSERT(m_vm->currentThreadIsHoldingAPILock());
     132    ASSERT(ticket->vm()->currentThreadIsHoldingAPILock());
    132133    ASSERT(m_pendingPromises.contains(ticket));
    133134
     
    138139bool PromiseDeferredTimer::cancelPendingPromise(JSPromiseDeferred* ticket)
    139140{
    140     ASSERT(m_vm->currentThreadIsHoldingAPILock());
     141    ASSERT(ticket->vm()->currentThreadIsHoldingAPILock());
    141142    bool result = m_pendingPromises.remove(ticket);
    142143
     
    152153    m_tasks.append(std::make_tuple(ticket, WTFMove(task)));
    153154    if (!isScheduled() && !m_currentlyRunningTask)
    154         scheduleTimer(0_s);
     155        setTimeUntilFire(0_s);
    155156}
    156157
  • trunk/Source/JavaScriptCore/runtime/PromiseDeferredTimer.h

    r235152 r235261  
    4545    PromiseDeferredTimer(VM&);
    4646
    47     void doWork() override;
     47    void doWork(VM&) override;
    4848
    49     void addPendingPromise(JSPromiseDeferred*, Vector<Strong<JSCell>>&& dependencies);
     49    void addPendingPromise(VM&, JSPromiseDeferred*, Vector<Strong<JSCell>>&& dependencies);
    5050    JS_EXPORT_PRIVATE bool hasPendingPromise(JSPromiseDeferred* ticket);
    5151    JS_EXPORT_PRIVATE bool hasDependancyInPendingPromise(JSPromiseDeferred* ticket, JSCell* dependency);
  • trunk/Source/JavaScriptCore/runtime/VM.cpp

    r235254 r235261  
    382382    setLastStackTop(stack.origin());
    383383
     384    JSRunLoopTimer::Manager::shared().registerVM(*this);
     385
    384386    // Need to be careful to keep everything consistent here
    385387    JSLockHolder lock(this);
     
    582584    m_apiLock->willDestroyVM(this);
    583585    heap.lastChanceToFinalize();
     586
     587    JSRunLoopTimer::Manager::shared().unregisterVM(*this);
    584588   
    585589    delete interpreter;
     
    12101214
    12111215#if USE(CF)
    1212 void VM::registerRunLoopTimer(JSRunLoopTimer* timer)
    1213 {
    1214     ASSERT(runLoop());
    1215     ASSERT(!m_runLoopTimers.contains(timer));
    1216     m_runLoopTimers.add(timer);
    1217     timer->setRunLoop(runLoop());
    1218 }
    1219 
    1220 void VM::unregisterRunLoopTimer(JSRunLoopTimer* timer)
    1221 {
    1222     ASSERT(m_runLoopTimers.contains(timer));
    1223     m_runLoopTimers.remove(timer);
    1224     timer->setRunLoop(nullptr);
    1225 }
    1226 
    12271216void VM::setRunLoop(CFRunLoopRef runLoop)
    12281217{
    12291218    ASSERT(runLoop);
    12301219    m_runLoop = runLoop;
    1231     for (auto timer : m_runLoopTimers)
    1232         timer->setRunLoop(runLoop);
     1220    JSRunLoopTimer::Manager::shared().didChangeRunLoop(*this, runLoop);
    12331221}
    12341222#endif // USE(CF)
  • trunk/Source/JavaScriptCore/runtime/VM.h

    r235254 r235261  
    308308#if USE(CF)
    309309    // These need to be initialized before heap below.
    310     HashSet<JSRunLoopTimer*> m_runLoopTimers;
    311310    RetainPtr<CFRunLoopRef> m_runLoop;
    312311#endif
     
    888887#if USE(CF)
    889888    CFRunLoopRef runLoop() const { return m_runLoop.get(); }
    890     void registerRunLoopTimer(JSRunLoopTimer*);
    891     void unregisterRunLoopTimer(JSRunLoopTimer*);
    892889    JS_EXPORT_PRIVATE void setRunLoop(CFRunLoopRef);
    893890#endif // USE(CF)
  • trunk/Source/JavaScriptCore/wasm/js/WebAssemblyPrototype.cpp

    r235152 r235261  
    9292    dependencies.append(Strong<JSCell>(vm, globalObject));
    9393
    94     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
     94    vm.promiseDeferredTimer->addPendingPromise(vm, promise, WTFMove(dependencies));
    9595
    9696    Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([promise, globalObject, &vm] (Wasm::Module::ValidationResult&& result) mutable {
     
    174174    dependencies.append(Strong<JSCell>(vm, instance));
    175175    dependencies.append(Strong<JSCell>(vm, importObject));
    176     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
     176    vm.promiseDeferredTimer->addPendingPromise(vm, promise, WTFMove(dependencies));
    177177    // Note: This completion task may or may not get called immediately.
    178178    module->module().compileAsync(&vm.wasmContext, instance->memoryMode(), createSharedTask<Wasm::CodeBlock::CallbackType>([promise, instance, module, importObject, resolveKind, creationMode, &vm] (Ref<Wasm::CodeBlock>&& refCodeBlock) mutable {
     
    195195    dependencies.append(Strong<JSCell>(vm, importObject));
    196196    dependencies.append(Strong<JSCell>(vm, moduleKeyCell));
    197     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
     197    vm.promiseDeferredTimer->addPendingPromise(vm, promise, WTFMove(dependencies));
    198198
    199199    Vector<uint8_t> source = createSourceBufferFromValue(vm, exec, buffer);
     
    232232    dependencies.append(Strong<JSCell>(vm, importObject));
    233233    dependencies.append(Strong<JSCell>(vm, globalObject));
    234     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
     234    vm.promiseDeferredTimer->addPendingPromise(vm, promise, WTFMove(dependencies));
    235235
    236236    Wasm::Module::validateAsync(&vm.wasmContext, WTFMove(source), createSharedTask<Wasm::Module::CallbackType>([promise, importObject, globalObject, &vm] (Wasm::Module::ValidationResult&& result) mutable {
     
    311311    Vector<Strong<JSCell>> dependencies;
    312312    dependencies.append(Strong<JSCell>(vm, globalObject));
    313     vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
     313    vm.promiseDeferredTimer->addPendingPromise(vm, promise, WTFMove(dependencies));
    314314
    315315    if (globalObject->globalObjectMethodTable()->compileStreaming)
     
    348348                dependencies.append(Strong<JSCell>(vm, globalObject));
    349349                dependencies.append(Strong<JSCell>(vm, importObject));
    350                 vm.promiseDeferredTimer->addPendingPromise(promise, WTFMove(dependencies));
     350                vm.promiseDeferredTimer->addPendingPromise(vm, promise, WTFMove(dependencies));
    351351
    352352                // FIXME: <http://webkit.org/b/184888> if there's an importObject and it contains a Memory, then we can compile the module with the right memory type (fast or not) by looking at the memory's type.
  • trunk/Source/WebCore/ChangeLog

    r235258 r235261  
     12018-08-23  Saam barati  <sbarati@apple.com>
     2
     3        JSRunLoopTimer may run part of a member function after it's destroyed
     4        https://bugs.webkit.org/show_bug.cgi?id=188426
     5
     6        Reviewed by Mark Lam.
     7
     8        * page/cocoa/ResourceUsageThreadCocoa.mm:
     9        (WebCore::ResourceUsageThread::platformThreadBody):
     10        * page/linux/ResourceUsageThreadLinux.cpp:
     11        (WebCore::ResourceUsageThread::platformThreadBody):
     12
    1132018-08-23  David Fenton  <david_fenton@apple.com>
    214
  • trunk/Source/WebCore/page/cocoa/ResourceUsageThreadCocoa.mm

    r235152 r235261  
    249249    data.totalExternalSize = currentGCOwnedExternal;
    250250
    251     data.timeOfNextEdenCollection = vm->heap.edenActivityCallback()->nextFireTime();
    252     data.timeOfNextFullCollection = vm->heap.fullActivityCallback()->nextFireTime();
     251    auto now = MonotonicTime::now();
     252    data.timeOfNextEdenCollection = now + vm->heap.edenActivityCallback()->timeUntilFire().value_or(Seconds(std::numeric_limits<double>::infinity()));
     253    data.timeOfNextFullCollection = now + vm->heap.fullActivityCallback()->timeUntilFire().value_or(Seconds(std::numeric_limits<double>::infinity()));
    253254}
    254255
  • trunk/Source/WebCore/page/linux/ResourceUsageThreadLinux.cpp

    r235152 r235261  
    11/*
    22 * Copyright (C) 2017 Igalia S.L.
     3 * Copyright (C) 2018 Apple Inc. All rights reserved.
    34 *
    45 * Redistribution and use in source and binary forms, with or without
     
    169170    data.totalExternalSize = currentGCOwnedExternal;
    170171
    171     data.timeOfNextEdenCollection = vm->heap.edenActivityCallback()->nextFireTime();
    172     data.timeOfNextFullCollection = vm->heap.fullActivityCallback()->nextFireTime();
     172    auto now = MonotonicTime::now();
     173    data.timeOfNextEdenCollection = now + vm->heap.edenActivityCallback()->timeUntilFire().value_or(Seconds(std::numeric_limits<double>::infinity()));
     174    data.timeOfNextFullCollection = now + vm->heap.fullActivityCallback()->timeUntilFire().value_or(Seconds(std::numeric_limits<double>::infinity()));
    173175}
    174176
Note: See TracChangeset for help on using the changeset viewer.