Title: [159351] trunk/Source/_javascript_Core
Revision
159351
Author
mhahnenb...@apple.com
Date
2013-11-15 11:53:30 -0800 (Fri, 15 Nov 2013)

Log Message

-dealloc callbacks from wrapped Objective-C objects can happen at bad times
https://bugs.webkit.org/show_bug.cgi?id=123821

Reviewed by Darin Adler.

Currently with the JSC Obj-C API, JS wrappers for client Obj-C objects retain their associated Obj-C 
object. When they are swept, they release their Obj-C objects which can trigger a call to that 
object's -dealloc method. These -dealloc methods can then call back into the same VM, which is not 
allowed during sweeping or VM shutdown.

We can handle this case by creating our own pool of Obj-C objects to be released when it is safe to do so.
This is accomplished by using DelayedReleaseScope, an RAII-style object that will retain all objects
that are unsafe to release until the end of the DelayedReleaseScope.

* API/APIShims.h:
(JSC::APICallbackShim::APICallbackShim):
(JSC::APICallbackShim::vmForDropAllLocks):
(JSC::APICallbackShim::execForDropAllLocks):
* API/JSAPIWrapperObject.mm:
(JSAPIWrapperObjectHandleOwner::finalize):
* API/ObjCCallbackFunction.mm:
(JSC::ObjCCallbackFunctionImpl::destroy):
(JSC::ObjCCallbackFunction::destroy):
* API/tests/testapi.mm:
(-[TinyDOMNode initWithVirtualMachine:]):
(-[TinyDOMNode dealloc]):
(-[TinyDOMNode appendChild:]):
(-[TinyDOMNode removeChildAtIndex:]):
(-[EvilAllocationObject initWithContext:]):
(-[EvilAllocationObject dealloc]):
(-[EvilAllocationObject doEvilThingsWithContext:]):
* _javascript_Core.xcodeproj/project.pbxproj:
* heap/DelayedReleaseScope.h: Added.
(JSC::DelayedReleaseScope::DelayedReleaseScope):
(JSC::DelayedReleaseScope::~DelayedReleaseScope):
(JSC::DelayedReleaseScope::releaseSoon):
(JSC::MarkedSpace::releaseSoon):
* heap/Heap.cpp:
(JSC::Heap::collectAllGarbage):
* heap/Heap.h:
(JSC::Heap::releaseSoon):
* heap/MarkedAllocator.cpp:
(JSC::MarkedAllocator::allocateSlowCase):
* heap/MarkedSpace.cpp:
(JSC::MarkedSpace::MarkedSpace):
(JSC::MarkedSpace::lastChanceToFinalize):
(JSC::MarkedSpace::sweep):
* heap/MarkedSpace.h:

Modified Paths

Added Paths

Diff

Modified: trunk/Source/_javascript_Core/API/APIShims.h (159350 => 159351)


--- trunk/Source/_javascript_Core/API/APIShims.h	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/API/APIShims.h	2013-11-15 19:53:30 UTC (rev 159351)
@@ -83,18 +83,39 @@
 class APICallbackShim {
 public:
     APICallbackShim(ExecState* exec)
-        : m_dropAllLocks(exec->vm().exclusiveThread ? 0 : exec)
+        : m_dropAllLocks(shouldDropAllLocks(exec->vm()) ? exec : nullptr)
         , m_vm(&exec->vm())
     {
         wtfThreadData().resetCurrentIdentifierTable();
     }
 
+    APICallbackShim(VM& vm)
+        : m_dropAllLocks(shouldDropAllLocks(vm) ? &vm : nullptr)
+        , m_vm(&vm)
+    {
+        wtfThreadData().resetCurrentIdentifierTable();
+    }
+
     ~APICallbackShim()
     {
         wtfThreadData().setCurrentIdentifierTable(m_vm->identifierTable);
     }
 
 private:
+    static bool shouldDropAllLocks(VM& vm)
+    {
+        if (vm.exclusiveThread)
+            return false;
+
+        // If the VM is in the middle of being destroyed then we don't want to resurrect it
+        // by allowing DropAllLocks to ref it. By this point the APILock has already been 
+        // released anyways, so it doesn't matter that DropAllLocks is a no-op.
+        if (!vm.refCount())
+            return false;
+
+        return true;
+    }
+
     JSLock::DropAllLocks m_dropAllLocks;
     VM* m_vm;
 };

