Title: [209691] trunk/Source
Revision
209691
Author
[email protected]
Date
2016-12-11 17:19:47 -0800 (Sun, 11 Dec 2016)

Log Message

MarkedBlock::marksConveyLivenessDuringMarking should take into account collection scope
https://bugs.webkit.org/show_bug.cgi?id=165741

Reviewed by Saam Barati.
        
Source/_javascript_Core:

MarkedBlock::marksConveyLivenessDuringMarking thought that the off-by-one marking
version indicated liveness during any collection when it's just during full collection.
One of its users - MarkedBlock::sweep - knew this and had a special case, but the other
one - MarkedBlock::isLive - didn't. So, I moved the special case into
marksConveyLivenessDuringMarking.
        
Also, this cleans up some remaining bitvector races.
        
To find this bug, I significantly strengthened our assertions.

* CMakeLists.txt:
* _javascript_Core.xcodeproj/project.pbxproj:
* heap/CellContainer.cpp: Added.
(JSC::CellContainer::isNewlyAllocated):
* heap/CellContainer.h:
* heap/MarkedAllocator.cpp:
(JSC::MarkedAllocator::addBlock):
(JSC::MarkedAllocator::removeBlock):
(JSC::MarkedAllocator::dumpBits):
* heap/MarkedAllocator.h:
(JSC::MarkedAllocator::forEachBitVector):
(JSC::MarkedAllocator::forEachBitVectorWithName):
* heap/MarkedBlock.cpp:
(JSC::MarkedBlock::tryCreate):
(JSC::MarkedBlock::Handle::~Handle):
(JSC::MarkedBlock::MarkedBlock):
(JSC::MarkedBlock::Handle::specializedSweep):
(JSC::MarkedBlock::Handle::sweepHelperSelectMarksMode):
(JSC::MarkedBlock::Handle::stopAllocating):
(JSC::MarkedBlock::Handle::resumeAllocating):
(JSC::MarkedBlock::aboutToMarkSlow):
(JSC::MarkedBlock::Handle::didConsumeFreeList):
(JSC::MarkedBlock::Handle::dumpState):
* heap/MarkedBlock.h:
(JSC::MarkedBlock::markingVersion):
(JSC::MarkedBlock::isMarkedRaw):
(JSC::MarkedBlock::isMarked):
* heap/MarkedBlockInlines.h:
(JSC::MarkedBlock::marksConveyLivenessDuringMarking):
* heap/SlotVisitor.cpp:
(JSC::SlotVisitor::appendJSCellOrAuxiliary):
* runtime/Options.cpp:
(JSC::recomputeDependentOptions):
* runtime/StructureIDTable.h:
(JSC::StructureIDTable::size):
(JSC::StructureIDTable::get):

Source/WTF:

To find this bug, I needed to seriously beef up our logging infrastructure.
        
It's now the case that:
        
    dataLog(...);
        
will print its output atomically. This happens with some careful magic:
        
- dataFile() is now a LockedPrintStream that locks around print().
        
- The lock is a recursive lock via RecursiveLockAdapter<>, so if the dump methods end
  up calling back into dataLog() then it just works. This is important: say the dump()
  calls a getter that itself does logging, maybe because it's encountering badness and
  wants to report it before crashing).
        
- The lock is a WordLock so that ParkingLot and Lock can keep using dataLog() for
  debugging. We probably won't need to debug WordLock anytime soon - the algorithm is
  so simple.
        
- LockedPrintStream::print(...) causes the print callbacks of its arguments to run on
  the underlying PrintStream, so that you don't need to do recursive lock acquisition
  on each individual argument and whatever printing it does recursively.

* WTF.xcodeproj/project.pbxproj:
* wtf/CMakeLists.txt:
* wtf/DataLog.cpp:
(WTF::initializeLogFileOnce):
(WTF::initializeLogFile):
(WTF::dataFile):
* wtf/DataLog.h:
* wtf/LockedPrintStream.cpp: Added.
(WTF::LockedPrintStream::LockedPrintStream):
(WTF::LockedPrintStream::~LockedPrintStream):
(WTF::LockedPrintStream::vprintf):
(WTF::LockedPrintStream::flush):
(WTF::LockedPrintStream::begin):
(WTF::LockedPrintStream::end):
* wtf/LockedPrintStream.h: Added.
* wtf/PrintStream.cpp:
(WTF::PrintStream::printfVariableFormat):
(WTF::PrintStream::begin):
(WTF::PrintStream::end):
* wtf/PrintStream.h:
(WTF::PrintStream::atomically):
(WTF::PrintStream::print):
(WTF::PrintStream::println):
(WTF::PrintStream::printImpl):
(WTF::>::unpack):
(WTF::FormatImpl::FormatImpl):
(WTF::FormatImpl::dump):
(WTF::format):
(WTF::printInternal):
* wtf/RecursiveLockAdapter.h: Added.
(WTF::RecursiveLockAdapter::RecursiveLockAdapter):
(WTF::RecursiveLockAdapter::lock):
(WTF::RecursiveLockAdapter::unlock):
(WTF::RecursiveLockAdapter::tryLock):
(WTF::RecursiveLockAdapter::isLocked):
* wtf/WordLock.cpp:
* wtf/WordLock.h:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/CMakeLists.txt (209690 => 209691)


--- trunk/Source/_javascript_Core/CMakeLists.txt	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/CMakeLists.txt	2016-12-12 01:19:47 UTC (rev 209691)
@@ -464,6 +464,7 @@
     ftl/FTLValueRange.cpp
 
     heap/AllocatorAttributes.cpp
+    heap/CellContainer.cpp
     heap/CodeBlockSet.cpp
     heap/CollectionScope.cpp
     heap/ConservativeRoots.cpp

Modified: trunk/Source/_javascript_Core/ChangeLog (209690 => 209691)


--- trunk/Source/_javascript_Core/ChangeLog	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/ChangeLog	2016-12-12 01:19:47 UTC (rev 209691)
@@ -1,5 +1,59 @@
 2016-12-10  Filip Pizlo  <[email protected]>
 
