Diff
Modified: trunk/Source/WTF/ChangeLog (188399 => 188400)
--- trunk/Source/WTF/ChangeLog 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Source/WTF/ChangeLog 2015-08-13 20:42:11 UTC (rev 188400)
@@ -1,3 +1,51 @@
+2015-08-13 Filip Pizlo <fpi...@apple.com>
+
+ WTF should have a compact Condition object to use with Lock
+ https://bugs.webkit.org/show_bug.cgi?id=147986
+
+ Reviewed by Geoffrey Garen.
+
+ Adds a condition variable implementation based on ParkingLot, called simply WTF::Condition.
+ It can be used with WTF::Lock or actually any lock implementation. It should even work with
+ WTF::SpinLock, WTF::Mutex, or std::mutex. Best of all, Condition only requires one byte.
+
+ ParkingLot almost contained all of the functionality needed to implemenet wait/notify. We
+ could have implemented Condition using a 32-bit (or even 64-bit) version that protects
+ against a notify that happens just before we park. But, this changes the ParkingLot API to
+ give us the ability to run some code between when ParkingLot enqueues the current thread
+ and when it actually sleeps. This callback is called with no locks held, so it can call
+ unlock() on any kind of lock, so long as that lock's unlock() method doesn't recurse into
+ ParkingLot::parkConditionally(). That seems unlikely; unlock() is more likely to call
+ ParkingLot::unparkOne() or unparkAll(). WTF::Lock will never call parkConditionally()
+ inside unlock(), so WTF::Lock is definitely appropriate for use with Condition.
+
+ Condition supports most of the API that std::condition_variable supports. It does some
+ things to try to reduce footgun potential. The preferred timeout form is waitUntil() which
+ takes an absolute time from the steady_clock. The only relative timeout form also takes a
+ predicate callback, so it's impossible to write the subtly incorrect
+ "while (...) wait_for(...)" idiom.
+
+ This patch doesn't actually introduce any uses of WTF::Condition other than the unit tests.
+ I'll start switching code over to using WTF::Condition in another patch.
+
+ * WTF.vcxproj/WTF.vcxproj:
+ * WTF.xcodeproj/project.pbxproj:
+ * wtf/CMakeLists.txt:
+ * wtf/Condition.h: Added.
+ (WTF::Condition::Condition):
+ (WTF::Condition::waitUntil):
+ (WTF::Condition::waitFor):
+ (WTF::Condition::wait):
+ (WTF::Condition::notifyOne):
+ (WTF::Condition::notifyAll):
+ * wtf/Lock.cpp:
+ (WTF::LockBase::unlockSlow): Make this useful assertion be a release assertion. It catches cases where you unlock the lock even though you don't hold it.
+ * wtf/ParkingLot.cpp:
+ (WTF::ParkingLot::parkConditionally): Add the beforeSleep() callback.
+ (WTF::ParkingLot::unparkOne):
+ * wtf/ParkingLot.h:
+ (WTF::ParkingLot::compareAndPark):
+
2015-08-12 Anders Carlsson <ander...@apple.com>
Use WTF::Optional in WindowFeatures
Modified: trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj (188399 => 188400)
--- trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj 2015-08-13 20:42:11 UTC (rev 188400)
@@ -176,6 +176,7 @@
<ClInclude Include="..\wtf\CheckedArithmetic.h" />
<ClInclude Include="..\wtf\CheckedBoolean.h" />
<ClInclude Include="..\wtf\Compiler.h" />
+ <ClInclude Include="..\wtf\Condition.h" />
<ClInclude Include="..\wtf\CryptographicUtilities.h" />
<ClInclude Include="..\wtf\CryptographicallyRandomNumber.h" />
<ClInclude Include="..\wtf\CurrentTime.h" />
Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (188399 => 188400)
--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2015-08-13 20:42:11 UTC (rev 188400)
@@ -40,6 +40,7 @@
0FC4488316FE9FE100844BE9 /* ProcessID.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC4488216FE9FE100844BE9 /* ProcessID.h */; };
0FC4EDE61696149600F65041 /* CommaPrinter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FC4EDE51696149600F65041 /* CommaPrinter.h */; };
0FD81AC5154FB22E00983E72 /* FastBitVector.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD81AC4154FB22E00983E72 /* FastBitVector.h */; settings = {ATTRIBUTES = (); }; };
+ 0FDB698E1B7C643A000C1078 /* Condition.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDB698D1B7C643A000C1078 /* Condition.h */; };
0FDDBFA71666DFA300C55FEF /* StringPrintStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FDDBFA51666DFA300C55FEF /* StringPrintStream.cpp */; };
0FDDBFA81666DFA300C55FEF /* StringPrintStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDDBFA61666DFA300C55FEF /* StringPrintStream.h */; };
0FE1646A1B6FFC9600400E7C /* Lock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FE164681B6FFC9600400E7C /* Lock.cpp */; };
@@ -327,6 +328,7 @@
0FC4488216FE9FE100844BE9 /* ProcessID.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessID.h; sourceTree = "<group>"; };
0FC4EDE51696149600F65041 /* CommaPrinter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommaPrinter.h; sourceTree = "<group>"; };
0FD81AC4154FB22E00983E72 /* FastBitVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FastBitVector.h; sourceTree = "<group>"; };
+ 0FDB698D1B7C643A000C1078 /* Condition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Condition.h; sourceTree = "<group>"; };
0FDDBFA51666DFA300C55FEF /* StringPrintStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StringPrintStream.cpp; sourceTree = "<group>"; };
0FDDBFA61666DFA300C55FEF /* StringPrintStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StringPrintStream.h; sourceTree = "<group>"; };
0FE164681B6FFC9600400E7C /* Lock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Lock.cpp; sourceTree = "<group>"; };
@@ -730,6 +732,7 @@
0F8F2B8F172E00F0007DBDA5 /* CompilationThread.cpp */,
0F8F2B90172E00F0007DBDA5 /* CompilationThread.h */,
A8A47270151A825A004123FF /* Compiler.h */,
+ 0FDB698D1B7C643A000C1078 /* Condition.h */,
E15556F318A0CC18006F48FB /* CryptographicUtilities.cpp */,
E15556F418A0CC18006F48FB /* CryptographicUtilities.h */,
A8A47273151A825A004123FF /* CryptographicallyRandomNumber.cpp */,
@@ -1185,6 +1188,7 @@
A8A47423151A825B004123FF /* SimpleStats.h in Headers */,
A8A47424151A825B004123FF /* SinglyLinkedList.h in Headers */,
A748745317A0BDAE00FA04CB /* SixCharacterHash.h in Headers */,
+ 0FDB698E1B7C643A000C1078 /* Condition.h in Headers */,
A8A47426151A825B004123FF /* Spectrum.h in Headers */,
A8A47428151A825B004123FF /* StackBounds.h in Headers */,
FEDACD3E1630F83F00C69634 /* StackStats.h in Headers */,
Modified: trunk/Source/WTF/wtf/CMakeLists.txt (188399 => 188400)
--- trunk/Source/WTF/wtf/CMakeLists.txt 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Source/WTF/wtf/CMakeLists.txt 2015-08-13 20:42:11 UTC (rev 188400)
@@ -10,6 +10,7 @@
ByteOrder.h
CompilationThread.h
Compiler.h
+ Condition.h
CryptographicUtilities.h
CryptographicallyRandomNumber.h
CurrentTime.h
Added: trunk/Source/WTF/wtf/Condition.h (0 => 188400)
--- trunk/Source/WTF/wtf/Condition.h (rev 0)
+++ trunk/Source/WTF/wtf/Condition.h 2015-08-13 20:42:11 UTC (rev 188400)
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 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_Condition_h
+#define WTF_Condition_h
+
+#include <chrono>
+#include <functional>
+#include <mutex>
+#include <wtf/ParkingLot.h>
+
+namespace WTF {
+
+class Condition {
+public:
+ Condition() { }
+
+ // Wait on a parking queue while releasing the given lock. It will unlock the lock just before
+ // parking, and relock it upon wakeup. Returns true if we woke up due to some call to
+ // notifyOne() or notifyAll(). Returns false if we woke up due to a timeout.
+ template<typename LockType>
+ bool waitUntil(
+ LockType& lock, std::chrono::steady_clock::time_point timeout)
+ {
+ bool result = ParkingLot::parkConditionally(
+ &m_dummy,
+ [] () -> bool { return true; },
+ [&lock] () { lock.unlock(); },
+ timeout);
+ lock.lock();
+ return result;
+ }
+
+ // Wait until the given predicate is satisfied. Returns true if it is satisfied in the end.
+ // May return early due to timeout.
+ template<typename LockType, typename Functor>
+ bool waitUntil(
+ LockType& lock, std::chrono::steady_clock::time_point timeout, const Functor& predicate)
+ {
+ while (!predicate()) {
+ if (!waitUntil(lock, timeout))
+ return predicate();
+ }
+ return true;
+ }
+
+ // Wait until the given predicate is satisfied. Returns true if it is satisfied in the end.
+ // May return early due to timeout.
+ template<typename LockType, typename DurationType, typename Functor>
+ bool waitFor(
+ LockType& lock, const DurationType& relativeTimeout, const Functor& predicate)
+ {
+ std::chrono::steady_clock::time_point absoluteTimeout =
+ std::chrono::steady_clock::now() +
+ std::chrono::duration_cast<std::chrono::steady_clock::duration>(relativeTimeout);
+
+ return waitUntil(lock, absoluteTimeout, predicate);
+ }
+
+ template<typename LockType>
+ void wait(LockType& lock)
+ {
+ waitUntil(lock, std::chrono::steady_clock::time_point::max());
+ }
+
+ template<typename LockType, typename Functor>
+ void wait(LockType& lock, const Functor& predicate)
+ {
+ while (!predicate())
+ wait(lock);
+ }
+
+ void notifyOne()
+ {
+ ParkingLot::unparkOne(&m_dummy);
+ }
+
+ void notifyAll()
+ {
+ ParkingLot::unparkAll(&m_dummy);
+ }
+
+private:
+
+ uint8_t m_dummy;
+};
+
+} // namespace WTF
+
+#endif // WTF_Condition_h
+
Modified: trunk/Source/WTF/wtf/Lock.cpp (188399 => 188400)
--- trunk/Source/WTF/wtf/Lock.cpp 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Source/WTF/wtf/Lock.cpp 2015-08-13 20:42:11 UTC (rev 188400)
@@ -81,7 +81,7 @@
// be held and parked if someone attempts to lock just as we are unlocking.
for (;;) {
uint8_t oldByteValue = m_byte.load();
- ASSERT(oldByteValue == isHeldBit || oldByteValue == (isHeldBit | hasParkedBit));
+ RELEASE_ASSERT(oldByteValue == isHeldBit || oldByteValue == (isHeldBit | hasParkedBit));
if (oldByteValue == isHeldBit) {
if (m_byte.compareExchangeWeak(isHeldBit, 0))
Modified: trunk/Source/WTF/wtf/ParkingLot.cpp (188399 => 188400)
--- trunk/Source/WTF/wtf/ParkingLot.cpp 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Source/WTF/wtf/ParkingLot.cpp 2015-08-13 20:42:11 UTC (rev 188400)
@@ -507,13 +507,20 @@
} // anonymous namespace
-bool ParkingLot::parkConditionally(const void* address, std::function<bool()> validation)
+bool ParkingLot::parkConditionally(
+ const void* address,
+ std::function<bool()> validation,
+ std::function<void()> beforeSleep,
+ std::chrono::steady_clock::time_point timeout)
{
if (verbose)
dataLog(toString(currentThread(), ": parking.\n"));
ThreadData* me = myThreadData();
+ // Guard against someone calling parkConditionally() recursively from beforeSleep().
+ RELEASE_ASSERT(!me->address);
+
bool result = enqueue(
address,
[&] () -> ThreadData* {
@@ -527,16 +534,49 @@
if (!result)
return false;
- if (verbose)
- dataLog(toString(currentThread(), ": parking self: ", RawPointer(me), "\n"));
+ beforeSleep();
+
+ bool didGetDequeued;
{
std::unique_lock<std::mutex> locker(me->parkingLock);
- while (me->address)
- me->parkingCondition.wait(locker);
+ while (me->address && std::chrono::steady_clock::now() < timeout)
+ me->parkingCondition.wait_until(locker, timeout);
+ ASSERT(!me->address || me->address == address);
+ didGetDequeued = !me->address;
}
- if (verbose)
- dataLog(toString(currentThread(), ": unparked self: ", RawPointer(me), "\n"));
- return true;
+ if (didGetDequeued) {
+ // Great! We actually got dequeued rather than the timeout expiring.
+ return true;
+ }
+
+ // Have to remove ourselves from the queue since we timed out and nobody has dequeued us yet.
+
+ // It's possible that we get unparked right here, just before dequeue() grabs a lock. It's
+ // probably worthwhile to detect when this happens, and return true in that case, to ensure
+ // that when we return false it really means that no unpark could have been responsible for us
+ // waking up, and that if an unpark call did happen, it woke someone else up.
+ bool didFind = false;
+ dequeue(
+ address, BucketMode::IgnoreEmpty,
+ [&] (ThreadData* element) {
+ if (element == me) {
+ didFind = true;
+ return DequeueResult::RemoveAndStop;
+ }
+ return DequeueResult::Ignore;
+ },
+ [] (bool) { });
+
+ ASSERT(!me->nextInQueue);
+
+ // Make sure that no matter what, me->address is null after this point.
+ {
+ std::lock_guard<std::mutex> locker(me->parkingLock);
+ me->address = nullptr;
+ }
+
+ // If we were not found in the search above, then we know that someone unparked us.
+ return !didFind;
}
bool ParkingLot::unparkOne(const void* address)
Modified: trunk/Source/WTF/wtf/ParkingLot.h (188399 => 188400)
--- trunk/Source/WTF/wtf/ParkingLot.h 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Source/WTF/wtf/ParkingLot.h 2015-08-13 20:42:11 UTC (rev 188400)
@@ -26,6 +26,7 @@
#ifndef WTF_ParkingLot_h
#define WTF_ParkingLot_h
+#include <chrono>
#include <functional>
#include <wtf/Atomics.h>
#include <wtf/Threading.h>
@@ -39,10 +40,23 @@
public:
// Parks the thread in a queue associated with the given address, which cannot be null. The
// parking only succeeds if the validation function returns true while the queue lock is held.
- // Returns true if the thread actually slept, or false if it returned quickly because of
- // validation failure.
- WTF_EXPORT_PRIVATE static bool parkConditionally(const void* address, std::function<bool()> validation);
+ // If validation returns false, it will unlock the internal parking queue and then it will
+ // return without doing anything else. If validation returns true, it will enqueue the thread,
+ // unlock the parking queue lock, call the beforeSleep function, and then it will sleep so long
+ // as the thread continues to be on the queue and the timeout hasn't fired. Finally, this
+ // returns true if we actually got unparked or false if the timeout was hit. Note that
+ // beforeSleep is called with no locks held, so it's OK to do pretty much anything so long as
+ // you don't recursively call parkConditionally(). You can call unparkOne()/unparkAll() though.
+ // It's useful to use beforeSleep() to unlock some mutex in the implementation of
+ // Condition::wait().
+ WTF_EXPORT_PRIVATE static bool parkConditionally(
+ const void* address,
+ std::function<bool()> validation,
+ std::function<void()> beforeSleep,
+ std::chrono::steady_clock::time_point timeout);
+ // Simple version of parkConditionally() that covers the most common case: you want to park
+ // indefinitely so long as the value at the given address hasn't changed.
template<typename T, typename U>
static bool compareAndPark(const Atomic<T>* address, U expected)
{
@@ -51,7 +65,9 @@
[address, expected] () -> bool {
U value = address->load();
return value == expected;
- });
+ },
+ [] () { },
+ std::chrono::steady_clock::time_point::max());
}
// Unparks one thread from the queue associated with the given address, which cannot be null.
Modified: trunk/Tools/ChangeLog (188399 => 188400)
--- trunk/Tools/ChangeLog 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Tools/ChangeLog 2015-08-13 20:42:11 UTC (rev 188400)
@@ -1,5 +1,22 @@
2015-08-13 Filip Pizlo <fpi...@apple.com>
+ WTF should have a compact Condition object to use with Lock
+ https://bugs.webkit.org/show_bug.cgi?id=147986
+
+ Reviewed by Geoffrey Garen.
+
+ Add a test for WTF::Condition.
+
+ * TestWebKitAPI/CMakeLists.txt:
+ * TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj:
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WTF/Condition.cpp: Added.
+ (TestWebKitAPI::TEST):
+ * TestWebKitAPI/Tests/WTF/Lock.cpp:
+ (TestWebKitAPI::runLockTest): Change the name of the thread.
+
+2015-08-13 Filip Pizlo <fpi...@apple.com>
+
Unreviewed, shorten another test. It's timing out in debug on some bot.
* TestWebKitAPI/Tests/WTF/Lock.cpp:
Modified: trunk/Tools/TestWebKitAPI/CMakeLists.txt (188399 => 188400)
--- trunk/Tools/TestWebKitAPI/CMakeLists.txt 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Tools/TestWebKitAPI/CMakeLists.txt 2015-08-13 20:42:11 UTC (rev 188400)
@@ -42,6 +42,7 @@
${TESTWEBKITAPI_DIR}/TestsController.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/AtomicString.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/CString.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/Condition.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/CheckedArithmeticOperations.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/DateMath.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/Deque.cpp
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj (188399 => 188400)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj 2015-08-13 20:42:11 UTC (rev 188400)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugSuffix|Win32">
@@ -312,6 +312,7 @@
<ClCompile Include="..\Tests\WTF\cf\RetainPtr.cpp" />
<ClCompile Include="..\Tests\WTF\cf\RetainPtrHashing.cpp" />
<ClCompile Include="..\Tests\WTF\CheckedArithmeticOperations.cpp" />
+ <ClCompile Include="..\Tests\WTF\Condition.cpp" />
<ClCompile Include="..\Tests\WTF\CString.cpp" />
<ClCompile Include="..\Tests\WTF\DateMath.cpp" />
<ClCompile Include="..\Tests\WTF\Deque.cpp" />
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (188399 => 188400)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2015-08-13 20:42:11 UTC (rev 188400)
@@ -12,6 +12,7 @@
0F139E791A42457000F590F5 /* PlatformUtilitiesCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E721A423A2B00F590F5 /* PlatformUtilitiesCocoa.mm */; };
0F3B94A71A77267400DE3272 /* WKWebViewEvaluateJavaScript.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F3B94A51A77266C00DE3272 /* WKWebViewEvaluateJavaScript.mm */; };
0FE447991B76F1EB009498EB /* ParkingLot.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FE447971B76F1E3009498EB /* ParkingLot.cpp */; };
+ 0FEAE3691B7D19D200CE17F2 /* Condition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FEAE3671B7D19CB00CE17F2 /* Condition.cpp */; };
0FFC45A61B73EBEB0085BD62 /* Lock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FFC45A41B73EBE20085BD62 /* Lock.cpp */; };
1A02C870125D4CFD00E3F4BD /* find.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 1A02C84B125D4A5E00E3F4BD /* find.html */; };
1A50AA201A2A51FC00F4C345 /* close-from-within-create-page.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 1A50AA1F1A2A4EA500F4C345 /* close-from-within-create-page.html */; };
@@ -431,6 +432,7 @@
0FC6C4CB141027E0005B7F0C /* RedBlackTree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = RedBlackTree.cpp; sourceTree = "<group>"; };
0FC6C4CE141034AD005B7F0C /* MetaAllocator.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MetaAllocator.cpp; sourceTree = "<group>"; };
0FE447971B76F1E3009498EB /* ParkingLot.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ParkingLot.cpp; sourceTree = "<group>"; };
+ 0FEAE3671B7D19CB00CE17F2 /* Condition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Condition.cpp; sourceTree = "<group>"; };
0FFC45A41B73EBE20085BD62 /* Lock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Lock.cpp; sourceTree = "<group>"; };
14464012167A8305000BD218 /* LayoutUnit.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LayoutUnit.cpp; sourceTree = "<group>"; };
14F3B11215E45EAB00210069 /* SaturatedArithmeticOperations.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SaturatedArithmeticOperations.cpp; sourceTree = "<group>"; };
@@ -1091,6 +1093,7 @@
BC029B1A1486B23800817DA9 /* ns */,
26F1B44215CA434F00D1E4BF /* AtomicString.cpp */,
A7A966DA140ECCC8005EF9B4 /* CheckedArithmeticOperations.cpp */,
+ 0FEAE3671B7D19CB00CE17F2 /* Condition.cpp */,
26A2C72E15E2E73C005B1A14 /* CString.cpp */,
E40019301ACE9B5C001B0A2A /* BloomFilter.cpp */,
7AA021BA1AB09EA70052953F /* DateMath.cpp */,
@@ -1494,6 +1497,7 @@
7CCE7EBF1A411A7E00447C4C /* ElementAtPointInWebFrame.mm in Sources */,
7CCE7EEF1A411AE600447C4C /* EphemeralSessionPushStateNoHistoryCallback.cpp in Sources */,
7CCE7EF01A411AE600447C4C /* EvaluateJavaScript.cpp in Sources */,
+ 0FEAE3691B7D19D200CE17F2 /* Condition.cpp in Sources */,
7CCE7EF11A411AE600447C4C /* FailedLoad.cpp in Sources */,
7CCE7EF31A411AE600447C4C /* Find.cpp in Sources */,
7CCE7EF41A411AE600447C4C /* FindMatches.mm in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WTF/Condition.cpp (0 => 188400)
--- trunk/Tools/TestWebKitAPI/Tests/WTF/Condition.cpp (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/Condition.cpp 2015-08-13 20:42:11 UTC (rev 188400)
@@ -0,0 +1,257 @@
+/*
+ * Copyright (C) 2015 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. AND ITS CONTRIBUTORS ``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 ITS 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 <mutex>
+#include <thread>
+#include <wtf/Condition.h>
+#include <wtf/DataLog.h>
+#include <wtf/Deque.h>
+#include <wtf/Lock.h>
+#include <wtf/StringPrintStream.h>
+#include <wtf/Threading.h>
+#include <wtf/Vector.h>
+
+using namespace WTF;
+
+namespace TestWebKitAPI {
+
+namespace {
+
+const bool verbose = false;
+
+enum NotifyStyle {
+ AlwaysNotifyOne,
+ TacticallyNotifyAll
+};
+
+template<typename Functor>
+void wait(Condition& condition, std::unique_lock<Lock>& locker, const Functor& predicate, std::chrono::microseconds timeout)
+{
+ if (timeout == std::chrono::microseconds::max())
+ condition.wait(locker, predicate);
+ else {
+ // This tests timeouts in the sense that it verifies that we can call wait() again after a
+ // timeout happened. That's a non-trivial piece of functionality since upon timeout the
+ // ParkingLot has to remove us from the queue.
+ while (!predicate())
+ condition.waitFor(locker, timeout, predicate);
+ }
+}
+
+void notify(NotifyStyle notifyStyle, Condition& condition, bool shouldNotify)
+{
+ switch (notifyStyle) {
+ case AlwaysNotifyOne:
+ condition.notifyOne();
+ break;
+ case TacticallyNotifyAll:
+ if (shouldNotify)
+ condition.notifyAll();
+ break;
+ }
+}
+
+void runTest(
+ unsigned numProducers,
+ unsigned numConsumers,
+ unsigned maxQueueSize,
+ unsigned numMessagesPerProducer,
+ NotifyStyle notifyStyle,
+ std::chrono::microseconds timeout = std::chrono::microseconds::max(),
+ std::chrono::microseconds delay = std::chrono::microseconds::zero())
+{
+ Deque<unsigned> queue;
+ bool shouldContinue = true;
+ Lock lock;
+ Condition emptyCondition;
+ Condition fullCondition;
+
+ Vector<ThreadIdentifier> consumerThreads;
+ Vector<ThreadIdentifier> producerThreads;
+
+ Vector<unsigned> received;
+ Lock receivedLock;
+
+ for (unsigned i = numConsumers; i--;) {
+ ThreadIdentifier threadIdentifier = createThread(
+ "Consumer thread",
+ [&] () {
+ for (;;) {
+ unsigned result;
+ unsigned shouldNotify = false;
+ {
+ std::unique_lock<Lock> locker(lock);
+ wait(
+ emptyCondition, locker,
+ [&] () {
+ if (verbose)
+ dataLog(toString(currentThread(), ": Checking consumption predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n"));
+ return !shouldContinue || !queue.isEmpty();
+ },
+ timeout);
+ if (!shouldContinue)
+ return;
+ shouldNotify = queue.size() == maxQueueSize;
+ result = queue.takeFirst();
+ }
+ notify(notifyStyle, fullCondition, shouldNotify);
+
+ {
+ std::lock_guard<Lock> locker(receivedLock);
+ received.append(result);
+ }
+ }
+ });
+ consumerThreads.append(threadIdentifier);
+ }
+
+ std::this_thread::sleep_for(delay);
+
+ for (unsigned i = numProducers; i--;) {
+ ThreadIdentifier threadIdentifier = createThread(
+ "Producer Thread",
+ [&] () {
+ for (unsigned i = 0; i < numMessagesPerProducer; ++i) {
+ bool shouldNotify = false;
+ {
+ std::unique_lock<Lock> locker(lock);
+ wait(
+ fullCondition, locker,
+ [&] () {
+ if (verbose)
+ dataLog(toString(currentThread(), ": Checking production predicate with shouldContinue = ", shouldContinue, ", queue.size() == ", queue.size(), "\n"));
+ return queue.size() < maxQueueSize;
+ },
+ timeout);
+ shouldNotify = queue.isEmpty();
+ queue.append(i);
+ }
+ notify(notifyStyle, emptyCondition, shouldNotify);
+ }
+ });
+ producerThreads.append(threadIdentifier);
+ }
+
+ for (ThreadIdentifier threadIdentifier : producerThreads)
+ waitForThreadCompletion(threadIdentifier);
+
+ {
+ std::lock_guard<Lock> locker(lock);
+ shouldContinue = false;
+ }
+ emptyCondition.notifyAll();
+
+ for (ThreadIdentifier threadIdentifier : consumerThreads)
+ waitForThreadCompletion(threadIdentifier);
+
+ EXPECT_EQ(numProducers * numMessagesPerProducer, received.size());
+ std::sort(received.begin(), received.end());
+ for (unsigned messageIndex = 0; messageIndex < numMessagesPerProducer; ++messageIndex) {
+ for (unsigned producerIndex = 0; producerIndex < numProducers; ++producerIndex)
+ EXPECT_EQ(messageIndex, received[messageIndex * numProducers + producerIndex]);
+ }
+}
+
+} // anonymous namespace
+
+TEST(WTF_Condition, OneProducerOneConsumerOneSlot)
+{
+ runTest(1, 1, 1, 100000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, OneProducerOneConsumerOneSlotTimeout)
+{
+ runTest(
+ 1, 1, 1, 100000, TacticallyNotifyAll,
+ std::chrono::microseconds(10000),
+ std::chrono::microseconds(1000000));
+}
+
+TEST(WTF_Condition, OneProducerOneConsumerHundredSlots)
+{
+ runTest(1, 1, 100, 1000000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersOneConsumerOneSlot)
+{
+ runTest(10, 1, 1, 10000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersOneConsumerHundredSlotsNotifyAll)
+{
+ runTest(10, 1, 100, 10000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersOneConsumerHundredSlotsNotifyOne)
+{
+ runTest(10, 1, 100, 10000, AlwaysNotifyOne);
+}
+
+TEST(WTF_Condition, OneProducerTenConsumersOneSlot)
+{
+ runTest(1, 10, 1, 10000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, OneProducerTenConsumersHundredSlotsNotifyAll)
+{
+ runTest(1, 10, 100, 100000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, OneProducerTenConsumersHundredSlotsNotifyOne)
+{
+ runTest(1, 10, 100, 100000, AlwaysNotifyOne);
+}
+
+TEST(WTF_Condition, TenProducersTenConsumersOneSlot)
+{
+ runTest(10, 10, 1, 50000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersTenConsumersHundredSlotsNotifyAll)
+{
+ runTest(10, 10, 100, 50000, TacticallyNotifyAll);
+}
+
+TEST(WTF_Condition, TenProducersTenConsumersHundredSlotsNotifyOne)
+{
+ runTest(10, 10, 100, 50000, AlwaysNotifyOne);
+}
+
+TEST(WTF_Condition, TimeoutTimesOut)
+{
+ Lock lock;
+ Condition condition;
+
+ lock.lock();
+ bool result = condition.waitFor(
+ lock, std::chrono::microseconds(10000), [] () -> bool { return false; });
+ lock.unlock();
+
+ EXPECT_EQ(false, result);
+}
+
+} // namespace TestWebKitAPI
+
Modified: trunk/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp (188399 => 188400)
--- trunk/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp 2015-08-13 20:30:32 UTC (rev 188399)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp 2015-08-13 20:42:11 UTC (rev 188400)
@@ -53,7 +53,7 @@
for (unsigned threadIndex = numThreadsPerGroup; threadIndex--;) {
threads[threadGroupIndex * numThreadsPerGroup + threadIndex] = createThread(
- "Benchmark thread",
+ "Lock test thread",
[threadGroupIndex, &locks, &words, numIterations, workPerCriticalSection] () {
for (unsigned i = numIterations; i--;) {
locks[threadGroupIndex].lock();