Modified: trunk/Source/_javascript_Core/API/JSAPIWrapperObject.mm (159350 => 159351)


--- trunk/Source/_javascript_Core/API/JSAPIWrapperObject.mm	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/API/JSAPIWrapperObject.mm	2013-11-15 19:53:30 UTC (rev 159351)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "JSAPIWrapperObject.h"
 
+#include "DelayedReleaseScope.h"
 #include "JSCJSValueInlines.h"
 #include "JSCallbackObject.h"
 #include "JSCellInlines.h"
@@ -53,7 +54,8 @@
     JSC::JSAPIWrapperObject* wrapperObject = JSC::jsCast<JSC::JSAPIWrapperObject*>(handle.get().asCell());
     if (!wrapperObject->wrappedObject())
         return;
-    [static_cast<id>(wrapperObject->wrappedObject()) release];
+
+    JSC::Heap::heap(wrapperObject)->releaseSoon(adoptNS(static_cast<id>(wrapperObject->wrappedObject())));
     JSC::WeakSet::deallocate(JSC::WeakImpl::asWeakImpl(handle.slot()));
 }
 

Modified: trunk/Source/_javascript_Core/API/ObjCCallbackFunction.mm (159350 => 159351)


--- trunk/Source/_javascript_Core/API/ObjCCallbackFunction.mm	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/API/ObjCCallbackFunction.mm	2013-11-15 19:53:30 UTC (rev 159351)
@@ -31,6 +31,7 @@
 #import "APICallbackFunction.h"
 #import "APICast.h"
 #import "APIShims.h"
+#import "DelayedReleaseScope.h"
 #import "Error.h"
 #import "JSCJSValueInlines.h"
 #import "JSCell.h"
@@ -405,12 +406,12 @@
         ASSERT((type != CallbackInstanceMethod && type != CallbackInitMethod) || instanceClass);
     }
 
-    ~ObjCCallbackFunctionImpl()
+    void destroy(Heap& heap)
     {
-        // We need to explicity release the target since we didn't call 
+        // We need to explicitly release the target since we didn't call 
         // -retainArguments on m_invocation (and we don't want to do so).
         if (m_type == CallbackBlock || m_type == CallbackClassMethod)
-            [[m_invocation.get() target] release];
+            heap.releaseSoon(adoptNS([m_invocation.get() target]));
         [m_instanceClass release];
     }
 
@@ -520,7 +521,9 @@
 
 void ObjCCallbackFunction::destroy(JSCell* cell)
 {
-    static_cast<ObjCCallbackFunction*>(cell)->ObjCCallbackFunction::~ObjCCallbackFunction();
+    ObjCCallbackFunction& function = *jsCast<ObjCCallbackFunction*>(cell);
+    function.impl()->destroy(*Heap::heap(cell));
+    function.~ObjCCallbackFunction();
 }
 
 

Modified: trunk/Source/_javascript_Core/API/tests/testapi.mm (159350 => 159351)


--- trunk/Source/_javascript_Core/API/tests/testapi.mm	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/API/tests/testapi.mm	2013-11-15 19:53:30 UTC (rev 159351)
@@ -182,54 +182,43 @@
 @end
 
 @interface TinyDOMNode : NSObject<TinyDOMNode>
-+ (JSVirtualMachine *)sharedVirtualMachine;
-+ (void)clearSharedVirtualMachine;
 @end
 
 @implementation TinyDOMNode {
     NSMutableArray *m_children;
+    JSVirtualMachine *m_sharedVirtualMachine;
 }
 
-static JSVirtualMachine *sharedInstance = nil;
-
-+ (JSVirtualMachine *)sharedVirtualMachine
+- (id)initWithVirtualMachine:(JSVirtualMachine *)virtualMachine
 {
-    if (!sharedInstance)
-        sharedInstance = [[JSVirtualMachine alloc] init];
-    return sharedInstance;
-}
-
-+ (void)clearSharedVirtualMachine
-{
-    sharedInstance = nil;
-}
-
-- (id)init
-{
     self = [super init];
     if (!self)
         return nil;
 
     m_children = [[NSMutableArray alloc] initWithCapacity:0];
+    m_sharedVirtualMachine = virtualMachine;
+#if !__has_feature(objc_arc)
+    [m_sharedVirtualMachine retain];
+#endif
 
     return self;
 }
 
 - (void)dealloc
 {
-    NSEnumerator *enumerator = [m_children objectEnumerator];
-    id nextChild;
-    while ((nextChild = [enumerator nextObject]))
-        [[TinyDOMNode sharedVirtualMachine] removeManagedReference:nextChild withOwner:self];
+    for (TinyDOMNode *child in m_children)
+        [m_sharedVirtualMachine removeManagedReference:child withOwner:self];
 
 #if !__has_feature(objc_arc)
+    [m_children release];
+    [m_sharedVirtualMachine release];
     [super dealloc];
 #endif
 }
 
 - (void)appendChild:(TinyDOMNode *)child
 {
-    [[TinyDOMNode sharedVirtualMachine] addManagedReference:child withOwner:self];
+    [m_sharedVirtualMachine addManagedReference:child withOwner:self];
     [m_children addObject:child];
 }
 