+        MarkedBlock::marksConveyLivenessDuringMarking should take into account collection scope
+        https://bugs.webkit.org/show_bug.cgi?id=165741
+
+        Reviewed by Saam Barati.
+        
+        MarkedBlock::marksConveyLivenessDuringMarking thought that the off-by-one marking
+        version indicated liveness during any collection when it's just during full collection.
+        One of its users - MarkedBlock::sweep - knew this and had a special case, but the other
+        one - MarkedBlock::isLive - didn't. So, I moved the special case into
+        marksConveyLivenessDuringMarking.
+        
+        Also, this cleans up some remaining bitvector races.
+        
+        To find this bug, I significantly strengthened our assertions.
+
+        * CMakeLists.txt:
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * heap/CellContainer.cpp: Added.
+        (JSC::CellContainer::isNewlyAllocated):
+        * heap/CellContainer.h:
+        * heap/MarkedAllocator.cpp:
+        (JSC::MarkedAllocator::addBlock):
+        (JSC::MarkedAllocator::removeBlock):
+        (JSC::MarkedAllocator::dumpBits):
+        * heap/MarkedAllocator.h:
+        (JSC::MarkedAllocator::forEachBitVector):
+        (JSC::MarkedAllocator::forEachBitVectorWithName):
+        * heap/MarkedBlock.cpp:
+        (JSC::MarkedBlock::tryCreate):
+        (JSC::MarkedBlock::Handle::~Handle):
+        (JSC::MarkedBlock::MarkedBlock):
+        (JSC::MarkedBlock::Handle::specializedSweep):
+        (JSC::MarkedBlock::Handle::sweepHelperSelectMarksMode):
+        (JSC::MarkedBlock::Handle::stopAllocating):
+        (JSC::MarkedBlock::Handle::resumeAllocating):
+        (JSC::MarkedBlock::aboutToMarkSlow):
+        (JSC::MarkedBlock::Handle::didConsumeFreeList):
+        (JSC::MarkedBlock::Handle::dumpState):
+        * heap/MarkedBlock.h:
+        (JSC::MarkedBlock::markingVersion):
+        (JSC::MarkedBlock::isMarkedRaw):
+        (JSC::MarkedBlock::isMarked):
+        * heap/MarkedBlockInlines.h:
+        (JSC::MarkedBlock::marksConveyLivenessDuringMarking):
+        * heap/SlotVisitor.cpp:
+        (JSC::SlotVisitor::appendJSCellOrAuxiliary):
+        * runtime/Options.cpp:
+        (JSC::recomputeDependentOptions):
+        * runtime/StructureIDTable.h:
+        (JSC::StructureIDTable::size):
+        (JSC::StructureIDTable::get):
+
+2016-12-10  Filip Pizlo  <[email protected]>
+
         The DOM should have an advancing wavefront opaque root barrier
         https://bugs.webkit.org/show_bug.cgi?id=165712
 

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (209690 => 209691)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2016-12-12 01:19:47 UTC (rev 209691)
@@ -752,6 +752,7 @@
 		0FDB2CEA174896C7007B3C1B /* ConcurrentJSLock.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDB2CE9174896C7007B3C1B /* ConcurrentJSLock.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		0FDDBFB51666EED800C55FEF /* DFGVariableAccessDataDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FDDBFB21666EED500C55FEF /* DFGVariableAccessDataDump.cpp */; };
 		0FDDBFB61666EEDA00C55FEF /* DFGVariableAccessDataDump.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDDBFB31666EED500C55FEF /* DFGVariableAccessDataDump.h */; };
+		0FDE87F91DFD0C760064C390 /* CellContainer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FDE87F81DFD0C6D0064C390 /* CellContainer.cpp */; };
 		0FDF67D21D9C6D27001B9825 /* B3Kind.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDF67D11D9C6086001B9825 /* B3Kind.h */; };
 		0FDF67D31D9C6D2A001B9825 /* B3Kind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FDF67D01D9C6086001B9825 /* B3Kind.cpp */; };
 		0FDF67D61D9DC440001B9825 /* AirKind.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FDF67D41D9DC43E001B9825 /* AirKind.cpp */; };
@@ -3178,6 +3179,7 @@
 		0FDB2CE9174896C7007B3C1B /* ConcurrentJSLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConcurrentJSLock.h; sourceTree = "<group>"; };
 		0FDDBFB21666EED500C55FEF /* DFGVariableAccessDataDump.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGVariableAccessDataDump.cpp; path = dfg/DFGVariableAccessDataDump.cpp; sourceTree = "<group>"; };
 		0FDDBFB31666EED500C55FEF /* DFGVariableAccessDataDump.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGVariableAccessDataDump.h; path = dfg/DFGVariableAccessDataDump.h; sourceTree = "<group>"; };
+		0FDE87F81DFD0C6D0064C390 /* CellContainer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CellContainer.cpp; sourceTree = "<group>"; };
 		0FDF67D01D9C6086001B9825 /* B3Kind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = B3Kind.cpp; path = b3/B3Kind.cpp; sourceTree = "<group>"; };
 		0FDF67D11D9C6086001B9825 /* B3Kind.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = B3Kind.h; path = b3/B3Kind.h; sourceTree = "<group>"; };
 		0FDF67D41D9DC43E001B9825 /* AirKind.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = AirKind.cpp; path = b3/air/AirKind.cpp; sourceTree = "<group>"; };
@@ -5629,6 +5631,7 @@
 				0FA7620A1DB959F600B7A2FD /* AllocatingScope.h */,
 				0F9630351D4192C3005609D9 /* AllocatorAttributes.cpp */,
 				0F9630361D4192C3005609D9 /* AllocatorAttributes.h */,
+				0FDE87F81DFD0C6D0064C390 /* CellContainer.cpp */,
 				0F070A421D543A89006E7232 /* CellContainer.h */,
 				0F070A431D543A89006E7232 /* CellContainerInlines.h */,
 				0F1C3DD91BBCE09E00E523E4 /* CellState.h */,
@@ -10242,6 +10245,7 @@
 				0FB1058B1675483100F8AB6E /* ProfilerOSRExit.cpp in Sources */,
 				0FB1058D1675483700F8AB6E /* ProfilerOSRExitSite.cpp in Sources */,
 				0F61832B1C45BF070072450B /* AirCustom.cpp in Sources */,
+				0FDE87F91DFD0C760064C390 /* CellContainer.cpp in Sources */,
 				0F13912B16771C3A009CCB07 /* ProfilerProfiledBytecodes.cpp in Sources */,
 				0FD3E40D1B618B6600C80E1E /* PropertyCondition.cpp in Sources */,
 				E35CA1531DBC3A5C00F83516 /* DOMJITHeapRange.cpp in Sources */,

Added: trunk/Source/_javascript_Core/heap/CellContainer.cpp (0 => 209691)


--- trunk/Source/_javascript_Core/heap/CellContainer.cpp	                        (rev 0)
+++ trunk/Source/_javascript_Core/heap/CellContainer.cpp	2016-12-12 01:19:47 UTC (rev 209691)
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "CellContainer.h"
+
+#include "MarkedBlockInlines.h"
+
+namespace JSC {
+
+bool CellContainer::isNewlyAllocated(HeapCell* cell) const
+{
+    if (isLargeAllocation())
+        return largeAllocation().isNewlyAllocated();
+    MarkedBlock::Handle& handle = markedBlock().handle();
+    return !handle.isNewlyAllocatedStale()
+        && handle.isNewlyAllocated(cell);
+}
+
+} // namespace JSC
+

Modified: trunk/Source/_javascript_Core/heap/CellContainer.h (209690 => 209691)


--- trunk/Source/_javascript_Core/heap/CellContainer.h	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/heap/CellContainer.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -84,6 +84,8 @@
     bool isMarked(HeapCell*) const;
     bool isMarked(HeapVersion markingVersion, HeapCell*) const;
     
+    bool isNewlyAllocated(HeapCell*) const;
+    
     void noteMarked();
     void assertValidCell(VM&, HeapCell*) const;
     

Modified: trunk/Source/_javascript_Core/heap/MarkedAllocator.cpp (209690 => 209691)


--- trunk/Source/_javascript_Core/heap/MarkedAllocator.cpp	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/heap/MarkedAllocator.cpp	2016-12-12 01:19:47 UTC (rev 209691)
@@ -270,6 +270,7 @@
         m_blocks.append(block);
         if (m_blocks.capacity() != oldCapacity) {
             forEachBitVector(
+                NoLockingNecessary,
                 [&] (FastBitVector& vector) {
                     ASSERT_UNUSED(vector, vector.numBits() == oldCapacity);
                 });
@@ -278,6 +279,7 @@
             
             LockHolder locker(m_bitvectorLock);
             forEachBitVector(
+                locker,
                 [&] (FastBitVector& vector) {
                     vector.resize(m_blocks.capacity());
                 });
@@ -289,6 +291,7 @@
     }
     
     forEachBitVector(
+        NoLockingNecessary,
         [&] (FastBitVector& vector) {
             ASSERT_UNUSED(vector, !vector[index]);
         });
