Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (207479 => 207480)
--- trunk/Source/_javascript_Core/ChangeLog 2016-10-18 20:03:18 UTC (rev 207479)
+++ trunk/Source/_javascript_Core/ChangeLog 2016-10-18 20:17:10 UTC (rev 207480)
@@ -1,3 +1,19 @@
+2016-10-18 Filip Pizlo <fpi...@apple.com>
+
+ 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.
+
+ 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):
+
2016-10-18 Keith Miller <keith_mil...@apple.com>
Cleanup Wasm memory.
Modified: trunk/Source/_javascript_Core/jsc.cpp (207479 => 207480)
--- trunk/Source/_javascript_Core/jsc.cpp 2016-10-18 20:03:18 UTC (rev 207479)
+++ trunk/Source/_javascript_Core/jsc.cpp 2016-10-18 20:17:10 UTC (rev 207480)
@@ -843,6 +843,7 @@
static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDescribeArray(ExecState*);
+static EncodedJSValue JSC_HOST_CALL functionSleepSeconds(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionGCAndSweep(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionFullGC(ExecState*);
@@ -1067,6 +1068,7 @@
addFunction(vm, "readFile", functionReadFile, 2);
addFunction(vm, "read", functionReadFile, 2);
addFunction(vm, "checkSyntax", functionCheckSyntax, 1);
+ addFunction(vm, "sleepSeconds", functionSleepSeconds, 1);
addFunction(vm, "jscStack", functionJSCStack, 1);
addFunction(vm, "readline", functionReadline, 0);
addFunction(vm, "preciseTime", functionPreciseTime, 0);
@@ -1493,6 +1495,13 @@
return JSValue::encode(jsNontrivialString(exec, toString("<Butterfly: ", RawPointer(object->butterfly()), "; public length: ", object->getArrayLength(), "; vector length: ", object->getVectorLength(), ">")));
}
+EncodedJSValue JSC_HOST_CALL functionSleepSeconds(ExecState* exec)
+{
+ if (exec->argumentCount() >= 1)
+ sleep(exec->argument(0).toNumber(exec));
+ return JSValue::encode(jsUndefined());
+}
+
class FunctionJSCStackFunctor {
public:
FunctionJSCStackFunctor(StringBuilder& trace)
Modified: trunk/Source/WTF/ChangeLog (207479 => 207480)
--- trunk/Source/WTF/ChangeLog 2016-10-18 20:03:18 UTC (rev 207479)
+++ trunk/Source/WTF/ChangeLog 2016-10-18 20:17:10 UTC (rev 207480)
@@ -1,3 +1,80 @@
+2016-10-18 Filip Pizlo <fpi...@apple.com>
+
+ 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.
+
+ 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:
+
2016-10-18 Said Abou-Hallawa <sabouhall...@apple.com>
SVGCSSParser: m_implicitShorthand value is not reset after adding the shorthand property
Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (207479 => 207480)
--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2016-10-18 20:03:18 UTC (rev 207479)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2016-10-18 20:17:10 UTC (rev 207480)
@@ -25,6 +25,8 @@
0F2B66A617B6B4FB00A7AE3F /* DeferrableRefCounted.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2B66A417B6B4F700A7AE3F /* DeferrableRefCounted.h */; };
0F2B66A717B6B4FD00A7AE3F /* FlipBytes.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F2B66A517B6B4F700A7AE3F /* FlipBytes.h */; };
0F3501641BB258D500F0A2A3 /* WeakRandom.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3501631BB258C800F0A2A3 /* WeakRandom.h */; };
+ 0F43D8F11DB5ADDC00108FB6 /* AutomaticThread.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F43D8EF1DB5ADDC00108FB6 /* AutomaticThread.cpp */; };
+ 0F43D8F21DB5ADDC00108FB6 /* AutomaticThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F43D8F01DB5ADDC00108FB6 /* AutomaticThread.h */; };
0F4570431BE5B58F0062A629 /* Dominators.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4570421BE5B58F0062A629 /* Dominators.h */; };
0F4570451BE834410062A629 /* BubbleSort.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4570441BE834410062A629 /* BubbleSort.h */; };
0F725CAC1C50461600AD943A /* RangeSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F725CAB1C50461600AD943A /* RangeSet.h */; };
@@ -366,6 +368,8 @@
0F2B66A517B6B4F700A7AE3F /* FlipBytes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FlipBytes.h; sourceTree = "<group>"; };
0F300B7D18AB48B400A6D72E /* HashMethod.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HashMethod.h; sourceTree = "<group>"; };
0F3501631BB258C800F0A2A3 /* WeakRandom.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = WeakRandom.h; sourceTree = "<group>"; };
+ 0F43D8EF1DB5ADDC00108FB6 /* AutomaticThread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AutomaticThread.cpp; sourceTree = "<group>"; };
+ 0F43D8F01DB5ADDC00108FB6 /* AutomaticThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutomaticThread.h; sourceTree = "<group>"; };
0F4570421BE5B58F0062A629 /* Dominators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dominators.h; sourceTree = "<group>"; };
0F4570441BE834410062A629 /* BubbleSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BubbleSort.h; sourceTree = "<group>"; };
0F725CAB1C50461600AD943A /* RangeSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RangeSet.h; sourceTree = "<group>"; };
@@ -839,6 +843,8 @@
A8A4725D151A825A004123FF /* Atomics.h */,
1469419A16EAB10A0024E146 /* AutodrainedPool.h */,
1469419B16EAB10A0024E146 /* AutodrainedPoolMac.mm */,
+ 0F43D8EF1DB5ADDC00108FB6 /* AutomaticThread.cpp */,
+ 0F43D8F01DB5ADDC00108FB6 /* AutomaticThread.h */,
DCEE22041CEB9869000C2396 /* BackwardsGraph.h */,
0FB14E18180FA218009B6B4D /* Bag.h */,
0FB14E1A1810E1DA009B6B4D /* BagToHashMap.h */,
@@ -1330,6 +1336,7 @@
26147B0A15DDCCDC00DDB907 /* IntegerToStringConversion.h in Headers */,
7CDD7FF8186D291E007433CD /* IteratorAdaptors.h in Headers */,
7CDD7FFA186D2A54007433CD /* IteratorRange.h in Headers */,
+ 0F43D8F21DB5ADDC00108FB6 /* AutomaticThread.h in Headers */,
93AC91A818942FC400244939 /* LChar.h in Headers */,
539EB0631D55284200C82EF7 /* LEBDecoder.h in Headers */,
A70DA0851799F04D00529A9B /* ListDump.h in Headers */,
@@ -1634,6 +1641,7 @@
A5BA15F3182433A900A82E69 /* StringMac.mm in Sources */,
0FDDBFA71666DFA300C55FEF /* StringPrintStream.cpp in Sources */,
A8A47443151A825B004123FF /* StringStatics.cpp in Sources */,
+ 0F43D8F11DB5ADDC00108FB6 /* AutomaticThread.cpp in Sources */,
93F1993E19D7958D00C2390B /* StringView.cpp in Sources */,
93934BD518A1F16900D0D6A1 /* StringViewCF.cpp in Sources */,
93934BD318A1E8C300D0D6A1 /* StringViewObjC.mm in Sources */,
Added: trunk/Source/WTF/wtf/AutomaticThread.cpp (0 => 207480)
--- trunk/Source/WTF/wtf/AutomaticThread.cpp (rev 0)
+++ trunk/Source/WTF/wtf/AutomaticThread.cpp 2016-10-18 20:17:10 UTC (rev 207480)
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "AutomaticThread.h"
+
+#include "DataLog.h"
+
+namespace WTF {
+
+static const bool verbose = false;
+
+RefPtr<AutomaticThreadCondition> AutomaticThreadCondition::create()
+{
+ return adoptRef(new AutomaticThreadCondition());
+}
+
+AutomaticThreadCondition::AutomaticThreadCondition()
+{
+}
+
+AutomaticThreadCondition::~AutomaticThreadCondition()
+{
+}
+
+void AutomaticThreadCondition::notifyAll(const LockHolder& locker)
+{
+ m_condition.notifyAll();
+
+ for (AutomaticThread* thread : m_threads)
+ thread->start(locker);
+ m_threads.clear();
+}
+
+void AutomaticThreadCondition::add(const LockHolder&, AutomaticThread* thread)
+{
+ ASSERT(!m_threads.contains(thread));
+ m_threads.append(thread);
+}
+
+void AutomaticThreadCondition::remove(const LockHolder&, AutomaticThread* thread)
+{
+ ASSERT(m_threads.contains(thread));
+ m_threads.removeFirst(thread);
+ ASSERT(!m_threads.contains(thread));
+}
+
+bool AutomaticThreadCondition::contains(const LockHolder&, AutomaticThread* thread)
+{
+ return m_threads.contains(thread);
+}
+
+AutomaticThread::AutomaticThread(const LockHolder& locker, Box<Lock> lock, RefPtr<AutomaticThreadCondition> condition)
+ : m_lock(lock)
+ , m_condition(condition)
+{
+ m_condition->add(locker, this);
+}
+
+AutomaticThread::~AutomaticThread()
+{
+ LockHolder locker(*m_lock);
+
+ // It's possible that we're in a waiting state with the thread shut down. This is a goofy way to
+ // die, but it could happen.
+ m_condition->remove(locker, this);
+}
+
+void AutomaticThread::join()
+{
+ LockHolder locker(*m_lock);
+ while (m_isRunning)
+ m_isRunningCondition.wait(*m_lock);
+}
+
+void AutomaticThread::start(const LockHolder&)
+{
+ RefPtr<AutomaticThread> preserveThisForThread = this;
+
+ ThreadIdentifier thread = createThread(
+ "WTF::AutomaticThread",
+ [=] () {
+ if (verbose)
+ dataLog("Running automatic thread!\n");
+ RefPtr<AutomaticThread> preserveThisInThread = preserveThisForThread;
+
+ {
+ LockHolder locker(*m_lock);
+ ASSERT(!m_condition->contains(locker, this));
+ }
+
+ auto stop = [&] (const LockHolder&) {
+ m_isRunning = false;
+ m_isRunningCondition.notifyAll();
+ };
+
+ for (;;) {
+ {
+ LockHolder locker(*m_lock);
+ for (;;) {
+ PollResult result = poll(locker);
+ if (result == PollResult::Work)
+ break;
+ if (result == PollResult::Stop)
+ return stop(locker);
+ RELEASE_ASSERT(result == PollResult::Wait);
+ // Shut the thread down after one second.
+ double timeout = monotonicallyIncreasingTime() + 1;
+ bool awokenByNotify =
+ m_condition->m_condition.waitUntilMonotonicClockSeconds(*m_lock, timeout);
+ if (!awokenByNotify) {
+ if (verbose)
+ dataLog("Going to sleep!\n");
+ m_condition->add(locker, this);
+ return;
+ }
+ }
+ }
+
+ WorkResult result = work();
+ if (result == WorkResult::Stop) {
+ LockHolder locker(*m_lock);
+ return stop(locker);
+ }
+ RELEASE_ASSERT(result == WorkResult::Continue);
+ }
+ });
+ detachThread(thread);
+}
+
+} // namespace WTF
+
Added: trunk/Source/WTF/wtf/AutomaticThread.h (0 => 207480)
--- trunk/Source/WTF/wtf/AutomaticThread.h (rev 0)
+++ trunk/Source/WTF/wtf/AutomaticThread.h 2016-10-18 20:17:10 UTC (rev 207480)
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WTF_AutomaticThread_h
+#define WTF_AutomaticThread_h
+
+#include <wtf/Box.h>
+#include <wtf/Condition.h>
+#include <wtf/Lock.h>
+#include <wtf/Ref.h>
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+namespace WTF {
+
+// Often, we create threads that have this as their body:
+//
+// for (;;) {
+// {
+// LockHolder locker(m_lock);
+// for (;;) {
+// [1] stuff that could break, return, or fall through;
+// m_condition.wait(m_lock);
+// }
+// }
+//
+// [2] do work;
+// }
+//
+// When we do this, we don't always do a good job of managing this thread's lifetime, which may lead
+// to this thread sitting around even when it is not needed.
+//
+// AutomaticThread is here to help you in these situations. It encapsulates a lock, a condition
+// variable, and a thread. It will automatically shut the thread down after 1 second of inactivity.
+// You use AutomaticThread by subclassing it, and put any state that is needed between [1] and [2]
+// in the subclass.
+//
+// The terminology we use is:
+//
+// [1] PollResult AutomaticThread::poll()
+// [2] WordResult AutomaticThread::work()
+//
+// Note that poll() and work() may not be called on the same thread every time, since this will shut
+// down the thread as necessary. This is legal since m_condition.wait(m_lock) can drop the lock, and
+// so there is no reason to keep the thread around.
+
+class AutomaticThread;
+
+class AutomaticThreadCondition : public ThreadSafeRefCounted<AutomaticThread> {
+public:
+ static WTF_EXPORT_PRIVATE RefPtr<AutomaticThreadCondition> create();
+
+ WTF_EXPORT_PRIVATE ~AutomaticThreadCondition();
+
+ WTF_EXPORT_PRIVATE void notifyAll(const LockHolder&);
+
+private:
+ friend class AutomaticThread;
+
+ WTF_EXPORT_PRIVATE AutomaticThreadCondition();
+
+ void add(const LockHolder&, AutomaticThread*);
+ void remove(const LockHolder&, AutomaticThread*);
+ bool contains(const LockHolder&, AutomaticThread*);
+
+ Condition m_condition;
+ Vector<AutomaticThread*> m_threads;
+};
+
+class WTF_EXPORT_PRIVATE AutomaticThread : public ThreadSafeRefCounted<AutomaticThread> {
+public:
+ // Note that if you drop all of your references to an AutomaticThread then as soon as there is a
+ // second during which it doesn't get woken up, it will simply die on its own. This is a
+ // permanent kind of death where the AutomaticThread object goes away, rather than the temporary
+ // kind of death where AutomaticThread lives but its underlying thread dies. All you have to do
+ // to prevent permanent death is keep a ref to AutomaticThread. At time of writing, every user of
+ // AutomaticThread keeps a ref to it and does join() as part of the shutdown process, so only the
+ // temporary kind of automatic death happens in practice. We keep the permanent death feature
+ // because it leads to an easy-to-understand reference counting discipline (AutomaticThread holds
+ // strong ref to AutomaticThreadCondition and the underlying thread holds a strong ref to
+ // AutomaticThread).
+ virtual ~AutomaticThread();
+
+ void join();
+
+protected:
+ // This logically creates the thread, but in reality the thread won't be created until someone
+ // calls AutomaticThreadCondition::notifyAll().
+ AutomaticThread(const LockHolder&, Box<Lock>, RefPtr<AutomaticThreadCondition>);
+
+ // To understand PollResult and WorkResult, imagine that poll() and work() are being called like
+ // so:
+ //
+ // void AutomaticThread::runThread()
+ // {
+ // for (;;) {
+ // {
+ // LockHolder locker(m_lock);
+ // for (;;) {
+ // PollResult result = poll();
+ // if (result == PollResult::Work)
+ // break;
+ // if (result == PollResult::Stop)
+ // return;
+ // RELEASE_ASSERT(result == PollResult::Wait);
+ // m_condition.wait(m_lock);
+ // }
+ // }
+ //
+ // WorkResult result = work();
+ // if (result == WorkResult::Stop)
+ // return;
+ // RELEASE_ASSERT(result == WorkResult::Continue);
+ // }
+ // }
+
+ enum class PollResult { Work, Stop, Wait };
+ virtual PollResult poll(const LockHolder&) = 0;
+
+ enum class WorkResult { Continue, Stop };
+ virtual WorkResult work() = 0;
+
+private:
+ friend class AutomaticThreadCondition;
+
+ void start(const LockHolder&);
+
+ Box<Lock> m_lock;
+ RefPtr<AutomaticThreadCondition> m_condition;
+ bool m_isRunning { true };
+ Condition m_isRunningCondition;
+};
+
+} // namespace WTF
+
+using WTF::AutomaticThread;
+
+#endif // WTF_AutomaticThread_h
+
Modified: trunk/Source/WTF/wtf/CMakeLists.txt (207479 => 207480)
--- trunk/Source/WTF/wtf/CMakeLists.txt 2016-10-18 20:03:18 UTC (rev 207479)
+++ trunk/Source/WTF/wtf/CMakeLists.txt 2016-10-18 20:17:10 UTC (rev 207480)
@@ -2,6 +2,7 @@
ASCIICType.h
Assertions.h
Atomics.h
+ AutomaticThread.h
BackwardsGraph.h
Bag.h
BagToHashMap.h
@@ -173,6 +174,7 @@
set(WTF_SOURCES
Assertions.cpp
Atomics.cpp
+ AutomaticThread.cpp
BitVector.cpp
CompilationThread.cpp
CrossThreadCopier.cpp
Modified: trunk/Source/WTF/wtf/ParallelHelperPool.cpp (207479 => 207480)
--- trunk/Source/WTF/wtf/ParallelHelperPool.cpp 2016-10-18 20:03:18 UTC (rev 207479)
+++ trunk/Source/WTF/wtf/ParallelHelperPool.cpp 2016-10-18 20:17:10 UTC (rev 207480)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,6 +26,7 @@
#include "config.h"
#include "ParallelHelperPool.h"
+#include "AutomaticThread.h"
#include "DataLog.h"
#include "StringPrintStream.h"
@@ -34,7 +35,7 @@
ParallelHelperClient::ParallelHelperClient(RefPtr<ParallelHelperPool> pool)
: m_pool(pool)
{
- LockHolder locker(m_pool->m_lock);
+ LockHolder locker(*m_pool->m_lock);
RELEASE_ASSERT(!m_pool->m_isDying);
m_pool->m_clients.append(this);
}
@@ -41,7 +42,7 @@
ParallelHelperClient::~ParallelHelperClient()
{
- LockHolder locker(m_pool->m_lock);
+ LockHolder locker(*m_pool->m_lock);
finish(locker);
for (size_t i = 0; i < m_pool->m_clients.size(); ++i) {
@@ -55,7 +56,7 @@
void ParallelHelperClient::setTask(RefPtr<SharedTask<void ()>> task)
{
- LockHolder locker(m_pool->m_lock);
+ LockHolder locker(*m_pool->m_lock);
RELEASE_ASSERT(!m_task);
m_task = task;
m_pool->didMakeWorkAvailable(locker);
@@ -63,7 +64,7 @@
void ParallelHelperClient::finish()
{
- LockHolder locker(m_pool->m_lock);
+ LockHolder locker(*m_pool->m_lock);
finish(locker);
}
@@ -71,7 +72,7 @@
{
RefPtr<SharedTask<void ()>> task;
{
- LockHolder locker(m_pool->m_lock);
+ LockHolder locker(*m_pool->m_lock);
task = claimTask(locker);
if (!task)
return;
@@ -91,7 +92,7 @@
{
m_task = nullptr;
while (m_numActive)
- m_pool->m_workCompleteCondition.wait(m_pool->m_lock);
+ m_pool->m_workCompleteCondition.wait(*m_pool->m_lock);
}
RefPtr<SharedTask<void ()>> ParallelHelperClient::claimTask(const LockHolder&)
@@ -111,7 +112,7 @@
task->run();
{
- LockHolder locker(m_pool->m_lock);
+ LockHolder locker(*m_pool->m_lock);
RELEASE_ASSERT(m_numActive);
// No new task could have been installed, since we were still active.
RELEASE_ASSERT(!m_task || m_task == task);
@@ -123,6 +124,8 @@
}
ParallelHelperPool::ParallelHelperPool()
+ : m_lock(Box<Lock>::create())
+ , m_workAvailableCondition(AutomaticThreadCondition::create())
{
}
@@ -131,18 +134,18 @@
RELEASE_ASSERT(m_clients.isEmpty());
{
- LockHolder locker(m_lock);
+ LockHolder locker(*m_lock);
m_isDying = true;
- m_workAvailableCondition.notifyAll();
+ m_workAvailableCondition->notifyAll(locker);
}
- for (ThreadIdentifier threadIdentifier : m_threads)
- waitForThreadCompletion(threadIdentifier);
+ for (RefPtr<AutomaticThread>& thread : m_threads)
+ thread->join();
}
void ParallelHelperPool::ensureThreads(unsigned numThreads)
{
- LockHolder locker(m_lock);
+ LockHolder locker(*m_lock);
if (numThreads < m_numThreads)
return;
m_numThreads = numThreads;
@@ -155,7 +158,7 @@
ParallelHelperClient* client;
RefPtr<SharedTask<void ()>> task;
{
- LockHolder locker(m_lock);
+ LockHolder locker(*m_lock);
client = getClientWithTask(locker);
if (!client)
return;
@@ -165,38 +168,46 @@
client->runTask(task);
}
-void ParallelHelperPool::didMakeWorkAvailable(const LockHolder&)
-{
- while (m_numThreads > m_threads.size()) {
- ThreadIdentifier threadIdentifier = createThread(
- "WTF Parallel Helper Thread",
- [this] () {
- helperThreadBody();
- });
- m_threads.append(threadIdentifier);
+class ParallelHelperPool::Thread : public AutomaticThread {
+public:
+ Thread(const LockHolder& locker, ParallelHelperPool& pool)
+ : AutomaticThread(locker, pool.m_lock, pool.m_workAvailableCondition)
+ , m_pool(pool)
+ {
}
- m_workAvailableCondition.notifyAll();
-}
+
+protected:
+ PollResult poll(const LockHolder& locker) override
+ {
+ if (m_pool.m_isDying)
+ return PollResult::Stop;
+ m_client = m_pool.getClientWithTask(locker);
+ if (m_client) {
+ m_task = m_client->claimTask(locker);
+ return PollResult::Work;
+ }
+ return PollResult::Wait;
+ }
+
+ WorkResult work() override
+ {
+ m_client->runTask(m_task);
+ m_client = nullptr;
+ m_task = nullptr;
+ return WorkResult::Continue;
+ }
+
+private:
+ ParallelHelperPool& m_pool;
+ ParallelHelperClient* m_client { nullptr };
+ RefPtr<SharedTask<void ()>> m_task;
+};
-void ParallelHelperPool::helperThreadBody()
+void ParallelHelperPool::didMakeWorkAvailable(const LockHolder& locker)
{
- for (;;) {
- ParallelHelperClient* client;
- RefPtr<SharedTask<void ()>> task;
-
- {
- LockHolder locker(m_lock);
- client = waitForClientWithTask(locker);
- if (!client) {
- RELEASE_ASSERT(m_isDying);
- return;
- }
-
- task = client->claimTask(locker);
- }
-
- client->runTask(task);
- }
+ while (m_numThreads > m_threads.size())
+ m_threads.append(adoptRef(new Thread(locker, *this)));
+ m_workAvailableCondition->notifyAll(locker);
}
bool ParallelHelperPool::hasClientWithTask(const LockHolder& locker)
@@ -222,20 +233,5 @@
return nullptr;
}
-ParallelHelperClient* ParallelHelperPool::waitForClientWithTask(const LockHolder& locker)
-{
- for (;;) {
- // It might be quittin' time.
- if (m_isDying)
- return nullptr;
-
- if (ParallelHelperClient* result = getClientWithTask(locker))
- return result;
-
- // Wait until work becomes available.
- m_workAvailableCondition.wait(m_lock);
- }
-}
-
} // namespace WTF
Modified: trunk/Source/WTF/wtf/ParallelHelperPool.h (207479 => 207480)
--- trunk/Source/WTF/wtf/ParallelHelperPool.h 2016-10-18 20:03:18 UTC (rev 207479)
+++ trunk/Source/WTF/wtf/ParallelHelperPool.h 2016-10-18 20:17:10 UTC (rev 207480)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2015 Apple Inc. All rights reserved.
+ * Copyright (C) 2015-2016 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
@@ -26,6 +26,7 @@
#ifndef ParallelHelperPool_h
#define ParallelHelperPool_h
+#include <wtf/Box.h>
#include <wtf/Condition.h>
#include <wtf/Lock.h>
#include <wtf/RefPtr.h>
@@ -37,6 +38,9 @@
namespace WTF {
+class AutomaticThread;
+class AutomaticThreadCondition;
+
// A ParallelHelperPool is a shared pool of threads that can be asked to help with some finite-time
// parallel activity. It's designed to work well when there are multiple concurrent tasks that may
// all want parallel help. In that case, we don't want each task to start its own thread pool. It's
@@ -186,22 +190,23 @@
private:
friend class ParallelHelperClient;
+ class Thread;
+ friend class Thread;
void didMakeWorkAvailable(const LockHolder&);
- void helperThreadBody();
bool hasClientWithTask(const LockHolder&);
ParallelHelperClient* getClientWithTask(const LockHolder&);
ParallelHelperClient* waitForClientWithTask(const LockHolder&);
- Lock m_lock;
- Condition m_workAvailableCondition;
+ Box<Lock> m_lock; // AutomaticThread wants this in a box for safety.
+ RefPtr<AutomaticThreadCondition> m_workAvailableCondition;
Condition m_workCompleteCondition;
WeakRandom m_random;
Vector<ParallelHelperClient*> m_clients;
- Vector<ThreadIdentifier> m_threads;
+ Vector<RefPtr<AutomaticThread>> m_threads;
unsigned m_numThreads { 0 }; // This can be larger than m_threads.size() because we start threads only once there is work.
bool m_isDying { false };
};