@@ -249,7 +238,7 @@
 {
     if (index >= [m_children count])
         return;
-    [[TinyDOMNode sharedVirtualMachine] removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
+    [m_sharedVirtualMachine removeManagedReference:[m_children objectAtIndex:index] withOwner:self];
     [m_children removeObjectAtIndex:index];
 }
 
@@ -424,6 +413,47 @@
     return self;
 }
 @end
+
+static bool evilAllocationObjectWasDealloced = false;
+
+@interface EvilAllocationObject : NSObject
+- (JSValue *)doEvilThingsWithContext:(JSContext *)context;
+@end
+
+@implementation EvilAllocationObject {
+    JSContext *m_context;
+}
+- (id)initWithContext:(JSContext *)context
+{
+    self = [super init];
+    if (!self)
+        return nil;
+
+    m_context = context;
+
+    return self;
+}
+- (void)dealloc
+{
+    [self doEvilThingsWithContext:m_context];
+    evilAllocationObjectWasDealloced = true;
+}
+
+- (JSValue *)doEvilThingsWithContext:(JSContext *)context
+{
+    return [context evaluateScript:@" \
+        (function() { \
+            var a = []; \
+            var sum = 0; \
+            for (var i = 0; i < 10000; ++i) { \
+                sum += i; \
+                a[i] = sum; \
+            } \
+            return sum; \
+        })()"];
+}
+@end
+
 static void checkResult(NSString *description, bool passed)
 {
     NSLog(@"TEST: \"%@\": %@", description, passed ? @"PASSED" : @"FAILED");
@@ -958,12 +988,11 @@
     }
 
     @autoreleasepool {
-        JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
-        JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
-        TinyDOMNode *root = [[TinyDOMNode alloc] init];
+        JSContext *context = [[JSContext alloc] init];
+        TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
         TinyDOMNode *lastNode = root;
         for (NSUInteger i = 0; i < 3; i++) {
-            TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
+            TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
             [lastNode appendChild:newNode];
             lastNode = newNode;
         }
@@ -985,17 +1014,14 @@
 
         JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
         checkResult(@"My custom property == 42", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
-
-        [TinyDOMNode clearSharedVirtualMachine];
     }
 
     @autoreleasepool {
-        JSVirtualMachine *vm = [TinyDOMNode sharedVirtualMachine];
-        JSContext *context = [[JSContext alloc] initWithVirtualMachine:vm];
-        TinyDOMNode *root = [[TinyDOMNode alloc] init];
+        JSContext *context = [[JSContext alloc] init];
+        TinyDOMNode *root = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
         TinyDOMNode *lastNode = root;
         for (NSUInteger i = 0; i < 3; i++) {
-            TinyDOMNode *newNode = [[TinyDOMNode alloc] init];
+            TinyDOMNode *newNode = [[TinyDOMNode alloc] initWithVirtualMachine:context.virtualMachine];
             [lastNode appendChild:newNode];
             lastNode = newNode;
         }
@@ -1020,8 +1046,6 @@
 
         JSValue *myCustomProperty = [context evaluateScript:@"getLastNodeInChain(root).myCustomProperty"];
         checkResult(@"duplicate calls to addManagedReference don't cause things to die", [myCustomProperty isNumber] && [myCustomProperty toInt32] == 42);
-
-        [TinyDOMNode clearSharedVirtualMachine];
     }
 
     @autoreleasepool {
@@ -1161,6 +1185,17 @@
         checkResult(@"Returning instance of ClassE from ClassD's init has correct class", [d isInstanceOf:context[@"ClassE"]]);
     }
 
+    @autoreleasepool {
+        JSContext *context = [[JSContext alloc] init];
+        @autoreleasepool {
+            EvilAllocationObject *evilObject = [[EvilAllocationObject alloc] initWithContext:context];
+            context[@"evilObject"] = evilObject;
+            context[@"evilObject"] = nil;
+        }
+        JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+        checkResult(@"EvilAllocationObject was successfully dealloced without crashing", evilAllocationObjectWasDealloced);
+    }
+
     currentThisInsideBlockGetterTest();
 }
 