@@ -309,6 +312,7 @@
     m_freeBlockIndices.append(block->index());
     
     forEachBitVector(
+        holdLock(m_bitvectorLock),
         [&] (FastBitVector& vector) {
             vector[block->index()] = false;
         });
@@ -467,6 +471,7 @@
 {
     unsigned maxNameLength = 0;
     forEachBitVectorWithName(
+        NoLockingNecessary,
         [&] (FastBitVector&, const char* name) {
             unsigned length = strlen(name);
             maxNameLength = std::max(maxNameLength, length);
@@ -473,6 +478,7 @@
         });
     
     forEachBitVectorWithName(
+        NoLockingNecessary,
         [&] (FastBitVector& vector, const char* name) {
             out.print("    ", name, ": ");
             for (unsigned i = maxNameLength - strlen(name); i--;)

Modified: trunk/Source/_javascript_Core/heap/MarkedAllocator.h (209690 => 209691)


--- trunk/Source/_javascript_Core/heap/MarkedAllocator.h	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/heap/MarkedAllocator.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -182,7 +182,7 @@
 #undef MARKED_ALLOCATOR_BIT_ACCESSORS
 
     template<typename Func>
-    void forEachBitVector(const Func& func)
+    void forEachBitVector(const AbstractLocker&, const Func& func)
     {
 #define MARKED_ALLOCATOR_BIT_CALLBACK(lowerBitName, capitalBitName) \
         func(m_ ## lowerBitName);
@@ -191,7 +191,7 @@
     }
     
     template<typename Func>
-    void forEachBitVectorWithName(const Func& func)
+    void forEachBitVectorWithName(const AbstractLocker&, const Func& func)
     {
 #define MARKED_ALLOCATOR_BIT_CALLBACK(lowerBitName, capitalBitName) \
         func(m_ ## lowerBitName, #capitalBitName);

Modified: trunk/Source/_javascript_Core/heap/MarkedBlock.cpp (209690 => 209691)


--- trunk/Source/_javascript_Core/heap/MarkedBlock.cpp	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/heap/MarkedBlock.cpp	2016-12-12 01:19:47 UTC (rev 209691)
@@ -44,7 +44,7 @@
     if (computeBalance) {
         balance++;
         if (!(balance % 10))
-            dataLog(toCString("MarkedBlock Balance: ", balance, "\n"));
+            dataLog("MarkedBlock Balance: ", balance, "\n");
     }
     void* blockSpace = tryFastAlignedMalloc(blockSize, blockSize);
     if (!blockSpace)
@@ -71,7 +71,7 @@
     if (computeBalance) {
         balance--;
         if (!(balance % 10))
-            dataLog(toCString("MarkedBlock Balance: ", balance, "\n"));
+            dataLog("MarkedBlock Balance: ", balance, "\n");
     }
     removeFromAllocator();
     m_block->~MarkedBlock();
@@ -85,7 +85,7 @@
     , m_vm(&vm)
 {
     if (false)
-        dataLog(toCString(RawPointer(this), ": Allocated.\n"));
+        dataLog(RawPointer(this), ": Allocated.\n");
 }
 
 template<MarkedBlock::Handle::EmptyMode emptyMode, MarkedBlock::Handle::SweepMode sweepMode, MarkedBlock::Handle::SweepDestructionMode destructionMode, MarkedBlock::Handle::ScribbleMode scribbleMode, MarkedBlock::Handle::NewlyAllocatedMode newlyAllocatedMode, MarkedBlock::Handle::MarksMode marksMode>
@@ -98,7 +98,7 @@
     MarkedBlock& block = this->block();
     
     if (false)
-        dataLog(toCString(RawPointer(this), "/", RawPointer(&block), ": MarkedBlock::Handle::specializedSweep!\n"));
+        dataLog(RawPointer(this), "/", RawPointer(&block), ": MarkedBlock::Handle::specializedSweep!\n");
     
     if (Options::useBumpAllocator()
         && emptyMode == IsEmpty
@@ -106,11 +106,14 @@
         
         // This is an incredibly powerful assertion that checks the sanity of our block bits.
         if (marksMode == MarksNotStale && !block.m_marks.isEmpty()) {
-            dataLog(toCString("Block ", RawPointer(&block), ": marks not empty!\n"));
-            dataLog(toCString("Block lock is held: ", block.m_lock.isHeld(), "\n"));
-            dataLog(toCString("Marking version of block: ", block.m_markingVersion, "\n"));
-            dataLog(toCString("Marking version of heap: ", space()->markingVersion(), "\n"));
-            RELEASE_ASSERT_NOT_REACHED();
+            WTF::dataFile().atomically(
+                [&] (PrintStream& out) {
+                    out.print("Block ", RawPointer(&block), ": marks not empty!\n");
+                    out.print("Block lock is held: ", block.m_lock.isHeld(), "\n");
+                    out.print("Marking version of block: ", block.m_markingVersion, "\n");
+                    out.print("Marking version of heap: ", space()->markingVersion(), "\n");
+                    UNREACHABLE_FOR_PLATFORM();
+                });
         }
         
         char* startOfLastCell = static_cast<char*>(cellAlign(block.atoms() + m_endAtom - 1));
@@ -127,7 +130,7 @@
             block.m_lock.unlock();
         FreeList result = FreeList::bump(payloadEnd, payloadEnd - payloadBegin);
         if (false)
-            dataLog(toCString("Quickly swept block ", RawPointer(this), " with cell size ", cellSize(), " and attributes ", m_attributes, ": ", result, "\n"));
+            dataLog("Quickly swept block ", RawPointer(this), " with cell size ", cellSize(), " and attributes ", m_attributes, ": ", result, "\n");
         return result;
     }
 
@@ -186,7 +189,7 @@
     else if (isEmpty)
         m_allocator->setIsEmpty(NoLockingNecessary, this, true);
     if (false)
-        dataLog(toCString("Slowly swept block ", RawPointer(&block), " with cell size ", cellSize(), " and attributes ", m_attributes, ": ", result, "\n"));
+        dataLog("Slowly swept block ", RawPointer(&block), " with cell size ", cellSize(), " and attributes ", m_attributes, ": ", result, "\n");
     return result;
 }
 
@@ -262,7 +265,7 @@
     HeapVersion markingVersion = space()->markingVersion();
     bool marksAreUseful = !block().areMarksStale(markingVersion);
     
-    if (space()->isMarking() && heap()->collectionScope() == CollectionScope::Full)
+    if (space()->isMarking())
         marksAreUseful |= block().marksConveyLivenessDuringMarking(markingVersion);
     
     if (!marksAreUseful)
@@ -287,12 +290,12 @@
     auto locker = holdLock(block().m_lock);
     
     if (false)
-        dataLog(toCString(RawPointer(this), ": MarkedBlock::Handle::stopAllocating!\n"));
+        dataLog(RawPointer(this), ": MarkedBlock::Handle::stopAllocating!\n");
     ASSERT(!allocator()->isAllocated(NoLockingNecessary, this));
 
     if (!isFreeListed()) {
         if (false)
-            dataLog(toCString("There ain't no newly allocated.\n"));
+            dataLog("There ain't no newly allocated.\n");
         // This means that we either didn't use this block at all for allocation since last GC,
         // or someone had already done stopAllocating() before.
         ASSERT(freeList.allocationWillFail());
@@ -300,7 +303,7 @@
     }
     
     if (false)
-        dataLog(toCString("Free list: ", freeList, "\n"));
+        dataLog("Free list: ", freeList, "\n");
     
     // Roll back to a coherent state for Heap introspection. Cells newly
     // allocated from our free list are not currently marked, so we need another
@@ -319,7 +322,7 @@
         freeList,
         [&] (HeapCell* cell) {
             if (false)
-                dataLog(toCString("Free cell: ", RawPointer(cell), "\n"));
+                dataLog("Free cell: ", RawPointer(cell), "\n");
             if (m_attributes.destruction == NeedsDestruction)
                 cell->zap();
             clearNewlyAllocated(cell);
@@ -346,13 +349,13 @@
         auto locker = holdLock(block().m_lock);
         
         if (false)
-            dataLog(toCString(RawPointer(this), ": MarkedBlock::Handle::resumeAllocating!\n"));
+            dataLog(RawPointer(this), ": MarkedBlock::Handle::resumeAllocating!\n");
         ASSERT(!allocator()->isAllocated(NoLockingNecessary, this));
         ASSERT(!isFreeListed());
         
         if (!hasAnyNewlyAllocated()) {
             if (false)
-                dataLog(toCString("There ain't no newly allocated.\n"));
+                dataLog("There ain't no newly allocated.\n");
             // This means we had already exhausted the block when we stopped allocation.
             return FreeList();
         }
@@ -401,7 +404,7 @@
     if (handle().allocator()->isAllocated(holdLock(allocator->bitvectorLock()), &handle())
         || !marksConveyLivenessDuringMarking(markingVersion)) {
         if (false)
-            dataLog(toCString(RawPointer(this), ": Clearing marks without doing anything else.\n"));
+            dataLog(RawPointer(this), ": Clearing marks without doing anything else.\n");
         // We already know that the block is full and is already recognized as such, or that the
         // block did not survive the previous GC. So, we can clear mark bits the old fashioned
         // way. Note that it's possible for such a block to have newlyAllocated with an up-to-
@@ -411,7 +414,7 @@
         m_marks.clearAll();
     } else {
         if (false)
-            dataLog(toCString(RawPointer(this), ": Doing things.\n"));
+            dataLog(RawPointer(this), ": Doing things.\n");
         HeapVersion newlyAllocatedVersion = space()->newlyAllocatedVersion();
         if (handle().m_newlyAllocatedVersion == newlyAllocatedVersion) {
             // Merge the contents of marked into newlyAllocated. If we get the full set of bits
@@ -477,7 +480,7 @@
 {
     auto locker = holdLock(block().m_lock);
     if (false)
-        dataLog(toCString(RawPointer(this), ": MarkedBlock::Handle::didConsumeFreeList!\n"));
+        dataLog(RawPointer(this), ": MarkedBlock::Handle::didConsumeFreeList!\n");
     ASSERT(isFreeListed());
     m_isFreeListed = false;
     allocator()->setIsAllocated(NoLockingNecessary, this, true);
@@ -573,6 +576,16 @@
 }
 #endif
 
+void MarkedBlock::Handle::dumpState(PrintStream& out)
+{
+    CommaPrinter comma;
+    allocator()->forEachBitVectorWithName(
+        holdLock(allocator()->bitvectorLock()),
+        [&] (FastBitVector& bitvector, const char* name) {
+            out.print(comma, name, ":", bitvector[index()] ? "YES" : "no");
+        });
+}
+
 } // namespace JSC
 
 namespace WTF {

Modified: trunk/Source/_javascript_Core/heap/MarkedBlock.h (209690 => 209691)


--- trunk/Source/_javascript_Core/heap/MarkedBlock.h	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/heap/MarkedBlock.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -187,6 +187,8 @@
         void didAddToAllocator(MarkedAllocator*, size_t index);
         void didRemoveFromAllocator();
         
+        void dumpState(PrintStream&);
+        
     private:
         Handle(Heap&, void*);
         
@@ -298,6 +300,9 @@
     
     void resetMarks();
     
+    bool isMarkedRaw(const void* p);
+    HeapVersion markingVersion() const { return m_markingVersion; }
+    
 private:
     static const size_t atomAlignmentMask = atomSize - 1;
 
@@ -518,9 +523,14 @@
     block().assertMarksNotStale();
 }
 
+inline bool MarkedBlock::isMarkedRaw(const void* p)
+{
+    return m_marks.get(atomNumber(p));
+}
+
 inline bool MarkedBlock::isMarked(HeapVersion markingVersion, const void* p)
 {
-    return areMarksStale(markingVersion) ? false : m_marks.get(atomNumber(p));
+    return areMarksStale(markingVersion) ? false : isMarkedRaw(p);
 }
 
 inline bool MarkedBlock::isMarkedConcurrently(HeapVersion markingVersion, const void* p)

Modified: trunk/Source/_javascript_Core/heap/MarkedBlockInlines.h (209690 => 209691)


--- trunk/Source/_javascript_Core/heap/MarkedBlockInlines.h	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/heap/MarkedBlockInlines.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -28,6 +28,7 @@
 #include "MarkedAllocator.h"
 #include "MarkedBlock.h"
 #include "MarkedSpace.h"
+#include "VM.h"
 
 namespace JSC {
 
@@ -51,6 +52,8 @@
     // back" state only makes sense when we're in a concurrent collection and have to be
     // conservative.
     ASSERT(space()->isMarking());
+    if (heap()->collectionScope() != CollectionScope::Full)
+        return false;
     return m_markingVersion == MarkedSpace::nullVersion
         || MarkedSpace::nextVersion(m_markingVersion) == markingVersion;
 }

Modified: trunk/Source/_javascript_Core/heap/SlotVisitor.cpp (209690 => 209691)


--- trunk/Source/_javascript_Core/heap/SlotVisitor.cpp	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/heap/SlotVisitor.cpp	2016-12-12 01:19:47 UTC (rev 209691)
@@ -135,18 +135,75 @@
     
     ASSERT(!m_isCheckingForDefaultMarkViolation);
     
+    auto validateCell = [&] (JSCell* jsCell) {
+        StructureID structureID = jsCell->structureID();
+        
+        auto die = [&] (const char* text) {
+            WTF::dataFile().atomically(
+                [&] (PrintStream& out) {
+                    out.print(text);
+                    out.print("GC type: ", heap()->collectionScope(), "\n");
+                    out.print("Object at: ", RawPointer(jsCell), "\n");
+#if USE(JSVALUE64)
+                    out.print("Structure ID: ", structureID, " (0x", format("%x", structureID), ")\n");
+                    out.print("Structure ID table size: ", heap()->structureIDTable().size(), "\n");
+#else
+                    out.print("Structure: ", RawPointer(structureID), "\n");
+#endif
+                    out.print("Object contents:");
+                    for (unsigned i = 0; i < 2; ++i)
+                        out.print(" ", format("0x%016llx", bitwise_cast<uint64_t*>(jsCell)[i]));
+                    out.print("\n");
+                    CellContainer container = jsCell->cellContainer();
+                    out.print("Is marked: ", container.isMarked(jsCell), "\n");
+                    out.print("Is newly allocated: ", container.isNewlyAllocated(jsCell), "\n");
+                    if (container.isMarkedBlock()) {
+                        MarkedBlock& block = container.markedBlock();
+                        out.print("Block: ", RawPointer(&block), "\n");
+                        block.handle().dumpState(out);
+                        out.print("\n");
+                        out.print("Is marked raw: ", block.isMarkedRaw(jsCell), "\n");
+                        out.print("Marking version: ", block.markingVersion(), "\n");
+                        out.print("Heap marking version: ", heap()->objectSpace().markingVersion(), "\n");
+                        out.print("Is newly allocated raw: ", block.handle().isNewlyAllocated(jsCell), "\n");
+                        out.print("Newly allocated version: ", block.handle().newlyAllocatedVersion(), "\n");
+                        out.print("Heap newly allocated version: ", heap()->objectSpace().newlyAllocatedVersion(), "\n");
+                    }
+                    UNREACHABLE_FOR_PLATFORM();
+                });
+        };
+        
+        // It's not OK for the structure to be null at any GC scan point. We must not GC while
+        // an object is not fully initialized.
+        if (!structureID)
+            die("GC scan found corrupt object: structureID is zero!\n");
+        
+        // It's not OK for the structure to be nuked at any GC scan point.
+        if (isNuked(structureID))
+            die("GC scan found object in bad state: structureID is nuked!\n");
+        
+#if USE(JSVALUE64)
+        // This detects the worst of the badness.
+        if (structureID >= heap()->structureIDTable().size())
+            die("GC scan found corrupt object: structureID is out of bounds!\n");
+#endif
+    };
+    
+    // In debug mode, we validate before marking since this makes it clearer what the problem
+    // was. It's also slower, so we don't do it normally.
+    if (!ASSERT_DISABLED && heapCell->cellKind() == HeapCell::JSCell)
+        validateCell(static_cast<JSCell*>(heapCell));
+    
     if (Heap::testAndSetMarked(m_markingVersion, heapCell))
         return;
     
     switch (heapCell->cellKind()) {
     case HeapCell::JSCell: {
+        // We have ample budget to perform validation here.
+    
         JSCell* jsCell = static_cast<JSCell*>(heapCell);
+        validateCell(jsCell);
         
-        if (!jsCell->structure()) {
-            ASSERT_NOT_REACHED();
-            return;
-        }
-        
         jsCell->setCellState(CellState::Grey);
 
         appendToMarkStack(jsCell);

Modified: trunk/Source/_javascript_Core/runtime/StructureIDTable.h (209690 => 209691)


--- trunk/Source/_javascript_Core/runtime/StructureIDTable.h	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/_javascript_Core/runtime/StructureIDTable.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -90,6 +90,8 @@
     StructureID allocateID(Structure*);
 
     void flushOldTables();
+    
+    size_t size() const { return m_size; }
 
 private:
     void resize(size_t newCapacity);
@@ -121,7 +123,9 @@
 inline Structure* StructureIDTable::get(StructureID structureID)
 {
 #if USE(JSVALUE64)
-    ASSERT_WITH_SECURITY_IMPLICATION(structureID && structureID < m_capacity);
+    ASSERT_WITH_SECURITY_IMPLICATION(structureID);
+    ASSERT_WITH_SECURITY_IMPLICATION(!isNuked(structureID));
+    ASSERT_WITH_SECURITY_IMPLICATION(structureID < m_capacity);
     return table()[structureID].structure;
 #else
     return structureID;

Modified: trunk/Source/WTF/ChangeLog (209690 => 209691)


--- trunk/Source/WTF/ChangeLog	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/WTF/ChangeLog	2016-12-12 01:19:47 UTC (rev 209691)
@@ -1,3 +1,71 @@
+2016-12-10  Filip Pizlo  <[email protected]>
+
+        MarkedBlock::marksConveyLivenessDuringMarking should take into account collection scope
+        https://bugs.webkit.org/show_bug.cgi?id=165741
+
+        Reviewed by Saam Barati.
+        
+        To find this bug, I needed to seriously beef up our logging infrastructure.
+        
+        It's now the case that:
+        
+            dataLog(...);
+        
+        will print its output atomically. This happens with some careful magic:
+        
+        - dataFile() is now a LockedPrintStream that locks around print().
+        
+        - The lock is a recursive lock via RecursiveLockAdapter<>, so if the dump methods end
+          up calling back into dataLog() then it just works. This is important: say the dump()
+          calls a getter that itself does logging, maybe because it's encountering badness and
+          wants to report it before crashing).
+        
+        - The lock is a WordLock so that ParkingLot and Lock can keep using dataLog() for
+          debugging. We probably won't need to debug WordLock anytime soon - the algorithm is
+          so simple.
+        
+        - LockedPrintStream::print(...) causes the print callbacks of its arguments to run on
+          the underlying PrintStream, so that you don't need to do recursive lock acquisition
+          on each individual argument and whatever printing it does recursively.
+
+        * WTF.xcodeproj/project.pbxproj:
+        * wtf/CMakeLists.txt:
+        * wtf/DataLog.cpp:
+        (WTF::initializeLogFileOnce):
+        (WTF::initializeLogFile):
+        (WTF::dataFile):
+        * wtf/DataLog.h:
+        * wtf/LockedPrintStream.cpp: Added.
+        (WTF::LockedPrintStream::LockedPrintStream):
+        (WTF::LockedPrintStream::~LockedPrintStream):
+        (WTF::LockedPrintStream::vprintf):
+        (WTF::LockedPrintStream::flush):
+        (WTF::LockedPrintStream::begin):
+        (WTF::LockedPrintStream::end):
+        * wtf/LockedPrintStream.h: Added.
+        * wtf/PrintStream.cpp:
+        (WTF::PrintStream::printfVariableFormat):
+        (WTF::PrintStream::begin):
+        (WTF::PrintStream::end):
+        * wtf/PrintStream.h:
+        (WTF::PrintStream::atomically):
+        (WTF::PrintStream::print):
+        (WTF::PrintStream::println):
+        (WTF::PrintStream::printImpl):
+        (WTF::>::unpack):
+        (WTF::FormatImpl::FormatImpl):
+        (WTF::FormatImpl::dump):
+        (WTF::format):
+        (WTF::printInternal):
+        * wtf/RecursiveLockAdapter.h: Added.
+        (WTF::RecursiveLockAdapter::RecursiveLockAdapter):
+        (WTF::RecursiveLockAdapter::lock):
+        (WTF::RecursiveLockAdapter::unlock):
+        (WTF::RecursiveLockAdapter::tryLock):
+        (WTF::RecursiveLockAdapter::isLocked):
+        * wtf/WordLock.cpp:
+        * wtf/WordLock.h:
+
 2016-12-11  Darin Adler  <[email protected]>
 
         Use std::vsnprintf instead of vasprintf

Modified: trunk/Source/WTF/WTF.xcodeproj/project.pbxproj (209690 => 209691)


--- trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/WTF/WTF.xcodeproj/project.pbxproj	2016-12-12 01:19:47 UTC (rev 209691)
@@ -30,6 +30,8 @@
 		0F43D8F21DB5ADDC00108FB6 /* AutomaticThread.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F43D8F01DB5ADDC00108FB6 /* AutomaticThread.h */; };
 		0F4570431BE5B58F0062A629 /* Dominators.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4570421BE5B58F0062A629 /* Dominators.h */; };
 		0F4570451BE834410062A629 /* BubbleSort.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F4570441BE834410062A629 /* BubbleSort.h */; };
+		0F60F32F1DFCBD1B00416D6C /* LockedPrintStream.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F60F32D1DFCBD1B00416D6C /* LockedPrintStream.cpp */; };
+		0F60F3301DFCBD1B00416D6C /* LockedPrintStream.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F60F32E1DFCBD1B00416D6C /* LockedPrintStream.h */; };
 		0F66B28A1DC97BAB004A1D3F /* ClockType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F66B2801DC97BAB004A1D3F /* ClockType.cpp */; };
 		0F66B28B1DC97BAB004A1D3F /* ClockType.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F66B2811DC97BAB004A1D3F /* ClockType.h */; };
 		0F66B28C1DC97BAB004A1D3F /* MonotonicTime.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F66B2821DC97BAB004A1D3F /* MonotonicTime.cpp */; };
@@ -63,6 +65,7 @@
 		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 */; };
