Diff
Modified: trunk/Source/_javascript_Core/ChangeLog (188099 => 188100)
--- trunk/Source/_javascript_Core/ChangeLog 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/ChangeLog 2015-08-07 00:49:54 UTC (rev 188100)
@@ -1,3 +1,28 @@
+2015-08-05 Filip Pizlo <fpi...@apple.com>
+
+ Lightweight locks should be adaptive
+ https://bugs.webkit.org/show_bug.cgi?id=147545
+
+ Reviewed by Geoffrey Garen.
+
+ * heap/CopiedBlock.h:
+ (JSC::CopiedBlock::workListLock):
+ * heap/CopiedBlockInlines.h:
+ (JSC::CopiedBlock::shouldReportLiveBytes):
+ (JSC::CopiedBlock::reportLiveBytes):
+ * heap/CopiedSpace.h:
+ (JSC::CopiedSpace::CopiedGeneration::CopiedGeneration):
+ * heap/CopiedSpaceInlines.h:
+ (JSC::CopiedSpace::recycleEvacuatedBlock):
+ * heap/GCThreadSharedData.h:
+ (JSC::GCThreadSharedData::getNextBlocksToCopy):
+ * heap/ListableHandler.h:
+ (JSC::ListableHandler::List::addThreadSafe):
+ (JSC::ListableHandler::List::addNotThreadSafe):
+ * heap/SlotVisitorInlines.h:
+ (JSC::SlotVisitor::copyLater):
+ * runtime/TypeProfilerLog.h:
+
2015-08-06 Sukolsak Sakshuwong <sukol...@gmail.com>
Parse the entire WebAssembly modules
Modified: trunk/Source/_javascript_Core/dfg/DFGCommon.cpp (188099 => 188100)
--- trunk/Source/_javascript_Core/dfg/DFGCommon.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/dfg/DFGCommon.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -34,7 +34,7 @@
namespace JSC { namespace DFG {
-static StaticSpinLock crashLock;
+static StaticLock crashLock;
void startCrashing()
{
Modified: trunk/Source/_javascript_Core/heap/CopiedBlock.h (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/CopiedBlock.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/CopiedBlock.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -31,7 +31,7 @@
#include "Options.h"
#include <wtf/Atomics.h>
#include <wtf/DoublyLinkedList.h>
-#include <wtf/SpinLock.h>
+#include <wtf/Lock.h>
namespace JSC {
@@ -54,8 +54,8 @@
void didPromote();
unsigned liveBytes();
- bool shouldReportLiveBytes(SpinLockHolder&, JSCell* owner);
- void reportLiveBytes(SpinLockHolder&, JSCell*, CopyToken, unsigned);
+ bool shouldReportLiveBytes(LockHolder&, JSCell* owner);
+ void reportLiveBytes(LockHolder&, JSCell*, CopyToken, unsigned);
void reportLiveBytesDuringCopying(unsigned);
void didSurviveGC();
void didEvacuateBytes(unsigned);
@@ -85,7 +85,7 @@
bool hasWorkList();
CopyWorkList& workList();
- SpinLock& workListLock() { return m_workListLock; }
+ Lock& workListLock() { return m_workListLock; }
private:
CopiedBlock(size_t);
@@ -98,7 +98,7 @@
size_t m_capacity;
- SpinLock m_workListLock;
+ Lock m_workListLock;
std::unique_ptr<CopyWorkList> m_workList;
size_t m_remaining;
Modified: trunk/Source/_javascript_Core/heap/CopiedBlockInlines.h (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/CopiedBlockInlines.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/CopiedBlockInlines.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -33,7 +33,7 @@
namespace JSC {
-inline bool CopiedBlock::shouldReportLiveBytes(SpinLockHolder&, JSCell* owner)
+inline bool CopiedBlock::shouldReportLiveBytes(LockHolder&, JSCell* owner)
{
// We want to add to live bytes if the owner isn't part of the remembered set or
// if this block was allocated during the last cycle.
@@ -43,7 +43,7 @@
return !Heap::isRemembered(owner) || !m_isOld;
}
-inline void CopiedBlock::reportLiveBytes(SpinLockHolder&, JSCell* owner, CopyToken token, unsigned bytes)
+inline void CopiedBlock::reportLiveBytes(LockHolder&, JSCell* owner, CopyToken token, unsigned bytes)
{
checkConsistency();
#ifndef NDEBUG
Modified: trunk/Source/_javascript_Core/heap/CopiedSpace.cpp (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/CopiedSpace.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/CopiedSpace.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -191,7 +191,7 @@
{
// Always put the block into the old gen because it's being promoted!
- SpinLockHolder locker(&m_toSpaceLock);
+ LockHolder locker(&m_toSpaceLock);
m_oldGen.toSpace->push(block);
m_blockSet.add(block);
m_oldGen.blockFilter.add(reinterpret_cast<Bits>(block));
Modified: trunk/Source/_javascript_Core/heap/CopiedSpace.h (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/CopiedSpace.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/CopiedSpace.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -33,9 +33,9 @@
#include <wtf/CheckedBoolean.h>
#include <wtf/DoublyLinkedList.h>
#include <wtf/HashSet.h>
+#include <wtf/Lock.h>
#include <wtf/OSAllocator.h>
#include <wtf/PageBlock.h>
-#include <wtf/SpinLock.h>
#include <wtf/StdLibExtras.h>
#include <wtf/ThreadingPrimitives.h>
@@ -113,7 +113,7 @@
HashSet<CopiedBlock*> m_blockSet;
- SpinLock m_toSpaceLock;
+ Lock m_toSpaceLock;
struct CopiedGeneration {
CopiedGeneration()
Modified: trunk/Source/_javascript_Core/heap/CopiedSpaceInlines.h (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/CopiedSpaceInlines.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/CopiedSpaceInlines.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -98,7 +98,7 @@
ASSERT(block->canBeRecycled());
ASSERT(!block->m_isPinned);
{
- SpinLockHolder locker(&m_toSpaceLock);
+ LockHolder locker(&m_toSpaceLock);
m_blockSet.remove(block);
if (collectionType == EdenCollection)
m_newGen.fromSpace->remove(block);
Modified: trunk/Source/_javascript_Core/heap/GCThreadSharedData.cpp (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/GCThreadSharedData.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/GCThreadSharedData.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -178,7 +178,7 @@
void GCThreadSharedData::didStartCopying()
{
{
- SpinLockHolder locker(&m_copyLock);
+ LockHolder locker(&m_copyLock);
if (m_vm->heap.operationInProgress() == EdenCollection) {
// Reset the vector to be empty, but don't throw away the backing store.
m_blocksToCopy.shrink(0);
Modified: trunk/Source/_javascript_Core/heap/GCThreadSharedData.h (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/GCThreadSharedData.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/GCThreadSharedData.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -33,7 +33,7 @@
#include "WeakReferenceHarvester.h"
#include <condition_variable>
#include <wtf/HashSet.h>
-#include <wtf/SpinLock.h>
+#include <wtf/Lock.h>
#include <wtf/Vector.h>
namespace JSC {
@@ -97,7 +97,7 @@
std::mutex m_opaqueRootsMutex;
HashSet<void*> m_opaqueRoots;
- SpinLock m_copyLock;
+ Lock m_copyLock;
Vector<CopiedBlock*> m_blocksToCopy;
size_t m_copyIndex;
static const size_t s_blockFragmentLength = 32;
@@ -115,7 +115,7 @@
inline void GCThreadSharedData::getNextBlocksToCopy(size_t& start, size_t& end)
{
- SpinLockHolder locker(&m_copyLock);
+ LockHolder locker(&m_copyLock);
start = m_copyIndex;
end = std::min(m_blocksToCopy.size(), m_copyIndex + s_blockFragmentLength);
m_copyIndex = end;
Modified: trunk/Source/_javascript_Core/heap/ListableHandler.h (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/ListableHandler.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/ListableHandler.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -21,9 +21,9 @@
#define ListableHandler_h
#include <stdint.h>
+#include <wtf/Lock.h>
#include <wtf/Locker.h>
#include <wtf/Noncopyable.h>
-#include <wtf/SpinLock.h>
#include <wtf/ThreadingPrimitives.h>
namespace JSC {
@@ -65,7 +65,7 @@
void addThreadSafe(T* handler)
{
- SpinLockHolder locker(&m_lock);
+ LockHolder locker(&m_lock);
addNotThreadSafe(handler);
}
@@ -103,7 +103,7 @@
m_first = handler;
}
- SpinLock m_lock;
+ Lock m_lock;
T* m_first;
};
Modified: trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/MachineStackMarker.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -568,8 +568,8 @@
{
// Prevent two VMs from suspending each other's threads at the same time,
// which can cause deadlock: <rdar://problem/20300842>.
- static StaticSpinLock mutex;
- std::lock_guard<StaticSpinLock> lock(mutex);
+ static StaticLock mutex;
+ std::lock_guard<StaticLock> lock(mutex);
*size = 0;
Modified: trunk/Source/_javascript_Core/heap/SlotVisitorInlines.h (188099 => 188100)
--- trunk/Source/_javascript_Core/heap/SlotVisitorInlines.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/heap/SlotVisitorInlines.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -250,7 +250,7 @@
ASSERT(heap()->m_storageSpace.contains(block));
- SpinLockHolder locker(&block->workListLock());
+ LockHolder locker(&block->workListLock());
if (heap()->operationInProgress() == FullCollection || block->shouldReportLiveBytes(locker, owner)) {
m_bytesCopied += bytes;
block->reportLiveBytes(locker, owner, token, bytes);
Modified: trunk/Source/_javascript_Core/parser/SourceProvider.cpp (188099 => 188100)
--- trunk/Source/_javascript_Core/parser/SourceProvider.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/parser/SourceProvider.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -27,7 +27,7 @@
#include "SourceProvider.h"
#include "JSCInlines.h"
-#include <wtf/SpinLock.h>
+#include <wtf/Lock.h>
#include <wtf/StdLibExtras.h>
namespace JSC {
@@ -44,11 +44,11 @@
{
}
-static StaticSpinLock providerIdLock;
+static StaticLock providerIdLock;
void SourceProvider::getID()
{
- SpinLockHolder lock(&providerIdLock);
+ LockHolder lock(&providerIdLock);
if (!m_id) {
static intptr_t nextProviderID = 0;
m_id = ++nextProviderID;
Modified: trunk/Source/_javascript_Core/profiler/ProfilerDatabase.cpp (188099 => 188100)
--- trunk/Source/_javascript_Core/profiler/ProfilerDatabase.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/profiler/ProfilerDatabase.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -35,7 +35,7 @@
static std::atomic<int> databaseCounter;
-static StaticSpinLock registrationLock;
+static StaticLock registrationLock;
static std::atomic<int> didRegisterAtExit;
static Database* firstDatabase;
@@ -138,14 +138,14 @@
if (++didRegisterAtExit == 1)
atexit(atExitCallback);
- SpinLockHolder holder(registrationLock);
+ LockHolder holder(registrationLock);
m_nextRegisteredDatabase = firstDatabase;
firstDatabase = this;
}
void Database::removeDatabaseFromAtExit()
{
- SpinLockHolder holder(registrationLock);
+ LockHolder holder(registrationLock);
for (Database** current = &firstDatabase; *current; current = &(*current)->m_nextRegisteredDatabase) {
if (*current != this)
continue;
@@ -163,7 +163,7 @@
Database* Database::removeFirstAtExitDatabase()
{
- SpinLockHolder holder(registrationLock);
+ LockHolder holder(registrationLock);
Database* result = firstDatabase;
if (result) {
firstDatabase = result->m_nextRegisteredDatabase;
Modified: trunk/Source/_javascript_Core/runtime/TypeProfilerLog.h (188099 => 188100)
--- trunk/Source/_javascript_Core/runtime/TypeProfilerLog.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/_javascript_Core/runtime/TypeProfilerLog.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -32,7 +32,6 @@
#include "JSCJSValue.h"
#include "Structure.h"
#include "TypeProfiler.h"
-#include <wtf/ByteSpinLock.h>
namespace JSC {
Modified: trunk/Source/WTF/ChangeLog (188099 => 188100)
--- trunk/Source/WTF/ChangeLog 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/ChangeLog 2015-08-07 00:49:54 UTC (rev 188100)
@@ -1,5 +1,99 @@
2015-08-05 Filip Pizlo <fpi...@apple.com>
+ Lightweight locks should be adaptive
+ https://bugs.webkit.org/show_bug.cgi?id=147545
+
+ Reviewed by Geoffrey Garen.
+
+ A common idiom in WebKit is to use spinlocks. We use them because the lock acquisition
+ overhead is lower than system locks and because they take dramatically less space than system
+ locks. The speed and space advantages of spinlocks can be astonishing: an uncontended spinlock
+ acquire is up to 10x faster and under microcontention - short critical section with two or
+ more threads taking turns - spinlocks are up to 100x faster. Spinlocks take only 1 byte or 4
+ bytes depending on the flavor, while system locks take 64 bytes or more. Clearly, WebKit
+ should continue to avoid system locks - they are just far too slow and far too big.
+
+ But there is a problem with this idiom. System lock implementations will sleep a thread when
+ it attempts to acquire a lock that is held, while spinlocks will cause the thread to burn CPU.
+ In WebKit spinlocks, the thread will repeatedly call sched_yield(). This is awesome for
+ microcontention, but awful when the lock will not be released for a while. In fact, when
+ critical sections take tens of microseconds or more, the CPU time cost of our spinlocks is
+ almost 100x more than the CPU time cost of a system lock. This case doesn't arise too
+ frequently in our current uses of spinlocks, but that's probably because right now there are
+ places where we make a conscious decision to use system locks - even though they use more
+ memory and are slower - because we don't want to waste CPU cycles when a thread has to wait a
+ while to acquire the lock.
+
+ The solution is to just implement a modern adaptive mutex in WTF. Luckily, this isn't a new
+ concept. This patch implements a mutex that is reminiscent of the kinds of low-overhead locks
+ that JVMs use. The actual implementation here is inspired by some of the ideas from [1]. The
+ idea is simple: the fast path is an inlined CAS to immediately acquire a lock that isn't held,
+ the slow path tries some number of spins to acquire the lock, and if that fails, the thread is
+ put on a queue and put to sleep. The queue is made up of statically allocated thread nodes and
+ the lock itself is a tagged pointer: either it is just bits telling us the complete lock state
+ (not held or held) or it is a pointer to the head of a queue of threads waiting to acquire the
+ lock. This approach gives WTF::Lock three different levels of adaptation: an inlined fast path
+ if the lock is not contended, a short burst of spinning for microcontention, and a full-blown
+ queue for critical sections that are held for a long time.
+
+ On a locking microbenchmark, this new Lock exhibits the following performance
+ characteristics:
+
+ - Lock+unlock on an uncontended no-op critical section: 2x slower than SpinLock and 3x faster
+ than a system mutex.
+
+ - Lock+unlock on a contended no-op critical section: 2x slower than SpinLock and 100x faster
+ than a system mutex.
+
+ - CPU time spent in lock() on a lock held for a while: same as system mutex, 90x less than a
+ SpinLock.
+
+ - Memory usage: sizeof(void*), so on 64-bit it's 8x less than a system mutex but 2x worse than
+ a SpinLock.
+
+ This patch replaces all uses of SpinLock with Lock, since our critical sections are not
+ no-ops so if you do basically anything in your critical section, the Lock overhead will be
+ invisible. Also, in all places where we used SpinLock, we could tolerate 8 bytes of overhead
+ instead of 4. Performance benchmarking using JSC macrobenchmarks shows no difference, which is
+ as it should be: the purpose of this change is to reduce CPU time wasted, not wallclock time.
+ This patch doesn't replace any uses of ByteSpinLock, since we expect that the space benefits
+ of having a lock that just uses a byte are still better than the CPU wastage benefits of
+ Lock. But, this work will enable some future work to create locks that will fit in just 1.6
+ bits: https://bugs.webkit.org/show_bug.cgi?id=147665.
+
+ [1] http://www.filpizlo.com/papers/pizlo-pppj2011-fable.pdf
+
+ * WTF.vcxproj/WTF.vcxproj:
+ * WTF.xcodeproj/project.pbxproj:
+ * benchmarks: Added.
+ * benchmarks/LockSpeedTest.cpp: Added.
+ (main):
+ * wtf/CMakeLists.txt:
+ * wtf/Lock.cpp: Added.
+ (WTF::LockBase::lockSlow):
+ (WTF::LockBase::unlockSlow):
+ * wtf/Lock.h: Added.
+ (WTF::LockBase::lock):
+ (WTF::LockBase::unlock):
+ (WTF::LockBase::isHeld):
+ (WTF::Lock::Lock):
+ * wtf/MetaAllocator.cpp:
+ (WTF::MetaAllocator::release):
+ (WTF::MetaAllocatorHandle::shrink):
+ (WTF::MetaAllocator::allocate):
+ (WTF::MetaAllocator::currentStatistics):
+ (WTF::MetaAllocator::addFreshFreeSpace):
+ (WTF::MetaAllocator::debugFreeSpaceSize):
+ * wtf/MetaAllocator.h:
+ * wtf/SpinLock.h:
+ * wtf/ThreadingPthreads.cpp:
+ * wtf/ThreadingWin.cpp:
+ * wtf/text/AtomicString.cpp:
+ * wtf/text/AtomicStringImpl.cpp:
+ (WTF::AtomicStringTableLocker::AtomicStringTableLocker):
+
+2015-08-05 Filip Pizlo <fpi...@apple.com>
+
Unreviewed, roll out http://trac.webkit.org/changeset/187972.
* wtf/Atomics.cpp:
Modified: trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj (188099 => 188100)
--- trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/WTF.vcxproj/WTF.vcxproj 2015-08-07 00:49:54 UTC (rev 188100)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugSuffix|Win32">
@@ -106,6 +106,7 @@
<ClCompile Include="..\wtf\glib\GThreadSafeMainLoopSource.cpp" />
<ClCompile Include="..\wtf\GregorianDateTime.cpp" />
<ClCompile Include="..\wtf\HashTable.cpp" />
+ <ClCompile Include="..\wtf\Lock.cpp" />
<ClCompile Include="..\wtf\MainThread.cpp" />
<ClCompile Include="..\wtf\MD5.cpp" />
<ClCompile Include="..\wtf\MediaTime.cpp" />
@@ -223,6 +224,7 @@
<ClInclude Include="..\wtf\IteratorAdaptors.h" />
<ClInclude Include="..\wtf\IteratorRange.h" />
<ClInclude Include="..\wtf\ListHashSet.h" />
+ <ClInclude Include="..\wtf\Lock.h" />
<ClInclude Include="..\wtf\Locker.h" />
<ClInclude Include="..\wtf\MainThread.h" />
<ClInclude Include="..\wtf\MallocPtr.h" />
Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (188099 => 188100)
--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj 2015-08-07 00:49:54 UTC (rev 188100)
@@ -40,6 +40,8 @@
0FD81AC5154FB22E00983E72 /* FastBitVector.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD81AC4154FB22E00983E72 /* FastBitVector.h */; settings = {ATTRIBUTES = (); }; };
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 */; };
+ 0FE1646B1B6FFC9600400E7C /* Lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE164691B6FFC9600400E7C /* Lock.h */; };
0FED67B61B22D4D80066CE15 /* TinyPtrSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FED67B51B22D4D80066CE15 /* TinyPtrSet.h */; };
14022F4118F5C3FC007FF0EB /* libbmalloc.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 14022F4018F5C3FC007FF0EB /* libbmalloc.a */; };
143F611F1565F0F900DB514A /* RAMSize.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 143F611D1565F0F900DB514A /* RAMSize.cpp */; };
@@ -321,6 +323,8 @@
0FD81AC4154FB22E00983E72 /* FastBitVector.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = FastBitVector.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>"; };
+ 0FE164691B6FFC9600400E7C /* Lock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Lock.h; sourceTree = "<group>"; };
0FEC3EE4171B834700FDAC8D /* ByteSpinLock.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ByteSpinLock.h; sourceTree = "<group>"; };
0FED67B51B22D4D80066CE15 /* TinyPtrSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TinyPtrSet.h; sourceTree = "<group>"; };
14022F4018F5C3FC007FF0EB /* libbmalloc.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libbmalloc.a; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -770,6 +774,8 @@
7CDD7FF9186D2A54007433CD /* IteratorRange.h */,
A70DA0831799F04D00529A9B /* ListDump.h */,
A8A472C1151A825A004123FF /* ListHashSet.h */,
+ 0FE164681B6FFC9600400E7C /* Lock.cpp */,
+ 0FE164691B6FFC9600400E7C /* Lock.h */,
A8A472C3151A825A004123FF /* Locker.h */,
1447AEC818FCE59400B3D7FF /* mbmalloc.cpp */,
A8A472CA151A825B004123FF /* MD5.cpp */,
@@ -1206,6 +1212,7 @@
A8A4746A151A825B004123FF /* UTF8.h in Headers */,
A8A473B9151A825B004123FF /* utils.h in Headers */,
A8A4747D151A825B004123FF /* ValueCheck.h in Headers */,
+ 0FE1646B1B6FFC9600400E7C /* Lock.h in Headers */,
A8A4747E151A825B004123FF /* Vector.h in Headers */,
A8A4747F151A825B004123FF /* VectorTraits.h in Headers */,
0FED67B61B22D4D80066CE15 /* TinyPtrSet.h in Headers */,
@@ -1337,6 +1344,7 @@
A8A4739E151A825B004123FF /* DataLog.cpp in Sources */,
A8A473A0151A825B004123FF /* DateMath.cpp in Sources */,
A8A473A2151A825B004123FF /* DecimalNumber.cpp in Sources */,
+ 0FE1646A1B6FFC9600400E7C /* Lock.cpp in Sources */,
A8A473AE151A825B004123FF /* diy-fp.cc in Sources */,
A8A473B0151A825B004123FF /* double-conversion.cc in Sources */,
A8A473BA151A825B004123FF /* dtoa.cpp in Sources */,
Added: trunk/Source/WTF/benchmarks/LockSpeedTest.cpp (0 => 188100)
--- trunk/Source/WTF/benchmarks/LockSpeedTest.cpp (rev 0)
+++ trunk/Source/WTF/benchmarks/LockSpeedTest.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -0,0 +1,137 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <wtf/CurrentTime.h>
+#include <wtf/Lock.h>
+#include <wtf/SpinLock.h>
+#include <wtf/StdLibExtras.h>
+#include <wtf/Threading.h>
+#include <wtf/ThreadingPrimitives.h>
+
+namespace {
+
+unsigned numThreadGroups;
+unsigned numThreadsPerGroup;
+unsigned workPerCriticalSection;
+unsigned numNoiseThreads;
+unsigned numIterations;
+
+void usage()
+{
+ printf("Usage: LockSpeedTest spinlock|lock|mutex|all <num thread groups> <num threads per group> <work per critical section> <num noise threads> <num iterations>\n");
+ exit(1);
+}
+
+template<typename LockType>
+void runBenchmark(const char* name)
+{
+ std::unique_ptr<LockType[]> locks = std::make_unique<LockType[]>(numThreadGroups);
+ std::unique_ptr<double[]> words = std::make_unique<double[]>(numThreadGroups);
+ std::unique_ptr<ThreadIdentifier[]> threads = std::make_unique<ThreadIdentifier[]>(numThreadGroups * numThreadsPerGroup);
+ std::unique_ptr<ThreadIdentifier[]> noiseThreads = std::make_unique<ThreadIdentifier[]>(numNoiseThreads);
+ std::unique_ptr<double[]> noiseCounts = std::make_unique<double[]>(numNoiseThreads);
+
+ volatile bool shouldStop = false;
+ for (unsigned threadIndex = numNoiseThreads; threadIndex--;) {
+ noiseCounts[threadIndex] = 0;
+ noiseThreads[threadIndex] = createThread(
+ "Noise Thread",
+ [&shouldStop, &noiseCounts, threadIndex] () {
+ while (!shouldStop)
+ noiseCounts[threadIndex]++;
+ });
+ }
+
+ double before = monotonicallyIncreasingTimeMS();
+
+ for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) {
+ words[threadGroupIndex] = 0;
+
+ for (unsigned threadIndex = numThreadsPerGroup; threadIndex--;) {
+ threads[threadGroupIndex * numThreadsPerGroup + threadIndex] = createThread(
+ "Benchmark thread",
+ [threadGroupIndex, &locks, &words] () {
+ for (unsigned i = numIterations; i--;) {
+ locks[threadGroupIndex].lock();
+ for (unsigned j = workPerCriticalSection; j--;) {
+ words[threadGroupIndex]++;
+ words[threadGroupIndex] *= 1.01;
+ }
+ locks[threadGroupIndex].unlock();
+ }
+ });
+ }
+ }
+
+ for (unsigned threadIndex = numThreadGroups * numThreadsPerGroup; threadIndex--;)
+ waitForThreadCompletion(threads[threadIndex]);
+ shouldStop = true;
+ double noiseCount = 0;
+ for (unsigned threadIndex = numNoiseThreads; threadIndex--;) {
+ waitForThreadCompletion(noiseThreads[threadIndex]);
+ noiseCount += noiseCounts[threadIndex];
+ }
+
+ double after = monotonicallyIncreasingTimeMS();
+
+ printf("%s: %.3lf ms, %.0lf noise.\n", name, after - before, noiseCount);
+}
+
+} // anonymous namespace
+
+int main(int argc, char** argv)
+{
+ WTF::initializeThreading();
+
+ if (argc != 7
+ || sscanf(argv[2], "%u", &numThreadGroups) != 1
+ || sscanf(argv[3], "%u", &numThreadsPerGroup) != 1
+ || sscanf(argv[4], "%u", &workPerCriticalSection) != 1
+ || sscanf(argv[5], "%u", &numNoiseThreads) != 1
+ || sscanf(argv[6], "%u", &numIterations) != 1)
+ usage();
+
+ bool didRun = false;
+ if (!strcmp(argv[1], "spinlock") || !strcmp(argv[1], "all")) {
+ runBenchmark<SpinLock>("SpinLock");
+ didRun = true;
+ }
+ if (!strcmp(argv[1], "lock") || !strcmp(argv[1], "all")) {
+ runBenchmark<Lock>("WTF Lock");
+ didRun = true;
+ }
+ if (!strcmp(argv[1], "mutex") || !strcmp(argv[1], "all")) {
+ runBenchmark<Mutex>("Platform Mutex");
+ didRun = true;
+ }
+
+ if (!didRun)
+ usage();
+
+ return 0;
+}
Modified: trunk/Source/WTF/wtf/CMakeLists.txt (188099 => 188100)
--- trunk/Source/WTF/wtf/CMakeLists.txt 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/wtf/CMakeLists.txt 2015-08-07 00:49:54 UTC (rev 188100)
@@ -41,6 +41,7 @@
IteratorAdaptors.h
IteratorRange.h
ListHashSet.h
+ Lock.h
Locker.h
MD5.h
MainThread.h
@@ -156,6 +157,7 @@
FunctionDispatcher.cpp
GregorianDateTime.cpp
HashTable.cpp
+ Lock.cpp
MD5.cpp
MainThread.cpp
MediaTime.cpp
Added: trunk/Source/WTF/wtf/Lock.cpp (0 => 188100)
--- trunk/Source/WTF/wtf/Lock.cpp (rev 0)
+++ trunk/Source/WTF/wtf/Lock.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -0,0 +1,253 @@
+/*
+ * 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.
+ */
+
+#include "config.h"
+#include "Lock.h"
+
+#include "ThreadSpecific.h"
+#include "ThreadingPrimitives.h"
+#include <condition_variable>
+#include <mutex>
+#include <thread>
+
+namespace WTF {
+
+namespace {
+
+// This data structure serves three purposes:
+//
+// 1) A parking mechanism for threads that go to sleep. That involves just a system mutex and
+// condition variable.
+//
+// 2) A queue node for when a thread is on some Lock's queue.
+//
+// 3) The queue head. This is kind of funky. When a thread is the head of a queue, it also serves as
+// the basic queue bookkeeping data structure. When a thread is dequeued, the next thread in the
+// queue takes on the queue head duties.
+struct ThreadData {
+ // The parking mechanism.
+ bool shouldPark { false };
+ std::mutex parkingLock;
+ std::condition_variable parkingCondition;
+
+ // The queue node.
+ ThreadData* nextInQueue { nullptr };
+
+ // The queue itself.
+ ThreadData* queueTail { nullptr };
+};
+
+ThreadSpecific<ThreadData>* threadData;
+
+ThreadData* myThreadData()
+{
+ static std::once_flag initializeOnce;
+ std::call_once(
+ initializeOnce,
+ [] {
+ threadData = new ThreadSpecific<ThreadData>();
+ });
+
+ return *threadData;
+}
+
+} // anonymous namespace
+
+void LockBase::lockSlow()
+{
+ unsigned spinCount = 0;
+
+ // This magic number turns out to be optimal based on past JikesRVM experiments.
+ const unsigned spinLimit = 40;
+
+ for (;;) {
+ uintptr_t currentWordValue = m_word.load();
+
+ if (!(currentWordValue & isLockedBit)) {
+ // It's not possible for someone to hold the queue lock while the lock itself is no longer
+ // held, since we will only attempt to acquire the queue lock when the lock is held and
+ // the queue lock prevents unlock.
+ ASSERT(!(currentWordValue & isQueueLockedBit));
+ if (m_word.compareExchangeWeak(currentWordValue, currentWordValue | isLockedBit)) {
+ // Success! We acquired the lock.
+ return;
+ }
+ }
+
+ // If there is no queue and we haven't spun too much, we can just try to spin around again.
+ if (!(currentWordValue & ~queueHeadMask) && spinCount < spinLimit) {
+ spinCount++;
+ std::this_thread::yield();
+ continue;
+ }
+
+ // Need to put ourselves on the queue. Create the queue if one does not exist. This requries
+ // owning the queue for a little bit. The lock that controls the queue is itself a spinlock.
+ // But before we acquire the queue spinlock, we make sure that we have a ThreadData for this
+ // thread.
+ ThreadData* me = myThreadData();
+ ASSERT(!me->shouldPark);
+ ASSERT(!me->nextInQueue);
+ ASSERT(!me->queueTail);
+
+ // Reload the current word value, since some time may have passed.
+ currentWordValue = m_word.load();
+
+ // We proceed only if the queue lock is not held, the Lock is held, and we succeed in
+ // acquiring the queue lock.
+ if ((currentWordValue & isQueueLockedBit)
+ || !(currentWordValue & isLockedBit)
+ || !m_word.compareExchangeWeak(currentWordValue, currentWordValue | isQueueLockedBit)) {
+ std::this_thread::yield();
+ continue;
+ }
+
+ me->shouldPark = true;
+
+ // We own the queue. Nobody can enqueue or dequeue until we're done. Also, it's not possible
+ // to release the Lock while we hold the queue lock.
+ ThreadData* queueHead = bitwise_cast<ThreadData*>(currentWordValue & ~queueHeadMask);
+ if (queueHead) {
+ // Put this thread at the end of the queue.
+ queueHead->queueTail->nextInQueue = me;
+ queueHead->queueTail = me;
+
+ // Release the queue lock.
+ currentWordValue = m_word.load();
+ ASSERT(currentWordValue & ~queueHeadMask);
+ ASSERT(currentWordValue & isQueueLockedBit);
+ ASSERT(currentWordValue & isLockedBit);
+ m_word.store(currentWordValue & ~isQueueLockedBit);
+ } else {
+ // Make this thread be the queue-head.
+ queueHead = me;
+ me->queueTail = me;
+
+ // Release the queue lock and install ourselves as the head. No need for a CAS loop, since
+ // we own the queue lock.
+ currentWordValue = m_word.load();
+ ASSERT(~(currentWordValue & ~queueHeadMask));
+ ASSERT(currentWordValue & isQueueLockedBit);
+ ASSERT(currentWordValue & isLockedBit);
+ uintptr_t newWordValue = currentWordValue;
+ newWordValue |= bitwise_cast<uintptr_t>(queueHead);
+ newWordValue &= ~isQueueLockedBit;
+ m_word.store(newWordValue);
+ }
+
+ // At this point everyone who acquires the queue lock will see me on the queue, and anyone who
+ // acquires me's lock will see that me wants to park. Note that shouldPark may have been
+ // cleared as soon as the queue lock was released above, but it will happen while the
+ // releasing thread holds me's parkingLock.
+
+ {
+ std::unique_lock<std::mutex> locker(me->parkingLock);
+ while (me->shouldPark)
+ me->parkingCondition.wait(locker);
+ }
+
+ ASSERT(!me->shouldPark);
+ ASSERT(!me->nextInQueue);
+ ASSERT(!me->queueTail);
+
+ // Now we can loop around and try to acquire the lock again.
+ }
+}
+
+void LockBase::unlockSlow()
+{
+ // If the fast path failed, it can only be because someone put a thread on the queue or the queue
+ // lock is held. If the queue lock is held, it can only be because someone *will* enqueue a thread
+ // onto the queue.
+
+ // Acquire the queue lock.
+ for (;;) {
+ uintptr_t currentWordValue = m_word.load();
+
+ ASSERT(currentWordValue & isLockedBit);
+
+ if (currentWordValue & isQueueLockedBit) {
+ std::this_thread::yield();
+ continue;
+ }
+
+ // If the queue lock is not held, then there must be an entry on the queue.
+ ASSERT(currentWordValue & ~queueHeadMask);
+
+ if (m_word.compareExchangeWeak(currentWordValue, currentWordValue | isQueueLockedBit))
+ break;
+ }
+
+ uintptr_t currentWordValue = m_word.load();
+
+ // After we acquire the queue lock, the Lock must still be held and the queue must be
+ // non-empty. The queue must be non-empty since only the lockSlow() method could have held the
+ // queue lock and if it did then it only releases it after putting something on the queue.
+ ASSERT(currentWordValue & isLockedBit);
+ ASSERT(currentWordValue & isQueueLockedBit);
+ ThreadData* queueHead = bitwise_cast<ThreadData*>(currentWordValue & ~queueHeadMask);
+ ASSERT(queueHead);
+
+ ThreadData* newQueueHead = queueHead->nextInQueue;
+ // Either this was the only thread on the queue, in which case we delete the queue, or there
+ // are still more threads on the queue, in which case we create a new queue head.
+ if (newQueueHead)
+ newQueueHead->queueTail = queueHead->queueTail;
+
+ // Change the queue head, possibly removing it if newQueueHead is null. No need for a CAS loop,
+ // since we hold the queue lock and the lock itself so nothing about the lock can change right
+ // now.
+ currentWordValue = m_word.load();
+ ASSERT(currentWordValue & isLockedBit);
+ ASSERT(currentWordValue & isQueueLockedBit);
+ ASSERT((currentWordValue & ~queueHeadMask) == bitwise_cast<uintptr_t>(queueHead));
+ uintptr_t newWordValue = currentWordValue;
+ newWordValue &= ~isLockedBit; // Release the Lock.
+ newWordValue &= ~isQueueLockedBit; // Release the queue lock.
+ newWordValue &= queueHeadMask; // Clear out the old queue head.
+ newWordValue |= bitwise_cast<uintptr_t>(newQueueHead); // Install new queue head.
+ m_word.store(newWordValue);
+
+ // Now the lock is available for acquisition. But we just have to wake up the old queue head.
+ // After that, we're done!
+
+ queueHead->nextInQueue = nullptr;
+ queueHead->queueTail = nullptr;
+
+ // We do this carefully because this may run either before or during the parkingLock critical
+ // section in lockSlow().
+ {
+ std::unique_lock<std::mutex> locker(queueHead->parkingLock);
+ queueHead->shouldPark = false;
+ }
+ // Doesn't matter if we notify_all() or notify_one() here since the only thread that could be
+ // waiting is queueHead.
+ queueHead->parkingCondition.notify_one();
+
+ // The old queue head can now contend for the lock again. We're done!
+}
+
+} // namespace WTF
+
Added: trunk/Source/WTF/wtf/Lock.h (0 => 188100)
--- trunk/Source/WTF/wtf/Lock.h (rev 0)
+++ trunk/Source/WTF/wtf/Lock.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -0,0 +1,168 @@
+/*
+ * 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_Lock_h
+#define WTF_Lock_h
+
+#include <wtf/Atomics.h>
+#include <wtf/Compiler.h>
+#include <wtf/Locker.h>
+#include <wtf/Noncopyable.h>
+
+namespace WTF {
+
+// A Lock is a fully adaptive mutex that gives you the best of SpinLock and Mutex. For small critical
+// sections (that take nanoseconds), it will usually perform within 2x of a SpinLock in both the
+// contended and uncontended case. When using a Mutex, such critical sections take up to 100x longer
+// than Lock in the contended case, or 3x longer than Lock in the uncontended case. For longer
+// critical sections (that take tens of microseconds), it will perform as well as a Mutex and slightly
+// better than a SpinLock. But, crucially, a SpinLock will burn up to 90x more time in the kernel for
+// such critical sections than either Lock or Mutex. Hence, using Lock will make the common case of
+// locking perform close to SpinLock for any critical section that does more than a few nanoseconds of
+// work while being as kind to the scheduler for longer critical sections as a Mutex.
+//
+// Like SpinLock, Lock takes very little memory - just sizeof(void*), though see a detailed caveat
+// below.
+//
+// Generally, you should use Lock instead of SpinLock because while it penalizes you slightly, you
+// make up for it by not wasting CPU cycles in case of contention.
+//
+// The Lock has the following nice properties:
+//
+// - Uncontended fast paths for lock acquisition and lock release that are almost as fast as the
+// uncontended fast paths of a spinlock. The only overhead is that the spinlock will not CAS on
+// release, while Lock will CAS. This overhead *can* slow things down for extremely small critical
+// sections that do little or nothing - it makes them 2x slower since in that case, CAS is the most
+// expensive instruction and having two of them is twice as bad as just having one. However, this
+// lock implementation is still almost 3x faster than a platform mutex in those cases. It's unlikely
+// that you'll encounter no-op critical sections, so usually, this lock is better than a spinlock.
+//
+// - Contended fast path that attempts to spin and yield for some number of times. For critical
+// sections that are held only briefly, this allows Lock to perform almost as well as a SpinLock.
+// SpinLock can still be almost 2x faster than Lock if the critical section is a no-op, but this
+// advantage diminishes as the critical section grows.
+//
+// - Contended slow path that enqueues the contending thread and causes it to wait on a condition
+// variable until the lock is released. This is the only case in which system mutexes and condition
+// variables are used. This case is rare and self-limiting: it will only happen when a lock is held
+// for long enough that spinning some number of times doesn't acquire it. The fact that Lock does
+// this as a fallback when spinning for some number of times fails means that it will burn
+// dramatically fewer CPU cycles - for example with 10 threads on an 8 logical CPU machine acquiring
+// a critical section that takes 50 microseconds, the WTF SpinLock will cause 90x more time to be
+// spent in the kernel than Lock.
+//
+// - Very low memory usage. Each Lock requires only sizeof(void*) memory. When the contended slow
+// path is activated, Lock only relies on each thread having a preallocated thread-specific data
+// structure called ThreadData that, together with the Lock itself, is used to build up a thread
+// queue. So, the total memory usage of all Locks is still bounded by:
+//
+// numberOfLocks * sizeof(void*) + numberOfThreads * sizeof(ThreadData)
+//
+// Where ThreadData is a decently large data structure, but we will only ever have one per thread,
+// regardless of the number of Locks in memory. Another way to view this is that the worst case
+// memory usage per Lock is:
+//
+// sizeof(void*) + numberOfThreads / numberOfLocks * sizeof(ThreadData)
+//
+// So, unless you have a small number of Locks (or, a large number of threads, which is far less
+// likely), the memory usage per-Lock is still going to be somewhere around sizeof(void*).
+//
+// - Barging fast paths. The Lock is tuned for maximum throughput rather than maximum fairness. If
+// a thread releases a Lock that was contended and had a queue of waiting threads, then it will
+// wake up the head of the queue, but it will also mark the lock as being available. This means that
+// some other thread that is just now attempting to acquire the lock may get it before the thread
+// that got woken up. When a thread barges into the lock, the thread that got woken up will simply
+// go back to the end of the queue. The barging behavior ends up being probabilistic on most
+// platforms and even though it may be unfair to some thread at some moment in time, it will rarely
+// have a long streak of unfairness towards any particular thread: eventually each thread waiting on
+// the lock will get to have a turn so long as no thread just holds the lock forever. That said,
+// there *is* a chance of pathologies - users of Lock should not depend on first-in, first-out lock
+// acquisition order under contention. The same caveat is generally true of SpinLock and platform
+// mutexes on some platforms.
+
+// This is a struct without a constructor or destructor so that it can be statically initialized.
+// Use Lock in instance variables.
+struct LockBase {
+ void lock()
+ {
+ if (LIKELY(m_word.compareExchangeWeak(0, isLockedBit, std::memory_order_acquire))) {
+ // Lock acquired!
+ return;
+ }
+
+ lockSlow();
+ }
+
+ void unlock()
+ {
+ if (LIKELY(m_word.compareExchangeWeak(isLockedBit, 0, std::memory_order_release))) {
+ // Lock released, and nobody was waiting!
+ return;
+ }
+
+ unlockSlow();
+ }
+
+ bool isHeld() const
+ {
+ return m_word.load(std::memory_order_acquire) & isLockedBit;
+ }
+
+ bool isLocked() const
+ {
+ return isHeld();
+ }
+
+protected:
+ static const uintptr_t isLockedBit = 1;
+ static const uintptr_t isQueueLockedBit = 2;
+ static const uintptr_t queueHeadMask = 3;
+
+ WTF_EXPORT_PRIVATE void lockSlow();
+ WTF_EXPORT_PRIVATE void unlockSlow();
+
+ Atomic<uintptr_t> m_word;
+};
+
+class Lock : public LockBase {
+ WTF_MAKE_NONCOPYABLE(Lock);
+public:
+ Lock()
+ {
+ m_word.store(0, std::memory_order_relaxed);
+ }
+};
+
+typedef LockBase StaticLock;
+typedef Locker<LockBase> LockHolder;
+
+} // namespace WTF
+
+using WTF::StaticLock;
+using WTF::Lock;
+using WTF::LockHolder;
+
+#endif // WTF_Lock_h
+
Modified: trunk/Source/WTF/wtf/MetaAllocator.cpp (188099 => 188100)
--- trunk/Source/WTF/wtf/MetaAllocator.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/wtf/MetaAllocator.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -60,7 +60,7 @@
ALWAYS_INLINE void MetaAllocator::release(MetaAllocatorHandle* handle)
{
- SpinLockHolder locker(&m_lock);
+ LockHolder locker(&m_lock);
if (handle->sizeInBytes()) {
decrementPageOccupancy(handle->start(), handle->sizeInBytes());
addFreeSpaceFromReleasedHandle(handle->start(), handle->sizeInBytes());
@@ -91,7 +91,7 @@
{
ASSERT(newSizeInBytes <= m_sizeInBytes);
- SpinLockHolder locker(&m_allocator->m_lock);
+ LockHolder locker(&m_allocator->m_lock);
newSizeInBytes = m_allocator->roundUp(newSizeInBytes);
@@ -150,7 +150,7 @@
PassRefPtr<MetaAllocatorHandle> MetaAllocator::allocate(size_t sizeInBytes, void* ownerUID)
{
- SpinLockHolder locker(&m_lock);
+ LockHolder locker(&m_lock);
if (!sizeInBytes)
return 0;
@@ -196,7 +196,7 @@
MetaAllocator::Statistics MetaAllocator::currentStatistics()
{
- SpinLockHolder locker(&m_lock);
+ LockHolder locker(&m_lock);
Statistics result;
result.bytesAllocated = m_bytesAllocated;
result.bytesReserved = m_bytesReserved;
@@ -281,7 +281,7 @@
void MetaAllocator::addFreshFreeSpace(void* start, size_t sizeInBytes)
{
- SpinLockHolder locker(&m_lock);
+ LockHolder locker(&m_lock);
m_bytesReserved += sizeInBytes;
addFreeSpace(start, sizeInBytes);
}
@@ -289,7 +289,7 @@
size_t MetaAllocator::debugFreeSpaceSize()
{
#ifndef NDEBUG
- SpinLockHolder locker(&m_lock);
+ LockHolder locker(&m_lock);
size_t result = 0;
for (FreeSpaceNode* node = m_freeSpaceSizeMap.first(); node; node = node->successor())
result += node->m_sizeInBytes;
Modified: trunk/Source/WTF/wtf/MetaAllocator.h (188099 => 188100)
--- trunk/Source/WTF/wtf/MetaAllocator.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/wtf/MetaAllocator.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -31,13 +31,13 @@
#include <wtf/Assertions.h>
#include <wtf/HashMap.h>
+#include <wtf/Lock.h>
#include <wtf/MetaAllocatorHandle.h>
#include <wtf/Noncopyable.h>
#include <wtf/PageBlock.h>
#include <wtf/RedBlackTree.h>
#include <wtf/RefCounted.h>
#include <wtf/RefPtr.h>
-#include <wtf/SpinLock.h>
namespace WTF {
@@ -183,7 +183,7 @@
size_t m_bytesReserved;
size_t m_bytesCommitted;
- SpinLock m_lock;
+ Lock m_lock;
MetaAllocatorTracker* m_tracker;
Modified: trunk/Source/WTF/wtf/SpinLock.h (188099 => 188100)
--- trunk/Source/WTF/wtf/SpinLock.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/wtf/SpinLock.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -32,6 +32,16 @@
namespace WTF {
+// SpinLock is a very simple lock implementation that has extremely fast lock/unlock for very small
+// uncontended critical sections. However, it will exhibit bad performance degradation when the lock
+// becomes contended: the thread trying to acquire the lock will simply waste CPU cycles.
+//
+// For most (all?) locking use cases, it's better to use Lock (see wtf/Lock.h). That uses only a bit
+// more memory (8 bytes instead of 4 on 64-bit), and is only a bit slower in the uncontended case
+// (Lock needs CAS to unlock, while SpinLock doesn't), but will burn a lot less CPU time - for 10
+// threads acquiring a 50 microsecond critical section, Lock will use up to 100x less kernel CPU time
+// than SpinLock.
+
// SpinLockBase is a struct without an explicitly defined constructors so that
// it can be initialized at compile time. See StaticSpinLock below.
struct SpinLockBase {
Modified: trunk/Source/WTF/wtf/ThreadingPthreads.cpp (188099 => 188100)
--- trunk/Source/WTF/wtf/ThreadingPthreads.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/wtf/ThreadingPthreads.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2009 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2009, 2015 Apple Inc. All rights reserved.
* Copyright (C) 2007 Justin Haygood (jhayg...@reaktix.com)
* Copyright (C) 2011 Research In Motion Limited. All rights reserved.
*
Modified: trunk/Source/WTF/wtf/ThreadingWin.cpp (188099 => 188100)
--- trunk/Source/WTF/wtf/ThreadingWin.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/wtf/ThreadingWin.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2007, 2008 Apple Inc. All rights reserved.
+ * Copyright (C) 2007, 2008, 2015 Apple Inc. All rights reserved.
* Copyright (C) 2009 Google Inc. All rights reserved.
* Copyright (C) 2009 Torch Mobile, Inc. All rights reserved.
*
Modified: trunk/Source/WTF/wtf/text/AtomicString.cpp (188099 => 188100)
--- trunk/Source/WTF/wtf/text/AtomicString.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/wtf/text/AtomicString.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -27,7 +27,7 @@
#include "dtoa.h"
#if USE(WEB_THREAD)
-#include "SpinLock.h"
+#include "Lock.h"
#endif
namespace WTF {
Modified: trunk/Source/WTF/wtf/text/AtomicStringImpl.cpp (188099 => 188100)
--- trunk/Source/WTF/wtf/text/AtomicStringImpl.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WTF/wtf/text/AtomicStringImpl.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -33,7 +33,7 @@
#include <wtf/unicode/UTF8.h>
#if USE(WEB_THREAD)
-#include "SpinLock.h"
+#include "Lock.h"
#endif
namespace WTF {
@@ -42,18 +42,18 @@
#if USE(WEB_THREAD)
-class AtomicStringTableLocker : public SpinLockHolder {
+class AtomicStringTableLocker : public LockHolder {
WTF_MAKE_NONCOPYABLE(AtomicStringTableLocker);
- static StaticSpinLock s_stringTableLock;
+ static StaticLock s_stringTableLock;
public:
AtomicStringTableLocker()
- : SpinLockHolder(&s_stringTableLock)
+ : LockHolder(&s_stringTableLock)
{
}
};
-StaticSpinLock AtomicStringTableLocker::s_stringTableLock;
+StaticLock AtomicStringTableLocker::s_stringTableLock;
#else
Modified: trunk/Source/WebCore/ChangeLog (188099 => 188100)
--- trunk/Source/WebCore/ChangeLog 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebCore/ChangeLog 2015-08-07 00:49:54 UTC (rev 188100)
@@ -1,3 +1,21 @@
+2015-08-05 Filip Pizlo <fpi...@apple.com>
+
+ Lightweight locks should be adaptive
+ https://bugs.webkit.org/show_bug.cgi?id=147545
+
+ Reviewed by Geoffrey Garen.
+
+ No new tests because no new behavior.
+
+ * bindings/objc/WebScriptObject.mm:
+ (WebCore::getJSWrapper):
+ (WebCore::addJSWrapper):
+ (WebCore::removeJSWrapper):
+ (WebCore::removeJSWrapperIfRetainCountOne):
+ * platform/ios/wak/WAKWindow.mm:
+ (-[WAKWindow setExposedScrollViewRect:]):
+ (-[WAKWindow exposedScrollViewRect]):
+
2015-08-06 Alex Christensen <achristen...@webkit.org>
[Win] Enable all Windows features in CMake
Modified: trunk/Source/WebCore/bindings/objc/WebScriptObject.mm (188099 => 188100)
--- trunk/Source/WebCore/bindings/objc/WebScriptObject.mm 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebCore/bindings/objc/WebScriptObject.mm 2015-08-07 00:49:54 UTC (rev 188100)
@@ -48,7 +48,7 @@
#import <runtime/JSLock.h>
#import <runtime/Completion.h>
#import <runtime/Completion.h>
-#import <wtf/SpinLock.h>
+#import <wtf/Lock.h>
#import <wtf/Threading.h>
#import <wtf/spi/cocoa/NSMapTableSPI.h>
#import <wtf/text/WTFString.h>
@@ -72,12 +72,12 @@
namespace WebCore {
static NSMapTable* JSWrapperCache;
-static StaticSpinLock spinLock;
+static StaticLock spinLock;
NSObject* getJSWrapper(JSObject* impl)
{
ASSERT(isMainThread());
- SpinLockHolder holder(&spinLock);
+ LockHolder holder(&spinLock);
if (!JSWrapperCache)
return nil;
@@ -88,7 +88,7 @@
void addJSWrapper(NSObject* wrapper, JSObject* impl)
{
ASSERT(isMainThread());
- SpinLockHolder holder(&spinLock);
+ LockHolder holder(&spinLock);
if (!JSWrapperCache)
JSWrapperCache = createWrapperCache();
@@ -97,7 +97,7 @@
void removeJSWrapper(JSObject* impl)
{
- SpinLockHolder holder(&spinLock);
+ LockHolder holder(&spinLock);
if (!JSWrapperCache)
return;
@@ -106,7 +106,7 @@
static void removeJSWrapperIfRetainCountOne(NSObject* wrapper, JSObject* impl)
{
- SpinLockHolder holder(&spinLock);
+ LockHolder holder(&spinLock);
if (!JSWrapperCache)
return;
Modified: trunk/Source/WebCore/platform/audio/mac/CARingBuffer.cpp (188099 => 188100)
--- trunk/Source/WebCore/platform/audio/mac/CARingBuffer.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebCore/platform/audio/mac/CARingBuffer.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -200,7 +200,7 @@
void CARingBuffer::setCurrentFrameBounds(uint64_t startTime, uint64_t endTime)
{
- ByteSpinLocker locker(m_currentFrameBoundsLock);
+ LockHolder locker(m_currentFrameBoundsLock);
uint32_t nextPtr = m_timeBoundsQueuePtr + 1;
uint32_t index = nextPtr & kGeneralRingTimeBoundsQueueMask;
@@ -212,7 +212,7 @@
void CARingBuffer::getCurrentFrameBounds(uint64_t &startTime, uint64_t &endTime)
{
- ByteSpinLocker locker(m_currentFrameBoundsLock);
+ LockHolder locker(m_currentFrameBoundsLock);
uint32_t curPtr = m_timeBoundsQueuePtr;
uint32_t index = curPtr & kGeneralRingTimeBoundsQueueMask;
CARingBuffer::TimeBounds& bounds = m_timeBoundsQueue[index];
Modified: trunk/Source/WebCore/platform/audio/mac/CARingBuffer.h (188099 => 188100)
--- trunk/Source/WebCore/platform/audio/mac/CARingBuffer.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebCore/platform/audio/mac/CARingBuffer.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -29,7 +29,7 @@
#if ENABLE(WEB_AUDIO) && USE(MEDIATOOLBOX)
#include <runtime/ArrayBuffer.h>
-#include <wtf/ByteSpinLock.h>
+#include <wtf/Lock.h>
#include <wtf/Vector.h>
typedef struct AudioBufferList AudioBufferList;
@@ -84,7 +84,7 @@
};
Vector<TimeBounds> m_timeBoundsQueue;
- ByteSpinLock m_currentFrameBoundsLock;
+ Lock m_currentFrameBoundsLock;
int32_t m_timeBoundsQueuePtr;
};
Modified: trunk/Source/WebCore/platform/ios/wak/WAKWindow.mm (188099 => 188100)
--- trunk/Source/WebCore/platform/ios/wak/WAKWindow.mm 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebCore/platform/ios/wak/WAKWindow.mm 2015-08-07 00:49:54 UTC (rev 188100)
@@ -36,7 +36,7 @@
#import "WKContentObservation.h"
#import "WKViewPrivate.h"
#import <QuartzCore/QuartzCore.h>
-#import <wtf/SpinLock.h>
+#import <wtf/Lock.h>
WEBCORE_EXPORT NSString * const WAKWindowScreenScaleDidChangeNotification = @"WAKWindowScreenScaleDidChangeNotification";
WEBCORE_EXPORT NSString * const WAKWindowVisibilityDidChangeNotification = @"WAKWindowVisibilityDidChangeNotification";
@@ -56,7 +56,7 @@
static id<OrientationProvider> gOrientationProvider;
@implementation WAKWindow {
- SpinLock _exposedScrollViewRectLock;
+ Lock _exposedScrollViewRectLock;
CGRect _exposedScrollViewRect;
}
@@ -358,14 +358,14 @@
- (void)setExposedScrollViewRect:(CGRect)exposedScrollViewRect
{
- SpinLockHolder locker(&_exposedScrollViewRectLock);
+ LockHolder locker(&_exposedScrollViewRectLock);
_exposedScrollViewRect = exposedScrollViewRect;
}
- (CGRect)exposedScrollViewRect
{
{
- SpinLockHolder locker(&_exposedScrollViewRectLock);
+ LockHolder locker(&_exposedScrollViewRectLock);
if (!CGRectIsNull(_exposedScrollViewRect))
return _exposedScrollViewRect;
}
Modified: trunk/Source/WebKit2/ChangeLog (188099 => 188100)
--- trunk/Source/WebKit2/ChangeLog 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebKit2/ChangeLog 2015-08-07 00:49:54 UTC (rev 188100)
@@ -1,3 +1,21 @@
+2015-08-05 Filip Pizlo <fpi...@apple.com>
+
+ Lightweight locks should be adaptive
+ https://bugs.webkit.org/show_bug.cgi?id=147545
+
+ Reviewed by Geoffrey Garen.
+
+ * WebProcess/WebPage/EventDispatcher.cpp:
+ (WebKit::EventDispatcher::clearQueuedTouchEventsForPage):
+ (WebKit::EventDispatcher::getQueuedTouchEventsForPage):
+ (WebKit::EventDispatcher::touchEvent):
+ (WebKit::EventDispatcher::dispatchTouchEvents):
+ * WebProcess/WebPage/EventDispatcher.h:
+ * WebProcess/WebPage/ViewUpdateDispatcher.cpp:
+ (WebKit::ViewUpdateDispatcher::visibleContentRectUpdate):
+ (WebKit::ViewUpdateDispatcher::dispatchVisibleContentRectUpdate):
+ * WebProcess/WebPage/ViewUpdateDispatcher.h:
+
2015-08-06 Matt Rajca <mra...@apple.com>
Media Session: notify focus manager clients when the playing state of the focused media element changes
Modified: trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.cpp (188099 => 188100)
--- trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -172,13 +172,13 @@
#if ENABLE(IOS_TOUCH_EVENTS)
void EventDispatcher::clearQueuedTouchEventsForPage(const WebPage& webPage)
{
- SpinLockHolder locker(&m_touchEventsLock);
+ LockHolder locker(&m_touchEventsLock);
m_touchEvents.remove(webPage.pageID());
}
void EventDispatcher::getQueuedTouchEventsForPage(const WebPage& webPage, TouchEventQueue& destinationQueue)
{
- SpinLockHolder locker(&m_touchEventsLock);
+ LockHolder locker(&m_touchEventsLock);
destinationQueue = m_touchEvents.take(webPage.pageID());
}
@@ -186,7 +186,7 @@
{
bool updateListWasEmpty;
{
- SpinLockHolder locker(&m_touchEventsLock);
+ LockHolder locker(&m_touchEventsLock);
updateListWasEmpty = m_touchEvents.isEmpty();
auto addResult = m_touchEvents.add(pageID, TouchEventQueue());
if (addResult.isNewEntry)
@@ -217,7 +217,7 @@
{
HashMap<uint64_t, TouchEventQueue> localCopy;
{
- SpinLockHolder locker(&m_touchEventsLock);
+ LockHolder locker(&m_touchEventsLock);
localCopy.swap(m_touchEvents);
}
Modified: trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.h (188099 => 188100)
--- trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebKit2/WebProcess/WebPage/EventDispatcher.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -32,9 +32,9 @@
#include <WebEvent.h>
#include <memory>
#include <wtf/HashMap.h>
+#include <wtf/Lock.h>
#include <wtf/Noncopyable.h>
#include <wtf/RefPtr.h>
-#include <wtf/SpinLock.h>
#include <wtf/ThreadingPrimitives.h>
namespace WebCore {
@@ -97,7 +97,7 @@
#endif
std::unique_ptr<WebCore::WheelEventDeltaTracker> m_recentWheelEventDeltaTracker;
#if ENABLE(IOS_TOUCH_EVENTS)
- SpinLock m_touchEventsLock;
+ Lock m_touchEventsLock;
HashMap<uint64_t, TouchEventQueue> m_touchEvents;
#endif
};
Modified: trunk/Source/WebKit2/WebProcess/WebPage/ViewUpdateDispatcher.cpp (188099 => 188100)
--- trunk/Source/WebKit2/WebProcess/WebPage/ViewUpdateDispatcher.cpp 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebKit2/WebProcess/WebPage/ViewUpdateDispatcher.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -58,7 +58,7 @@
{
bool updateListWasEmpty;
{
- SpinLockHolder locker(&m_dataMutex);
+ LockHolder locker(&m_dataMutex);
updateListWasEmpty = m_latestUpdate.isEmpty();
auto iterator = m_latestUpdate.find(pageID);
if (iterator == m_latestUpdate.end())
@@ -78,7 +78,7 @@
{
HashMap<uint64_t, UpdateData> update;
{
- SpinLockHolder locker(&m_dataMutex);
+ LockHolder locker(&m_dataMutex);
update = WTF::move(m_latestUpdate);
}
Modified: trunk/Source/WebKit2/WebProcess/WebPage/ViewUpdateDispatcher.h (188099 => 188100)
--- trunk/Source/WebKit2/WebProcess/WebPage/ViewUpdateDispatcher.h 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Source/WebKit2/WebProcess/WebPage/ViewUpdateDispatcher.h 2015-08-07 00:49:54 UTC (rev 188100)
@@ -30,8 +30,8 @@
#include "VisibleContentRectUpdateInfo.h"
#include <wtf/HashMap.h>
+#include <wtf/Lock.h>
#include <wtf/Ref.h>
-#include <wtf/SpinLock.h>
namespace WebKit {
@@ -57,7 +57,7 @@
};
Ref<WorkQueue> m_queue;
- SpinLock m_dataMutex;
+ Lock m_dataMutex;
HashMap<uint64_t, UpdateData> m_latestUpdate;
};
Modified: trunk/Tools/ChangeLog (188099 => 188100)
--- trunk/Tools/ChangeLog 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Tools/ChangeLog 2015-08-07 00:49:54 UTC (rev 188100)
@@ -1,3 +1,17 @@
+2015-08-06 Filip Pizlo <fpi...@apple.com>
+
+ Lightweight locks should be adaptive
+ https://bugs.webkit.org/show_bug.cgi?id=147545
+
+ Reviewed by Geoffrey Garen.
+
+ * TestWebKitAPI/CMakeLists.txt:
+ * TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj:
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/WTF/Lock.cpp: Added.
+ (TestWebKitAPI::runLockTest):
+ (TestWebKitAPI::TEST):
+
2015-08-06 Ryosuke Niwa <rn...@webkit.org>
http_server_driver and benchmark_builder should not be in run-benchmark's plan files
Modified: trunk/Tools/TestWebKitAPI/CMakeLists.txt (188099 => 188100)
--- trunk/Tools/TestWebKitAPI/CMakeLists.txt 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Tools/TestWebKitAPI/CMakeLists.txt 2015-08-07 00:49:54 UTC (rev 188100)
@@ -75,6 +75,7 @@
${TESTWEBKITAPI_DIR}/Tests/WTF/HashSet.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/IntegerToStringConversion.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/ListHashSet.cpp
+ ${TESTWEBKITAPI_DIR}/Tests/WTF/Lock.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/MD5.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/MathExtras.cpp
${TESTWEBKITAPI_DIR}/Tests/WTF/MediaTime.cpp
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj (188099 => 188100)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.vcxproj/TestWebKitAPI.vcxproj 2015-08-07 00:49:54 UTC (rev 188100)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="DebugSuffix|Win32">
@@ -319,6 +319,7 @@
<ClCompile Include="..\Tests\WTF\HashSet.cpp" />
<ClCompile Include="..\Tests\WTF\IntegerToStringConversion.cpp" />
<ClCompile Include="..\Tests\WTF\ListHashSet.cpp" />
+ <ClCompile Include="..\Tests\WTF\Lock.cpp" />
<ClCompile Include="..\Tests\WTF\MD5.cpp" />
<ClCompile Include="..\Tests\WTF\MathExtras.cpp" />
<ClCompile Include="..\Tests\WTF\MediaTime.cpp" />
@@ -365,4 +366,4 @@
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
-</Project>
\ No newline at end of file
+</Project>
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (188099 => 188100)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2015-08-07 00:21:45 UTC (rev 188099)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2015-08-07 00:49:54 UTC (rev 188100)
@@ -11,6 +11,7 @@
0F139E781A423A6B00F590F5 /* PlatformUtilitiesCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E721A423A2B00F590F5 /* PlatformUtilitiesCocoa.mm */; };
0F139E791A42457000F590F5 /* PlatformUtilitiesCocoa.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F139E721A423A2B00F590F5 /* PlatformUtilitiesCocoa.mm */; };
0F3B94A71A77267400DE3272 /* WKWebViewEvaluateJavaScript.mm in Sources */ = {isa = PBXBuildFile; fileRef = 0F3B94A51A77266C00DE3272 /* WKWebViewEvaluateJavaScript.mm */; };
+ 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 */; };
1A63479F183D72A4005B1707 /* all-content-in-one-iframe.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 93D3D19B17B1A7B000C7C415 /* all-content-in-one-iframe.html */; };
@@ -428,6 +429,7 @@
0F3B94A51A77266C00DE3272 /* WKWebViewEvaluateJavaScript.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = WKWebViewEvaluateJavaScript.mm; sourceTree = "<group>"; };
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>"; };
+ 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>"; };
1A02C84B125D4A5E00E3F4BD /* find.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = find.html; sourceTree = "<group>"; };
@@ -1096,6 +1098,7 @@
26B2DFF815BDE599004F691D /* HashSet.cpp */,
266FAFD215E5775200F61D5B /* IntegerToStringConversion.cpp */,
26300B1716755CD90066886D /* ListHashSet.cpp */,
+ 0FFC45A41B73EBE20085BD62 /* Lock.cpp */,
B4039F9C15E6D8B3007255D6 /* MathExtras.cpp */,
CD5393C71757BA9700C07123 /* MD5.cpp */,
CD5497B315857F0C00B5BC30 /* MediaTime.cpp */,
@@ -1567,6 +1570,7 @@
7CCE7EC91A411A7E00447C4C /* RenderedImageFromDOMNode.mm in Sources */,
7CCE7ECA1A411A7E00447C4C /* RenderedImageFromDOMRange.mm in Sources */,
51CD1C6C1B38CE4300142CA5 /* ModalAlerts.mm in Sources */,
+ 0FFC45A61B73EBEB0085BD62 /* Lock.cpp in Sources */,
7CCE7F0E1A411AE600447C4C /* ResizeReversePaginatedWebView.cpp in Sources */,
7CCE7F0F1A411AE600447C4C /* ResizeWindowAfterCrash.cpp in Sources */,
7CCE7F101A411AE600447C4C /* ResponsivenessTimerDoesntFireEarly.cpp in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp (0 => 188100)
--- trunk/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/WTF/Lock.cpp 2015-08-07 00:49:54 UTC (rev 188100)
@@ -0,0 +1,100 @@
+/*
+ * 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 <wtf/Lock.h>
+#include <wtf/Threading.h>
+#include <wtf/ThreadingPrimitives.h>
+
+using namespace WTF;
+
+namespace TestWebKitAPI {
+
+template<typename LockType>
+void runLockTest(unsigned numThreadGroups, unsigned numThreadsPerGroup, unsigned workPerCriticalSection, unsigned numIterations)
+{
+ std::unique_ptr<LockType[]> locks = std::make_unique<LockType[]>(numThreadGroups);
+ std::unique_ptr<double[]> words = std::make_unique<double[]>(numThreadGroups);
+ std::unique_ptr<ThreadIdentifier[]> threads = std::make_unique<ThreadIdentifier[]>(numThreadGroups * numThreadsPerGroup);
+
+ for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;) {
+ words[threadGroupIndex] = 0;
+
+ for (unsigned threadIndex = numThreadsPerGroup; threadIndex--;) {
+ threads[threadGroupIndex * numThreadsPerGroup + threadIndex] = createThread(
+ "Benchmark thread",
+ [threadGroupIndex, &locks, &words, numIterations, workPerCriticalSection] () {
+ for (unsigned i = numIterations; i--;) {
+ locks[threadGroupIndex].lock();
+ for (unsigned j = workPerCriticalSection; j--;)
+ words[threadGroupIndex]++;
+ locks[threadGroupIndex].unlock();
+ }
+ });
+ }
+ }
+
+ for (unsigned threadIndex = numThreadGroups * numThreadsPerGroup; threadIndex--;)
+ waitForThreadCompletion(threads[threadIndex]);
+
+ double expected = 0;
+ for (uint64_t i = static_cast<uint64_t>(numIterations) * workPerCriticalSection * numThreadsPerGroup; i--;)
+ expected++;
+
+ for (unsigned threadGroupIndex = numThreadGroups; threadGroupIndex--;)
+ EXPECT_EQ(expected, words[threadGroupIndex]);
+}
+
+TEST(WTF_Lock, UncontentedShortSection)
+{
+ runLockTest<Lock>(1, 1, 1, 10000000);
+}
+
+TEST(WTF_Lock, UncontentedLongSection)
+{
+ runLockTest<Lock>(1, 1, 10000, 1000);
+}
+
+TEST(WTF_Lock, ContentedShortSection)
+{
+ runLockTest<Lock>(1, 10, 1, 10000000);
+}
+
+TEST(WTF_Lock, ContentedLongSection)
+{
+ runLockTest<Lock>(1, 10, 10000, 10000);
+}
+
+TEST(WTF_Lock, ManyContentedShortSections)
+{
+ runLockTest<Lock>(10, 10, 1, 500000);
+}
+
+TEST(WTF_Lock, ManyContentedLongSections)
+{
+ runLockTest<Lock>(10, 10, 10000, 2000);
+}
+
+} // namespace TestWebKitAPI