Modified: trunk/Source/_javascript_Core/ChangeLog (159350 => 159351)


--- trunk/Source/_javascript_Core/ChangeLog	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/ChangeLog	2013-11-15 19:53:30 UTC (rev 159351)
@@ -1,3 +1,54 @@
+2013-11-14  Mark Hahnenberg  <mhahnenb...@apple.com>
+
+        -dealloc callbacks from wrapped Objective-C objects can happen at bad times
+        https://bugs.webkit.org/show_bug.cgi?id=123821
+
+        Reviewed by Darin Adler.
+
+        Currently with the JSC Obj-C API, JS wrappers for client Obj-C objects retain their associated Obj-C 
+        object. When they are swept, they release their Obj-C objects which can trigger a call to that 
+        object's -dealloc method. These -dealloc methods can then call back into the same VM, which is not 
+        allowed during sweeping or VM shutdown.
+
+        We can handle this case by creating our own pool of Obj-C objects to be released when it is safe to do so.
+        This is accomplished by using DelayedReleaseScope, an RAII-style object that will retain all objects
+        that are unsafe to release until the end of the DelayedReleaseScope.
+
+        * API/APIShims.h:
+        (JSC::APICallbackShim::APICallbackShim):
+        (JSC::APICallbackShim::vmForDropAllLocks):
+        (JSC::APICallbackShim::execForDropAllLocks):
+        * API/JSAPIWrapperObject.mm:
+        (JSAPIWrapperObjectHandleOwner::finalize):
+        * API/ObjCCallbackFunction.mm:
+        (JSC::ObjCCallbackFunctionImpl::destroy):
+        (JSC::ObjCCallbackFunction::destroy):
+        * API/tests/testapi.mm:
+        (-[TinyDOMNode initWithVirtualMachine:]):
+        (-[TinyDOMNode dealloc]):
+        (-[TinyDOMNode appendChild:]):
+        (-[TinyDOMNode removeChildAtIndex:]):
+        (-[EvilAllocationObject initWithContext:]):
+        (-[EvilAllocationObject dealloc]):
+        (-[EvilAllocationObject doEvilThingsWithContext:]):
+        * _javascript_Core.xcodeproj/project.pbxproj:
+        * heap/DelayedReleaseScope.h: Added.
+        (JSC::DelayedReleaseScope::DelayedReleaseScope):
+        (JSC::DelayedReleaseScope::~DelayedReleaseScope):
+        (JSC::DelayedReleaseScope::releaseSoon):
+        (JSC::MarkedSpace::releaseSoon):
+        * heap/Heap.cpp:
+        (JSC::Heap::collectAllGarbage):
+        * heap/Heap.h:
+        (JSC::Heap::releaseSoon):
+        * heap/MarkedAllocator.cpp:
+        (JSC::MarkedAllocator::allocateSlowCase):
+        * heap/MarkedSpace.cpp:
+        (JSC::MarkedSpace::MarkedSpace):
+        (JSC::MarkedSpace::lastChanceToFinalize):
+        (JSC::MarkedSpace::sweep):
+        * heap/MarkedSpace.h:
+
 2013-11-15  Michael Saboff  <msab...@apple.com>
 
         REGRESSION (r158586): callToJavaScript needs to save return PC to Sentinel frame

Modified: trunk/Source/_javascript_Core/GNUmakefile.list.am (159350 => 159351)


--- trunk/Source/_javascript_Core/GNUmakefile.list.am	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/GNUmakefile.list.am	2013-11-15 19:53:30 UTC (rev 159351)
@@ -486,8 +486,9 @@
 	Source/_javascript_Core/heap/CopyWorkList.h \
 	Source/_javascript_Core/heap/ConservativeRoots.cpp \
 	Source/_javascript_Core/heap/ConservativeRoots.h \