+		0FDE87F71DFD07CC0064C390 /* RecursiveLockAdapter.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FDE87F61DFD07CC0064C390 /* RecursiveLockAdapter.h */; };
 		0FE1646A1B6FFC9600400E7C /* Lock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FE164681B6FFC9600400E7C /* Lock.cpp */; };
 		0FE1646B1B6FFC9600400E7C /* Lock.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FE164691B6FFC9600400E7C /* Lock.h */; };
 		0FE4479C1B7AAA03009498EB /* WordLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FE4479A1B7AAA03009498EB /* WordLock.cpp */; };
@@ -388,6 +391,8 @@
 		0F43D8F01DB5ADDC00108FB6 /* AutomaticThread.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AutomaticThread.h; sourceTree = "<group>"; };
 		0F4570421BE5B58F0062A629 /* Dominators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = Dominators.h; sourceTree = "<group>"; };
 		0F4570441BE834410062A629 /* BubbleSort.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BubbleSort.h; sourceTree = "<group>"; };
+		0F60F32D1DFCBD1B00416D6C /* LockedPrintStream.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = LockedPrintStream.cpp; sourceTree = "<group>"; };
+		0F60F32E1DFCBD1B00416D6C /* LockedPrintStream.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LockedPrintStream.h; sourceTree = "<group>"; };
 		0F66B2801DC97BAB004A1D3F /* ClockType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ClockType.cpp; sourceTree = "<group>"; };
 		0F66B2811DC97BAB004A1D3F /* ClockType.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ClockType.h; sourceTree = "<group>"; };
 		0F66B2821DC97BAB004A1D3F /* MonotonicTime.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = MonotonicTime.cpp; sourceTree = "<group>"; };
@@ -421,6 +426,7 @@
 		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>"; };
+		0FDE87F61DFD07CC0064C390 /* RecursiveLockAdapter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RecursiveLockAdapter.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>"; };
 		0FE4479A1B7AAA03009498EB /* WordLock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = WordLock.cpp; sourceTree = "<group>"; };
@@ -858,7 +864,6 @@
 		A876DBD6151816E500DADB95 /* wtf */ = {
 			isa = PBXGroup;
 			children = (
-				AD7C434A1DD2A4A70026888B /* Expected.h */,
 				2CDED0F018115C3F004DBA70 /* cf */,
 				E4A0AD3B1A96251900536DF6 /* cocoa */,
 				A8A47281151A825A004123FF /* dtoa */,
@@ -926,6 +931,7 @@
 				A8A47297151A825A004123FF /* dtoa.cpp */,
 				A8A47298151A825A004123FF /* dtoa.h */,
 				1AEA88E11D6BBCF400E5AD64 /* EnumTraits.h */,
+				AD7C434A1DD2A4A70026888B /* Expected.h */,
 				A8A4729F151A825A004123FF /* ExportMacros.h */,
 				0F7C5FB51D885CF20044F5E2 /* FastBitVector.cpp */,
 				0FD81AC4154FB22E00983E72 /* FastBitVector.h */,
@@ -966,6 +972,8 @@
 				0FE164681B6FFC9600400E7C /* Lock.cpp */,
 				0FE164691B6FFC9600400E7C /* Lock.h */,
 				0F0FCDDD1DD167F900CCAB53 /* LockAlgorithm.h */,
+				0F60F32D1DFCBD1B00416D6C /* LockedPrintStream.cpp */,
+				0F60F32E1DFCBD1B00416D6C /* LockedPrintStream.h */,
 				A8A472C3151A825A004123FF /* Locker.h */,
 				513E170A1CD7D5BF00E3650B /* LoggingAccumulator.h */,
 				A8A472C6151A825A004123FF /* MainThread.cpp */,
@@ -1025,6 +1033,7 @@
 				A8A472FD151A825B004123FF /* RandomNumberSeed.h */,
 				0F725CAB1C50461600AD943A /* RangeSet.h */,
 				0F87105916643F190090B0AD /* RawPointer.h */,
+				0FDE87F61DFD07CC0064C390 /* RecursiveLockAdapter.h */,
 				A8A472FE151A825B004123FF /* RedBlackTree.h */,
 				26299B6D17A9E5B800ADEBE5 /* Ref.h */,
 				A8A472FF151A825B004123FF /* RefCounted.h */,
@@ -1386,6 +1395,7 @@
 				7CDD7FFA186D2A54007433CD /* IteratorRange.h in Headers */,
 				0F43D8F21DB5ADDC00108FB6 /* AutomaticThread.h in Headers */,
 				93AC91A818942FC400244939 /* LChar.h in Headers */,
+				0F60F3301DFCBD1B00416D6C /* LockedPrintStream.h in Headers */,
 				539EB0631D55284200C82EF7 /* LEBDecoder.h in Headers */,
 				A70DA0851799F04D00529A9B /* ListDump.h in Headers */,
 				0F66B2931DC97BAB004A1D3F /* WallTime.h in Headers */,
@@ -1459,6 +1469,7 @@
 				0FEB3DCF1BB5D684009D7AAD /* SharedTask.h in Headers */,
 				A8A47423151A825B004123FF /* SimpleStats.h in Headers */,
 				A8A47424151A825B004123FF /* SinglyLinkedList.h in Headers */,
+				0FDE87F71DFD07CC0064C390 /* RecursiveLockAdapter.h in Headers */,
 				A748745317A0BDAE00FA04CB /* SixCharacterHash.h in Headers */,
 				79EC70611C99F9BC003A3AE2 /* SmallPtrSet.h in Headers */,
 				A8A47426151A825B004123FF /* Spectrum.h in Headers */,
@@ -1654,6 +1665,7 @@
 				A8A473B0151A825B004123FF /* double-conversion.cc in Sources */,
 				A8A473BA151A825B004123FF /* dtoa.cpp in Sources */,
 				A8A473B3151A825B004123FF /* fast-dtoa.cc in Sources */,
+				0F60F32F1DFCBD1B00416D6C /* LockedPrintStream.cpp in Sources */,
 				A8A473C3151A825B004123FF /* FastMalloc.cpp in Sources */,
 				0F9D3360165DBA73005AD387 /* FilePrintStream.cpp in Sources */,
 				A8A473B5151A825B004123FF /* fixed-dtoa.cc in Sources */,

Modified: trunk/Source/WTF/wtf/CMakeLists.txt (209690 => 209691)


--- trunk/Source/WTF/wtf/CMakeLists.txt	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/WTF/wtf/CMakeLists.txt	2016-12-12 01:19:47 UTC (rev 209691)
@@ -57,6 +57,7 @@
     ListHashSet.h
     Lock.h
     LockAlgorithm.h
+    LockedPrintStream.h
     Locker.h
     MD5.h
     MainThread.h
@@ -92,6 +93,7 @@
     RandomNumberSeed.h
     RangeSet.h
     RawPointer.h
+    RecursiveLockAdapter.h
     RedBlackTree.h
     Ref.h
     RefCounted.h
@@ -198,6 +200,7 @@
     GregorianDateTime.cpp
     HashTable.cpp
     Lock.cpp
+    LockedPrintStream.cpp
     MD5.cpp
     MainThread.cpp
     MediaTime.cpp

Modified: trunk/Source/WTF/wtf/DataLog.cpp (209690 => 209691)


--- trunk/Source/WTF/wtf/DataLog.cpp	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/WTF/wtf/DataLog.cpp	2016-12-12 01:19:47 UTC (rev 209691)
@@ -28,7 +28,10 @@
 #include <stdarg.h>
 #include <string.h>
 #include <wtf/FilePrintStream.h>
+#include <wtf/LockedPrintStream.h>
 #include <wtf/Threading.h>
+#include <mutex>
+#include <thread>
 
 #if OS(UNIX) || OS(DARWIN)
 #include <unistd.h>
@@ -47,16 +50,14 @@
 
 namespace WTF {
 
-#if USE(PTHREADS)
-static pthread_once_t initializeLogFileOnceKey = PTHREAD_ONCE_INIT;
-#endif
+static PrintStream* s_file;
 
-static FilePrintStream* file;
+static uint64_t s_fileData[(sizeof(FilePrintStream) + 7) / 8];
 
-static uint64_t fileData[(sizeof(FilePrintStream) + 7) / 8];
-
 static void initializeLogFileOnce()
 {
+    FilePrintStream* file = nullptr;
+    
 #if DATA_LOG_TO_FILE
     const long maxPathLength = 1024;
 
@@ -117,29 +118,38 @@
             WTFLogAlways("Warning: Could not open DataLog file %s for writing.\n", actualFilename);
     }
 #endif // DATA_LOG_TO_FILE
+    
+    bool wrapWithLocked = true;
+    
     if (!file) {
         // Use placement new; this makes it easier to use dataLog() to debug
         // fastMalloc.
-        file = new (fileData) FilePrintStream(stderr, FilePrintStream::Borrow);
+        file = new (s_fileData) FilePrintStream(stderr, FilePrintStream::Borrow);
+        wrapWithLocked = false;
     }
     
     setvbuf(file->file(), 0, _IONBF, 0); // Prefer unbuffered output, so that we get a full log upon crash or deadlock.
+    
+    if (wrapWithLocked)
+        s_file = new LockedPrintStream(std::unique_ptr<FilePrintStream>(file));
+    else
+        s_file = file;
 }
 
 static void initializeLogFile()
 {
-#if USE(PTHREADS)
-    pthread_once(&initializeLogFileOnceKey, initializeLogFileOnce);
-#else
-    if (!file)
-        initializeLogFileOnce();
-#endif
+    static std::once_flag once;
+    std::call_once(
+        once,
+        [] {
+            initializeLogFileOnce();
+        });
 }
 
-FilePrintStream& dataFile()
+PrintStream& dataFile()
 {
     initializeLogFile();
-    return *file;
+    return *s_file;
 }
 
 void dataLogFV(const char* format, va_list argList)

Modified: trunk/Source/WTF/wtf/DataLog.h (209690 => 209691)


--- trunk/Source/WTF/wtf/DataLog.h	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/WTF/wtf/DataLog.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -28,12 +28,12 @@
 
 #include <stdarg.h>
 #include <stdio.h>
-#include <wtf/FilePrintStream.h>
+#include <wtf/PrintStream.h>
 #include <wtf/StdLibExtras.h>
 
 namespace WTF {
 
-WTF_EXPORT_PRIVATE FilePrintStream& dataFile();
+WTF_EXPORT_PRIVATE PrintStream& dataFile();
 
 WTF_EXPORT_PRIVATE void dataLogFV(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(1, 0);
 WTF_EXPORT_PRIVATE void dataLogF(const char* format, ...) WTF_ATTRIBUTE_PRINTF(1, 2);

Added: trunk/Source/WTF/wtf/LockedPrintStream.cpp (0 => 209691)


--- trunk/Source/WTF/wtf/LockedPrintStream.cpp	                        (rev 0)
+++ trunk/Source/WTF/wtf/LockedPrintStream.cpp	2016-12-12 01:19:47 UTC (rev 209691)
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "LockedPrintStream.h"
+
+namespace WTF {
+
+LockedPrintStream::LockedPrintStream(std::unique_ptr<PrintStream> target)
+    : m_target(WTFMove(target))
+{
+}
+
+LockedPrintStream::~LockedPrintStream()
+{
+}
+
+void LockedPrintStream::vprintf(const char* format, va_list args)
+{
+    auto locker = holdLock(m_lock);
+    m_target->vprintf(format, args);
+}
+
+void LockedPrintStream::flush()
+{
+    auto locker = holdLock(m_lock);
+    m_target->flush();
+}
+
+PrintStream& LockedPrintStream::begin()
+{
+    m_lock.lock();
+    return *m_target;
+}
+
+void LockedPrintStream::end()
+{
+    m_lock.unlock();
+}
+
+} // namespace WTF
+

Added: trunk/Source/WTF/wtf/LockedPrintStream.h (0 => 209691)


--- trunk/Source/WTF/wtf/LockedPrintStream.h	                        (rev 0)
+++ trunk/Source/WTF/wtf/LockedPrintStream.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2016 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef LockedPrintStream_h
+#define LockedPrintStream_h
+
+#include <wtf/PrintStream.h>
+#include <wtf/RecursiveLockAdapter.h>
+#include <wtf/WordLock.h>
+
+namespace WTF {
+
+// Makes every call to print() atomic.
+class LockedPrintStream : public PrintStream {
+public:
+    LockedPrintStream(std::unique_ptr<PrintStream> target);
+    virtual ~LockedPrintStream();
+    
+    void vprintf(const char* format, va_list) override WTF_ATTRIBUTE_PRINTF(2, 0);
+    void flush() override;
+
+protected:
+    PrintStream& begin() override;
+    void end() override;
+
+private:
+    // This needs to be a recursive lock because a printInternal or dump method could assert,
+    // and that assert might want to log. Better to let it. This needs to be a WordLock so that
+    // LockedPrintStream (i.e. cataLog) can be used to debug ParkingLot and Lock.
+    RecursiveLockAdapter<WordLock> m_lock;
+    std::unique_ptr<PrintStream> m_target;
+};
+
+} // namespace WTF
+
+using WTF::LockedPrintStream;
+
+#endif // LockedPrintStream_h

Modified: trunk/Source/WTF/wtf/PrintStream.cpp (209690 => 209691)


--- trunk/Source/WTF/wtf/PrintStream.cpp	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/WTF/wtf/PrintStream.cpp	2016-12-12 01:19:47 UTC (rev 209691)
@@ -44,10 +44,30 @@
     va_end(argList);
 }
 
+void PrintStream::printfVariableFormat(const char* format, ...)
+{
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wformat-nonliteral"
+    va_list argList;
+    va_start(argList, format);
+    vprintf(format, argList);
+    va_end(argList);
+#pragma clang diagnostic pop
+}
+
 void PrintStream::flush()
 {
 }
 
+PrintStream& PrintStream::begin()
+{
+    return *this;
+}
+
+void PrintStream::end()
+{
+}
+
 void printInternal(PrintStream& out, const char* string)
 {
     out.printf("%s", string);

Modified: trunk/Source/WTF/wtf/PrintStream.h (209690 => 209691)


--- trunk/Source/WTF/wtf/PrintStream.h	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/WTF/wtf/PrintStream.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -30,6 +30,7 @@
 #include <stdarg.h>
 #include <wtf/FastMalloc.h>
 #include <wtf/Noncopyable.h>
+#include <wtf/Optional.h>
 #include <wtf/RawPointer.h>
 #include <wtf/RefPtr.h>
 #include <wtf/StdLibExtras.h>
@@ -50,6 +51,7 @@
     virtual ~PrintStream();
 
     WTF_EXPORT_PRIVATE void printf(const char* format, ...) WTF_ATTRIBUTE_PRINTF(2, 3);
+    WTF_EXPORT_PRIVATE void printfVariableFormat(const char* format, ...);
     virtual void vprintf(const char* format, va_list) WTF_ATTRIBUTE_PRINTF(2, 0) = 0;
 
     // Typically a no-op for many subclasses of PrintStream, this is a hint that
@@ -56,27 +58,40 @@
     // the implementation should flush its buffers if it had not done so already.
     virtual void flush();
     
-    void print() { }
-
-    template<typename T>
-    void print(const T& value)
+    template<typename Func>
+    void atomically(const Func& func)
     {
-        printInternal(*this, value);
+        func(begin());
+        end();
     }
-
-    template<typename T, typename... Types>
-    void print(const T& value, const Types&... remainingValues)
+    
+    template<typename... Types>
+    void print(const Types&... values)
     {
-        printInternal(*this, value);
-        print(remainingValues...);
+        atomically(
+            [&] (PrintStream& out) {
+                out.printImpl(values...);
+            });
     }
     
     template<typename... Types>
     void println(const Types&... values)
     {
-        print(values...);
-        print("\n");
+        print(values..., "\n");
     }
+
+protected:
+    void printImpl() { }
+
+    template<typename T, typename... Types>
+    void printImpl(const T& value, const Types&... remainingValues)
+    {
+        printInternal(*this, value);
+        printImpl(remainingValues...);
+    }
+    
+    virtual PrintStream& begin();
+    virtual void end();
 };
 
 WTF_EXPORT_PRIVATE void printInternal(PrintStream&, const char*);
@@ -266,11 +281,66 @@
     return ValueIgnoringContext<T, U>(value);
 }
 
+template<unsigned index, typename... Types>
+struct FormatImplUnpacker {
+    template<typename... Args>
+    static void unpack(PrintStream& out, const std::tuple<Types...>& tuple, const Args&... values);
+};
+    
+template<typename... Types>
+struct FormatImplUnpacker<0, Types...> {
+    template<typename... Args>
+    static void unpack(PrintStream& out, const std::tuple<Types...>& tuple, const Args&... values)
+    {
+        out.printfVariableFormat(std::get<0>(tuple), values...);
+    }
+};
+    
+template<unsigned index, typename... Types>
+template<typename... Args>
+void FormatImplUnpacker<index, Types...>::unpack(PrintStream& out, const std::tuple<Types...>& tuple, const Args&... values)
+{
+    FormatImplUnpacker<index - 1, Types...>::unpack(out, tuple, std::get<index>(tuple), values...);
+}
+
+template<typename... Types>
+class FormatImpl {
+public:
+    FormatImpl(Types... values)
+        : m_values(values...)
+    {
+    }
+    
+    void dump(PrintStream& out) const
+    {
+        FormatImplUnpacker<sizeof...(Types) - 1, Types...>::unpack(out, m_values);
+    }
+
+private:
+    std::tuple<Types...> m_values;
+};
+
+template<typename... Types>
+FormatImpl<Types...> format(Types... values)
+{
+    return FormatImpl<Types...>(values...);
+}
+
+template<typename T>
+void printInternal(PrintStream& out, const std::optional<T>& value)
+{
+    if (value)
+        out.print(*value);
+    else
+        out.print("<nullopt>");
+}
+
 } // namespace WTF
 
 using WTF::CharacterDump;
 using WTF::PointerDump;
 using WTF::PrintStream;
+using WTF::format;
 using WTF::ignoringContext;
 using WTF::inContext;
 using WTF::pointerDump;

Added: trunk/Source/WTF/wtf/RecursiveLockAdapter.h (0 => 209691)


--- trunk/Source/WTF/wtf/RecursiveLockAdapter.h	                        (rev 0)
+++ trunk/Source/WTF/wtf/RecursiveLockAdapter.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2016 Apple Inc.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef WTF_RecursiveLockAdapter_h
+#define WTF_RecursiveLockAdapter_h
+
+#include <wtf/Threading.h>
+
+namespace WTF {
+
+template<typename LockType>
+class RecursiveLockAdapter {
+public:
+    RecursiveLockAdapter()
+    {
+    }
+    
+    void lock()
+    {
+        ThreadIdentifier me = currentThread();
+        if (me == m_owner) {
+            m_recursionCount++;
+            return;
+        }
+        
+        m_lock.lock();
+        ASSERT(!m_owner);
+        ASSERT(!m_recursionCount);
+        m_owner = me;
+        m_recursionCount = 1;
+    }
+    
+    void unlock()
+    {
+        if (--m_recursionCount)
+            return;
+        m_owner = 0;
+        m_lock.unlock();
+    }
+    
+    bool tryLock()
+    {
+        ThreadIdentifier me = currentThread();
+        if (me == m_owner) {
+            m_recursionCount++;
+            return true;
+        }
+        
+        if (!m_lock.tryLock())
+            return false;
+        
+        ASSERT(!m_owner);
+        ASSERT(!m_recursionCount);
+        m_owner = me;
+        m_recursionCount = 1;
+        return true;
+    }
+    
+    bool isLocked() const
+    {
+        return m_lock.isLocked();
+    }
+    
+private:
+    ThreadIdentifier m_owner { 0 };
+    unsigned m_recursionCount { 0 };
+    LockType m_lock;
+};
+
+} // namespace WTF
+
+#endif // WTF_RecursiveLockAdapter_h
+

Modified: trunk/Source/WTF/wtf/WordLock.cpp (209690 => 209691)


--- trunk/Source/WTF/wtf/WordLock.cpp	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/WTF/wtf/WordLock.cpp	2016-12-12 01:19:47 UTC (rev 209691)
@@ -26,8 +26,6 @@
 #include "config.h"
 #include "WordLock.h"
 
-#include "DataLog.h"
-#include "StringPrintStream.h"
 #include "ThreadSpecific.h"
 #include "ThreadingPrimitives.h"
 #include <condition_variable>

Modified: trunk/Source/WTF/wtf/WordLock.h (209690 => 209691)


--- trunk/Source/WTF/wtf/WordLock.h	2016-12-12 01:10:00 UTC (rev 209690)
+++ trunk/Source/WTF/wtf/WordLock.h	2016-12-12 01:19:47 UTC (rev 209691)
@@ -42,6 +42,10 @@
 // Lock instead. WordLock sits lower in the stack and is used to implement Lock, so Lock is the main
 // client of WordLock.
 
+// NOTE: This is also a great lock to use if you are very low in the stack. For example,
+// PrintStream uses this so that ParkingLot and Lock can use PrintStream. This means that if you
+// try to use dataLog to debug this code, you will have a bad time.
+
 struct WordLockBase {
     void lock()
     {
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to