Changeset 207480 in webkit


Ignore:
Timestamp:
Oct 18, 2016 1:17:10 PM (8 years ago)
Author:
fpizlo@apple.com
Message:

WTF should make it easier to create threads that die automatically after inactivity
https://bugs.webkit.org/show_bug.cgi?id=163576

Reviewed by Andreas Kling.

Source/JavaScriptCore:

Added a sleepSeconds() function, which made it easier for me to test this change.

The WTF changes in this patch change how the JSC GC manages threads: the GC threads will now
shut down automatically after 1 second of inactivity. Maybe this will save some memory.

  • jsc.cpp:

(GlobalObject::finishCreation):
(functionSleepSeconds):

Source/WTF:

For a long time now, I've been adding threads to WTF/JSC and each time I do this, I feel
guilty because those threads don't shut down when they are inactive. For example, in bug
163562, I need to add a new GC thread. There will be one of them per VM. This means that a
JSC API client that starts a lot of VMs will have a lot of threads. I don't think that's
good.

A common pattern for all of these threads is that they have some well-defined trigger that
causes them to run. This trigger has a lock, a condition variable, some logic that determines
if there is work to do, and then of course the logic for the thread's actual work. The thread
bodies usually look like this:

void Thingy::runThread()
{

for (;;) {

Work work;
{

LockHolder locker(m_lock);
while (!hasWork())

m_cond.wait(m_lock);

work = takeWork();

}
doWork(work);

}

}

If you look at ParallelHelperPool (the GC's threads) and DFG::Worklist (some of the JIT's
threads), you will see this pattern.

This change adds a new kind of thread, called AutomaticThread, that lets you write threads to
this pattern while getting automatic thread shutdown for free: instead of just waiting on a
condition variable, AutomaticThread will have a timeout that causes the thread to die. The
condition variable associated with AutomaticThread, called AutomaticThreadCondition, is smart
enough to restart any threads that have decided to stop due to inactivity. The inactivity
threshold is current just 1 second.

In this patch I only adopt AutomaticThread for ParallelHelperPool. I plan to adopt it in more
places soon.

  • WTF.xcodeproj/project.pbxproj:
  • wtf/AutomaticThread.cpp: Added.

(WTF::AutomaticThreadCondition::create):
(WTF::AutomaticThreadCondition::AutomaticThreadCondition):
(WTF::AutomaticThreadCondition::~AutomaticThreadCondition):
(WTF::AutomaticThreadCondition::notifyAll):
(WTF::AutomaticThreadCondition::add):
(WTF::AutomaticThreadCondition::remove):
(WTF::AutomaticThreadCondition::contains):
(WTF::AutomaticThread::AutomaticThread):
(WTF::AutomaticThread::~AutomaticThread):
(WTF::AutomaticThread::join):
(WTF::AutomaticThread::start):

  • wtf/AutomaticThread.h: Added.
  • wtf/CMakeLists.txt:
  • wtf/ParallelHelperPool.cpp:

(WTF::ParallelHelperClient::ParallelHelperClient):
(WTF::ParallelHelperClient::~ParallelHelperClient):
(WTF::ParallelHelperClient::setTask):
(WTF::ParallelHelperClient::finish):
(WTF::ParallelHelperClient::doSomeHelping):
(WTF::ParallelHelperClient::runTask):
(WTF::ParallelHelperPool::ParallelHelperPool):
(WTF::ParallelHelperPool::~ParallelHelperPool):
(WTF::ParallelHelperPool::ensureThreads):
(WTF::ParallelHelperPool::doSomeHelping):
(WTF::ParallelHelperPool::Thread::Thread):
(WTF::ParallelHelperPool::didMakeWorkAvailable):
(WTF::ParallelHelperPool::helperThreadBody): Deleted.
(WTF::ParallelHelperPool::waitForClientWithTask): Deleted.

  • wtf/ParallelHelperPool.h:
Location:
trunk/Source
Files:
2 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/ChangeLog

    r207476 r207480  
     12016-10-18  Filip Pizlo  <fpizlo@apple.com>
     2
     3        WTF should make it easier to create threads that die automatically after inactivity
     4        https://bugs.webkit.org/show_bug.cgi?id=163576
     5
     6        Reviewed by Andreas Kling.
     7       
     8        Added a sleepSeconds() function, which made it easier for me to test this change.
     9       
     10        The WTF changes in this patch change how the JSC GC manages threads: the GC threads will now
     11        shut down automatically after 1 second of inactivity. Maybe this will save some memory.
     12
     13        * jsc.cpp:
     14        (GlobalObject::finishCreation):
     15        (functionSleepSeconds):
     16
    1172016-10-18  Keith Miller  <keith_miller@apple.com>
    218
  • trunk/Source/JavaScriptCore/jsc.cpp

    r207456 r207480  
    844844static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
    845845static EncodedJSValue JSC_HOST_CALL functionDescribeArray(ExecState*);
     846static EncodedJSValue JSC_HOST_CALL functionSleepSeconds(ExecState*);
    846847static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
    847848static EncodedJSValue JSC_HOST_CALL functionGCAndSweep(ExecState*);
     
    10681069        addFunction(vm, "read", functionReadFile, 2);
    10691070        addFunction(vm, "checkSyntax", functionCheckSyntax, 1);
     1071        addFunction(vm, "sleepSeconds", functionSleepSeconds, 1);
    10701072        addFunction(vm, "jscStack", functionJSCStack, 1);
    10711073        addFunction(vm, "readline", functionReadline, 0);
     
    14941496}
    14951497
     1498EncodedJSValue JSC_HOST_CALL functionSleepSeconds(ExecState* exec)
     1499{
     1500    if (exec->argumentCount() >= 1)
     1501        sleep(exec->argument(0).toNumber(exec));
     1502    return JSValue::encode(jsUndefined());
     1503}
     1504
    14961505class FunctionJSCStackFunctor {
    14971506public:
  • trunk/Source/WTF/ChangeLog

    r207471 r207480  
     12016-10-18  Filip Pizlo  <fpizlo@apple.com>
     2
     3        WTF should make it easier to create threads that die automatically after inactivity
     4        https://bugs.webkit.org/show_bug.cgi?id=163576
     5
     6        Reviewed by Andreas Kling.
     7       
     8        For a long time now, I've been adding threads to WTF/JSC and each time I do this, I feel
     9        guilty because those threads don't shut down when they are inactive. For example, in bug
     10        163562, I need to add a new GC thread. There will be one of them per VM. This means that a
     11        JSC API client that starts a lot of VMs will have a lot of threads. I don't think that's
     12        good.
     13       
     14        A common pattern for all of these threads is that they have some well-defined trigger that
     15        causes them to run. This trigger has a lock, a condition variable, some logic that determines
     16        if there is work to do, and then of course the logic for the thread's actual work. The thread
     17        bodies usually look like this:
     18       
     19        void Thingy::runThread()
     20        {
     21            for (;;) {
     22                Work work;
     23                {
     24                    LockHolder locker(m_lock);
     25                    while (!hasWork())
     26                        m_cond.wait(m_lock);
     27                    work = takeWork();
     28                }
     29                doWork(work);
     30            }
     31        }
     32       
     33        If you look at ParallelHelperPool (the GC's threads) and DFG::Worklist (some of the JIT's
     34        threads), you will see this pattern.
     35       
     36        This change adds a new kind of thread, called AutomaticThread, that lets you write threads to
     37        this pattern while getting automatic thread shutdown for free: instead of just waiting on a
     38        condition variable, AutomaticThread will have a timeout that causes the thread to die. The
     39        condition variable associated with AutomaticThread, called AutomaticThreadCondition, is smart
     40        enough to restart any threads that have decided to stop due to inactivity. The inactivity
     41        threshold is current just 1 second.
     42       
     43        In this patch I only adopt AutomaticThread for ParallelHelperPool. I plan to adopt it in more
     44        places soon.
     45
     46        * WTF.xcodeproj/project.pbxproj:
     47        * wtf/AutomaticThread.cpp: Added.
     48        (WTF::AutomaticThreadCondition::create):
     49        (WTF::AutomaticThreadCondition::AutomaticThreadCondition):
     50        (WTF::AutomaticThreadCondition::~AutomaticThreadCondition):
     51        (WTF::AutomaticThreadCondition::notifyAll):
     52        (WTF::AutomaticThreadCondition::add):
     53        (WTF::AutomaticThreadCondition::remove):
     54        (WTF::AutomaticThreadCondition::contains):
     55        (WTF::AutomaticThread::AutomaticThread):
     56        (WTF::AutomaticThread::~AutomaticThread):
     57        (WTF::AutomaticThread::join):
     58        (WTF::AutomaticThread::start):
     59        * wtf/AutomaticThread.h: Added.
     60        * wtf/CMakeLists.txt:
     61        * wtf/ParallelHelperPool.cpp:
     62        (WTF::ParallelHelperClient::ParallelHelperClient):
     63        (WTF::ParallelHelperClient::~ParallelHelperClient):
     64        (WTF::ParallelHelperClient::setTask):
     65        (WTF::ParallelHelperClient::finish):
     66        (WTF::ParallelHelperClient::doSomeHelping):
     67        (WTF::ParallelHelperClient::runTask):
     68        (WTF::ParallelHelperPool::ParallelHelperPool):
     69        (WTF::ParallelHelperPool::~ParallelHelperPool):
     70        (WTF::ParallelHelperPool::ensureThreads):
     71        (WTF::ParallelHelperPool::doSomeHelping):
     72        (WTF::ParallelHelperPool::Thread::Thread):
     73        (WTF::ParallelHelperPool::didMakeWorkAvailable):
     74        (WTF::ParallelHelperPool::helperThreadBody): Deleted.
     75        (WTF::ParallelHelperPool::waitForClientWithTask): Deleted.
     76        * wtf/ParallelHelperPool.h:
     77
    1782016-10-18  Said Abou-Hallawa  <sabouhallawa@apple.com>
    279
  • trunk/Source/WTF/WTF.xcodeproj/project.pbxproj

    r207156 r207480  
    2626                0F2B66A717B6B4FD00A7AE3F /* FlipBytes.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2B66A517B6B4F700A7AE3F /* FlipBytes.h */; };
    2727                0F3501641BB258D500F0A2A3 /* WeakRandom.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3501631BB258C800F0A2A3 /* WeakRandom.h */; };
     28                0F43D8F11DB5ADDC00108FB6 /* AutomaticThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F43D8EF1DB5ADDC00108FB6 /* AutomaticThread.cpp */; };
     29                0F43D8F21DB5ADDC00108FB6 /* AutomaticThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F43D8F01DB5ADDC00108FB6 /* AutomaticThread.h */; };
    2830                0F4570431BE5B58F0062A629 /* Dominators.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4570421BE5B58F0062A629 /* Dominators.h */; };
    2931                0F4570451BE834410062A629 /* BubbleSort.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4570441BE834410062A629 /* BubbleSort.h */; };
     
    367369                0F300B7D18AB48B400A6D72E /* HashMethod.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HashMethod.h; sourceTree = "<group>"; };
    368370                0F3501631BB258C800F0A2A3 /* WeakRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WeakRandom.h; sourceTree = "<group>"; };
     371                0F43D8EF1DB5ADDC00108FB6 /* AutomaticThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AutomaticThread.cpp; sourceTree = "<group>"; };
     372                0F43D8F01DB5ADDC00108FB6 /* AutomaticThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutomaticThread.h; sourceTree = "<group>"; };
    369373                0F4570421BE5B58F0062A629 /* Dominators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dominators.h; sourceTree = "<group>"; };
    370374                0F4570441BE834410062A629 /* BubbleSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BubbleSort.h; sourceTree = "<group>"; };
     
    840844                                1469419A16EAB10A0024E146 /* AutodrainedPool.h */,
    841845                                1469419B16EAB10A0024E146 /* AutodrainedPoolMac.mm */,
     846                                0F43D8EF1DB5ADDC00108FB6 /* AutomaticThread.cpp */,
     847                                0F43D8F01DB5ADDC00108FB6 /* AutomaticThread.h */,
    842848                                DCEE22041CEB9869000C2396 /* BackwardsGraph.h */,
    843849                                0FB14E18180FA218009B6B4D /* Bag.h */,
     
    13311337                                7CDD7FF8186D291E007433CD /* IteratorAdaptors.h in Headers */,
    13321338                                7CDD7FFA186D2A54007433CD /* IteratorRange.h in Headers */,
     1339                                0F43D8F21DB5ADDC00108FB6 /* AutomaticThread.h in Headers */,
    13331340                                93AC91A818942FC400244939 /* LChar.h in Headers */,
    13341341                                539EB0631D55284200C82EF7 /* LEBDecoder.h in Headers */,
     
    16351642                                0FDDBFA71666DFA300C55FEF /* StringPrintStream.cpp in Sources */,
    16361643                                A8A47443151A825B004123FF /* StringStatics.cpp in Sources */,
     1644                                0F43D8F11DB5ADDC00108FB6 /* AutomaticThread.cpp in Sources */,
    16371645                                93F1993E19D7958D00C2390B /* StringView.cpp in Sources */,
    16381646                                93934BD518A1F16900D0D6A1 /* StringViewCF.cpp in Sources */,
  • trunk/Source/WTF/wtf/CMakeLists.txt

    r206691 r207480  
    33    Assertions.h
    44    Atomics.h
     5    AutomaticThread.h
    56    BackwardsGraph.h
    67    Bag.h
     
    174175    Assertions.cpp
    175176    Atomics.cpp
     177    AutomaticThread.cpp
    176178    BitVector.cpp
    177179    CompilationThread.cpp
  • trunk/Source/WTF/wtf/ParallelHelperPool.cpp

    r190875 r207480  
    11/*
    2  * Copyright (C) 2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2727#include "ParallelHelperPool.h"
    2828
     29#include "AutomaticThread.h"
    2930#include "DataLog.h"
    3031#include "StringPrintStream.h"
     
    3536    : m_pool(pool)
    3637{
    37     LockHolder locker(m_pool->m_lock);
     38    LockHolder locker(*m_pool->m_lock);
    3839    RELEASE_ASSERT(!m_pool->m_isDying);
    3940    m_pool->m_clients.append(this);
     
    4243ParallelHelperClient::~ParallelHelperClient()
    4344{
    44     LockHolder locker(m_pool->m_lock);
     45    LockHolder locker(*m_pool->m_lock);
    4546    finish(locker);
    4647
     
    5657void ParallelHelperClient::setTask(RefPtr<SharedTask<void ()>> task)
    5758{
    58     LockHolder locker(m_pool->m_lock);
     59    LockHolder locker(*m_pool->m_lock);
    5960    RELEASE_ASSERT(!m_task);
    6061    m_task = task;
     
    6465void ParallelHelperClient::finish()
    6566{
    66     LockHolder locker(m_pool->m_lock);
     67    LockHolder locker(*m_pool->m_lock);
    6768    finish(locker);
    6869}
     
    7273    RefPtr<SharedTask<void ()>> task;
    7374    {
    74         LockHolder locker(m_pool->m_lock);
     75        LockHolder locker(*m_pool->m_lock);
    7576        task = claimTask(locker);
    7677        if (!task)
     
    9293    m_task = nullptr;
    9394    while (m_numActive)
    94         m_pool->m_workCompleteCondition.wait(m_pool->m_lock);
     95        m_pool->m_workCompleteCondition.wait(*m_pool->m_lock);
    9596}
    9697
     
    112113
    113114    {
    114         LockHolder locker(m_pool->m_lock);
     115        LockHolder locker(*m_pool->m_lock);
    115116        RELEASE_ASSERT(m_numActive);
    116117        // No new task could have been installed, since we were still active.
     
    124125
    125126ParallelHelperPool::ParallelHelperPool()
     127    : m_lock(Box<Lock>::create())
     128    , m_workAvailableCondition(AutomaticThreadCondition::create())
    126129{
    127130}
     
    132135   
    133136    {
    134         LockHolder locker(m_lock);
     137        LockHolder locker(*m_lock);
    135138        m_isDying = true;
    136         m_workAvailableCondition.notifyAll();
    137     }
    138 
    139     for (ThreadIdentifier threadIdentifier : m_threads)
    140         waitForThreadCompletion(threadIdentifier);
     139        m_workAvailableCondition->notifyAll(locker);
     140    }
     141
     142    for (RefPtr<AutomaticThread>& thread : m_threads)
     143        thread->join();
    141144}
    142145
    143146void ParallelHelperPool::ensureThreads(unsigned numThreads)
    144147{
    145     LockHolder locker(m_lock);
     148    LockHolder locker(*m_lock);
    146149    if (numThreads < m_numThreads)
    147150        return;
     
    156159    RefPtr<SharedTask<void ()>> task;
    157160    {
    158         LockHolder locker(m_lock);
     161        LockHolder locker(*m_lock);
    159162        client = getClientWithTask(locker);
    160163        if (!client)
     
    166169}
    167170
    168 void ParallelHelperPool::didMakeWorkAvailable(const LockHolder&)
    169 {
    170     while (m_numThreads > m_threads.size()) {
    171         ThreadIdentifier threadIdentifier = createThread(
    172             "WTF Parallel Helper Thread",
    173             [this] () {
    174                 helperThreadBody();
    175             });
    176         m_threads.append(threadIdentifier);
    177     }
    178     m_workAvailableCondition.notifyAll();
    179 }
    180 
    181 void ParallelHelperPool::helperThreadBody()
    182 {
    183     for (;;) {
    184         ParallelHelperClient* client;
    185         RefPtr<SharedTask<void ()>> task;
    186 
    187         {
    188             LockHolder locker(m_lock);
    189             client = waitForClientWithTask(locker);
    190             if (!client) {
    191                 RELEASE_ASSERT(m_isDying);
    192                 return;
    193             }
    194 
    195             task = client->claimTask(locker);
     171class ParallelHelperPool::Thread : public AutomaticThread {
     172public:
     173    Thread(const LockHolder& locker, ParallelHelperPool& pool)
     174        : AutomaticThread(locker, pool.m_lock, pool.m_workAvailableCondition)
     175        , m_pool(pool)
     176    {
     177    }
     178   
     179protected:
     180    PollResult poll(const LockHolder& locker) override
     181    {
     182        if (m_pool.m_isDying)
     183            return PollResult::Stop;
     184        m_client = m_pool.getClientWithTask(locker);
     185        if (m_client) {
     186            m_task = m_client->claimTask(locker);
     187            return PollResult::Work;
    196188        }
    197 
    198         client->runTask(task);
    199     }
     189        return PollResult::Wait;
     190    }
     191   
     192    WorkResult work() override
     193    {
     194        m_client->runTask(m_task);
     195        m_client = nullptr;
     196        m_task = nullptr;
     197        return WorkResult::Continue;
     198    }
     199   
     200private:
     201    ParallelHelperPool& m_pool;
     202    ParallelHelperClient* m_client { nullptr };
     203    RefPtr<SharedTask<void ()>> m_task;
     204};
     205
     206void ParallelHelperPool::didMakeWorkAvailable(const LockHolder& locker)
     207{
     208    while (m_numThreads > m_threads.size())
     209        m_threads.append(adoptRef(new Thread(locker, *this)));
     210    m_workAvailableCondition->notifyAll(locker);
    200211}
    201212
     
    223234}
    224235
    225 ParallelHelperClient* ParallelHelperPool::waitForClientWithTask(const LockHolder& locker)
    226 {
    227     for (;;) {
    228         // It might be quittin' time.
    229         if (m_isDying)
    230             return nullptr;
    231 
    232         if (ParallelHelperClient* result = getClientWithTask(locker))
    233             return result;
    234 
    235         // Wait until work becomes available.
    236         m_workAvailableCondition.wait(m_lock);
    237     }
    238 }
    239 
    240236} // namespace WTF
    241237
  • trunk/Source/WTF/wtf/ParallelHelperPool.h

    r190875 r207480  
    11/*
    2  * Copyright (C) 2015 Apple Inc. All rights reserved.
     2 * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
    33 *
    44 * Redistribution and use in source and binary forms, with or without
     
    2727#define ParallelHelperPool_h
    2828
     29#include <wtf/Box.h>
    2930#include <wtf/Condition.h>
    3031#include <wtf/Lock.h>
     
    3738
    3839namespace WTF {
     40
     41class AutomaticThread;
     42class AutomaticThreadCondition;
    3943
    4044// A ParallelHelperPool is a shared pool of threads that can be asked to help with some finite-time
     
    187191private:
    188192    friend class ParallelHelperClient;
     193    class Thread;
     194    friend class Thread;
    189195
    190196    void didMakeWorkAvailable(const LockHolder&);
    191     void helperThreadBody();
    192197
    193198    bool hasClientWithTask(const LockHolder&);
     
    195200    ParallelHelperClient* waitForClientWithTask(const LockHolder&);
    196201   
    197     Lock m_lock;
    198     Condition m_workAvailableCondition;
     202    Box<Lock> m_lock; // AutomaticThread wants this in a box for safety.
     203    RefPtr<AutomaticThreadCondition> m_workAvailableCondition;
    199204    Condition m_workCompleteCondition;
    200205
     
    202207   
    203208    Vector<ParallelHelperClient*> m_clients;
    204     Vector<ThreadIdentifier> m_threads;
     209    Vector<RefPtr<AutomaticThread>> m_threads;
    205210    unsigned m_numThreads { 0 }; // This can be larger than m_threads.size() because we start threads only once there is work.
    206211    bool m_isDying { false };
Note: See TracChangeset for help on using the changeset viewer.