-   Source/_javascript_Core/heap/DeferGC.cpp \
+    Source/_javascript_Core/heap/DeferGC.cpp \
 	Source/_javascript_Core/heap/DeferGC.h \
+    Source/_javascript_Core/heap/DelayedReleaseScope.h \
 	Source/_javascript_Core/heap/GCAssertions.h \
 	Source/_javascript_Core/heap/GCIncomingRefCounted.h \
 	Source/_javascript_Core/heap/GCIncomingRefCountedInlines.h \

Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj (159350 => 159351)


--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj	2013-11-15 19:53:30 UTC (rev 159351)
@@ -936,6 +936,7 @@
     <ClInclude Include="..\heap\CopyVisitorInlines.h" />
     <ClInclude Include="..\heap\CopyWorkList.h" />
     <ClInclude Include="..\heap\DeferGC.h" />
+    <ClInclude Include="..\heap\DelayedReleaseScope.h" />
     <ClInclude Include="..\heap\GCAssertions.h" />
     <ClInclude Include="..\heap\GCThread.h" />
     <ClInclude Include="..\heap\GCThreadSharedData.h" />

Modified: trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters (159350 => 159351)


--- trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/_javascript_Core.vcxproj/_javascript_Core.vcxproj.filters	2013-11-15 19:53:30 UTC (rev 159351)
@@ -1559,6 +1559,9 @@
     <ClInclude Include="..\heap\DeferGC.h">
       <Filter>heap</Filter>
     </ClInclude>
+    <ClInclude Include="..\heap\DelayedReleaseScpe.h">
+      <Filter>heap</Filter>
+    </ClInclude>
     <ClInclude Include="..\heap\GCAssertions.h">
       <Filter>heap</Filter>
     </ClInclude>

Modified: trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj (159350 => 159351)


--- trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/_javascript_Core.xcodeproj/project.pbxproj	2013-11-15 19:53:30 UTC (rev 159351)
@@ -705,6 +705,7 @@
 		1ACF7377171CA6FB00C9BB1E /* Weak.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 1ACF7376171CA6FB00C9BB1E /* Weak.cpp */; };
 		2600B5A6152BAAA70091EE5F /* JSStringJoiner.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2600B5A4152BAAA70091EE5F /* JSStringJoiner.cpp */; };
 		2600B5A7152BAAA70091EE5F /* JSStringJoiner.h in Headers */ = {isa = PBXBuildFile; fileRef = 2600B5A5152BAAA70091EE5F /* JSStringJoiner.h */; };
+		2A2825D018341F2D0087FBA9 /* DelayedReleaseScope.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A2825CF18341F2D0087FBA9 /* DelayedReleaseScope.h */; };
 		2A48D1911772365B00C65A5F /* APICallbackFunction.h in Headers */ = {isa = PBXBuildFile; fileRef = C211B574176A224D000E2A23 /* APICallbackFunction.h */; };
 		2A6F462617E959CE00C45C98 /* HeapOperation.h in Headers */ = {isa = PBXBuildFile; fileRef = 2A6F462517E959CE00C45C98 /* HeapOperation.h */; settings = {ATTRIBUTES = (Private, ); }; };
 		2A7A58EF1808A4C40020BDF7 /* DeferGC.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 2A7A58EE1808A4C40020BDF7 /* DeferGC.cpp */; };
@@ -1957,6 +1958,7 @@
 		1CAA8B4B0D32C39A0041BCFF /* _javascript_Core.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = _javascript_Core.h; sourceTree = "<group>"; };
 		2600B5A4152BAAA70091EE5F /* JSStringJoiner.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = JSStringJoiner.cpp; sourceTree = "<group>"; };
 		2600B5A5152BAAA70091EE5F /* JSStringJoiner.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSStringJoiner.h; sourceTree = "<group>"; };
+		2A2825CF18341F2D0087FBA9 /* DelayedReleaseScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DelayedReleaseScope.h; sourceTree = "<group>"; };
 		2A6F462517E959CE00C45C98 /* HeapOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapOperation.h; sourceTree = "<group>"; };
 		2A7A58EE1808A4C40020BDF7 /* DeferGC.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = DeferGC.cpp; sourceTree = "<group>"; };
 		2AD8932917E3868F00668276 /* HeapIterationScope.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = HeapIterationScope.h; sourceTree = "<group>"; };
@@ -3094,6 +3096,7 @@
 				14150132154BB13F005D8C98 /* WeakSetInlines.h */,
 				0FC8150814043BCA00CFA603 /* WriteBarrierSupport.cpp */,
 				0FC8150914043BD200CFA603 /* WriteBarrierSupport.h */,
+				2A2825CF18341F2D0087FBA9 /* DelayedReleaseScope.h */,
 			);
 			path = heap;
 			sourceTree = "<group>";
@@ -4745,6 +4748,7 @@
 				C22B31B9140577D700DB475A /* SamplingCounter.h in Headers */,
 				1429D8860ED21C3D00B89619 /* SamplingTool.h in Headers */,
 				0F24E55217EE274900ABB217 /* ScratchRegisterAllocator.h in Headers */,
+				2A2825D018341F2D0087FBA9 /* DelayedReleaseScope.h in Headers */,
 				A7299DA617D12858005F5FF9 /* SetConstructor.h in Headers */,
 				A7299DA217D12848005F5FF9 /* SetPrototype.h in Headers */,
 				86AE64AA135E5E1C00963012 /* SH4Assembler.h in Headers */,

Added: trunk/Source/_javascript_Core/heap/DelayedReleaseScope.h (0 => 159351)


--- trunk/Source/_javascript_Core/heap/DelayedReleaseScope.h	                        (rev 0)
+++ trunk/Source/_javascript_Core/heap/DelayedReleaseScope.h	2013-11-15 19:53:30 UTC (rev 159351)
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2013 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.
+ */
+
+#ifndef DelayedReleaseScope_h
+#define DelayedReleaseScope_h
+
+#include "APIShims.h"
+#include "MarkedSpace.h"
+
+namespace JSC {
+
+#if USE(CF)
+
+class DelayedReleaseScope {
+public:
+    DelayedReleaseScope(MarkedSpace& markedSpace)
+        : m_markedSpace(markedSpace)
+    {
+        ASSERT(!m_markedSpace.m_currentDelayedReleaseScope);
+        m_markedSpace.m_currentDelayedReleaseScope = this;
+    }
+
+    ~DelayedReleaseScope()
+    {
+        ASSERT(m_markedSpace.m_currentDelayedReleaseScope == this);
+        m_markedSpace.m_currentDelayedReleaseScope = nullptr;
+
+        APICallbackShim callbackShim(*m_markedSpace.m_heap->vm());
+        m_delayedReleaseObjects.clear();
+    }
+
+    template <typename T>
+    void releaseSoon(RetainPtr<T>&& object)
+    {
+        m_delayedReleaseObjects.append(std::move(object));
+    }
+
+private:
+    MarkedSpace& m_markedSpace;
+    Vector<RetainPtr<CFTypeRef>> m_delayedReleaseObjects;
+};
+
+template <typename T>
+inline void MarkedSpace::releaseSoon(RetainPtr<T>&& object)
+{
+    ASSERT(m_currentDelayedReleaseScope);
+    m_currentDelayedReleaseScope->releaseSoon(std::move(object));
+}
+
+#else // USE(CF)
+
+class DelayedReleaseScope {
+public:
+    DelayedReleaseScope(MarkedSpace&)
+    {
+    }
+};
+
+#endif // USE(CF)
+
+} // namespace JSC
+
+#endif // DelayedReleaseScope_h

Modified: trunk/Source/_javascript_Core/heap/Heap.cpp (159350 => 159351)


--- trunk/Source/_javascript_Core/heap/Heap.cpp	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/heap/Heap.cpp	2013-11-15 19:53:30 UTC (rev 159351)
@@ -27,6 +27,7 @@
 #include "CopiedSpaceInlines.h"
 #include "CopyVisitorInlines.h"
 #include "DFGWorklist.h"
+#include "DelayedReleaseScope.h"
 #include "GCActivityCallback.h"
 #include "GCIncomingRefCountedSetInlines.h"
 #include "HeapIterationScope.h"
@@ -729,6 +730,7 @@
     if (!m_isSafeToCollect)
         return;
     
+    DelayedReleaseScope delayedReleaseScope(m_objectSpace);
     collect(DoSweep);
 }
 

Modified: trunk/Source/_javascript_Core/heap/Heap.h (159350 => 159351)


--- trunk/Source/_javascript_Core/heap/Heap.h	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/heap/Heap.h	2013-11-15 19:53:30 UTC (rev 159351)
@@ -186,6 +186,10 @@
         
         bool isDeferred() const { return !!m_deferralDepth; }
 
+#if USE(CF)
+        template<typename T> void releaseSoon(RetainPtr<T>&&);
+#endif
+
     private:
         friend class CodeBlock;
         friend class CopiedBlock;
@@ -468,6 +472,14 @@
         return m_blockAllocator;
     }
 
+#if USE(CF)
+    template <typename T>
+    inline void Heap::releaseSoon(RetainPtr<T>&& object)
+    {
+        m_objectSpace.releaseSoon(std::move(object));
+    }
+#endif
+
 } // namespace JSC
 
 #endif // Heap_h

Modified: trunk/Source/_javascript_Core/heap/MarkedAllocator.cpp (159350 => 159351)


--- trunk/Source/_javascript_Core/heap/MarkedAllocator.cpp	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/heap/MarkedAllocator.cpp	2013-11-15 19:53:30 UTC (rev 159351)
@@ -1,6 +1,7 @@
 #include "config.h"
 #include "MarkedAllocator.h"
 
+#include "DelayedReleaseScope.h"
 #include "GCActivityCallback.h"
 #include "Heap.h"
 #include "IncrementalSweeper.h"
@@ -77,6 +78,7 @@
 void* MarkedAllocator::allocateSlowCase(size_t bytes)
 {
     ASSERT(m_heap->vm()->currentThreadIsHoldingAPILock());
+    DelayedReleaseScope delayedReleaseScope(*m_markedSpace);
 #if COLLECT_ON_EVERY_ALLOCATION
     if (!m_heap->isDeferred())
         m_heap->collectAllGarbage();

Modified: trunk/Source/_javascript_Core/heap/MarkedSpace.cpp (159350 => 159351)


--- trunk/Source/_javascript_Core/heap/MarkedSpace.cpp	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/heap/MarkedSpace.cpp	2013-11-15 19:53:30 UTC (rev 159351)
@@ -21,6 +21,7 @@
 #include "config.h"
 #include "MarkedSpace.h"
 
+#include "DelayedReleaseScope.h"
 #include "IncrementalSweeper.h"
 #include "JSGlobalObject.h"
 #include "JSLock.h"
@@ -81,6 +82,7 @@
     : m_heap(heap)
     , m_capacity(0)
     , m_isIterating(false)
+    , m_currentDelayedReleaseScope(nullptr)
 {
     for (size_t cellSize = preciseStep; cellSize <= preciseCutoff; cellSize += preciseStep) {
         allocatorFor(cellSize).init(heap, this, cellSize, MarkedBlock::None);
@@ -111,12 +113,14 @@
 
 void MarkedSpace::lastChanceToFinalize()
 {
+    DelayedReleaseScope delayedReleaseScope(*this);
     stopAllocating();
     forEachBlock<LastChanceToFinalize>();
 }
 
 void MarkedSpace::sweep()
 {
+    ASSERT(m_currentDelayedReleaseScope);
     m_heap->sweeper()->willFinishSweeping();
     forEachBlock<Sweep>();
 }

Modified: trunk/Source/_javascript_Core/heap/MarkedSpace.h (159350 => 159351)


--- trunk/Source/_javascript_Core/heap/MarkedSpace.h	2013-11-15 19:51:26 UTC (rev 159350)
+++ trunk/Source/_javascript_Core/heap/MarkedSpace.h	2013-11-15 19:53:30 UTC (rev 159351)
@@ -36,6 +36,7 @@
 
 namespace JSC {
 
+class DelayedReleaseScope;
 class Heap;
 class HeapIterationScope;
 class JSCell;
@@ -114,7 +115,12 @@
 
     bool isPagedOut(double deadline);
 
+#if USE(CF)
+    template<typename T> void releaseSoon(RetainPtr<T>&&);
+#endif
+
 private:
+    friend class DelayedReleaseScope;
     friend class LLIntOffsetsExtractor;
 
     template<typename Functor> void forEachAllocator(Functor&);
@@ -144,6 +150,8 @@
     size_t m_capacity;
     bool m_isIterating;
     MarkedBlockSet m_blocks;
+
+    DelayedReleaseScope* m_currentDelayedReleaseScope;
 };
 
 template<typename Functor> inline typename Functor::ReturnType MarkedSpace::forEachLiveCell(HeapIterationScope&, Functor& functor)
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to