<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[180992] trunk/Source/JavaScriptCore</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/180992">180992</a></dd>
<dt>Author</dt> <dd>msaboff@apple.com</dd>
<dt>Date</dt> <dd>2015-03-03 21:33:37 -0800 (Tue, 03 Mar 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>DelayedReleaseScope drops locks during GC which can cause a thread switch and code reentry
https://bugs.webkit.org/show_bug.cgi?id=141275

Reviewed by Geoffrey Garen.

The original issue is that the CodeCache uses an unsafe method to add new UnlinkedCodeBlocks.
It basically adds a null UnlinkedCodeBlock if there isn't a cached entry and then later
updates the null entry to the result of the compilation.  If during that compilation and
related processing we need to garbage collect, the DelayedReleaseScope would drop locks
possibly allowing another thread to try to get the same source out of the CodeCache.
This second thread would find the null entry and crash.  The fix is to move the processing of
DelayedReleaseScope to when we drop locks and not drop locks during GC.  That was done in
the original patch with the new function releaseDelayedReleasedObjects().

Updated releaseDelayedReleasedObjects() so that objects are released with all locks
dropped.  Now its processing follows these steps
    Increment recursion counter and do recursion check and exit if recursing
    While there are objects to release
        ASSERT that lock is held by current thread
        Take all items from delayed release Vector and put into temporary Vector
        Release API lock
        Release and clear items from temporary vector
        Reaquire API lock
This meets the requirement that we release while the API lock is released and it is
safer processing of the delayed release Vector.

Added new regression test to testapi.

Also added comment describing how recursion into releaseDelayedReleasedObjects() is
prevented.

* API/tests/Regress141275.h: Added.
* API/tests/Regress141275.mm: Added.
(+[JSTEvaluatorTask evaluatorTaskWithEvaluateBlock:completionHandler:]):
(-[JSTEvaluator init]):
(-[JSTEvaluator initWithScript:]):
(-[JSTEvaluator _accessPendingTasksWithBlock:]):
(-[JSTEvaluator insertSignPostWithCompletion:]):
(-[JSTEvaluator evaluateScript:completion:]):
(-[JSTEvaluator evaluateBlock:completion:]):
(-[JSTEvaluator waitForTasksDoneAndReportResults]):
(__JSTRunLoopSourceScheduleCallBack):
(__JSTRunLoopSourcePerformCallBack):
(__JSTRunLoopSourceCancelCallBack):
(-[JSTEvaluator _jsThreadMain]):
(-[JSTEvaluator _sourceScheduledOnRunLoop:]):
(-[JSTEvaluator _setupEvaluatorThreadContextIfNeeded]):
(-[JSTEvaluator _callCompletionHandler:ifNeededWithError:]):
(-[JSTEvaluator _sourcePerform]):
(-[JSTEvaluator _sourceCanceledOnRunLoop:]):
(runRegress141275):
* API/tests/testapi.mm:
(testObjectiveCAPI):
* JavaScriptCore.xcodeproj/project.pbxproj:
* heap/Heap.cpp:
(JSC::Heap::releaseDelayedReleasedObjects):
* runtime/JSLock.cpp:
(JSC::JSLock::unlock):</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreAPIteststestapimm">trunk/Source/JavaScriptCore/API/tests/testapi.mm</a></li>
<li><a href="#trunkSourceJavaScriptCoreChangeLog">trunk/Source/JavaScriptCore/ChangeLog</a></li>
<li><a href="#trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj">trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj</a></li>
<li><a href="#trunkSourceJavaScriptCoreheapHeapcpp">trunk/Source/JavaScriptCore/heap/Heap.cpp</a></li>
<li><a href="#trunkSourceJavaScriptCoreruntimeJSLockcpp">trunk/Source/JavaScriptCore/runtime/JSLock.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkSourceJavaScriptCoreAPItestsRegress141275h">trunk/Source/JavaScriptCore/API/tests/Regress141275.h</a></li>
<li><a href="#trunkSourceJavaScriptCoreAPItestsRegress141275mm">trunk/Source/JavaScriptCore/API/tests/Regress141275.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkSourceJavaScriptCoreAPItestsRegress141275h"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/API/tests/Regress141275.h (0 => 180992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/API/tests/Regress141275.h                                (rev 0)
+++ trunk/Source/JavaScriptCore/API/tests/Regress141275.h        2015-03-04 05:33:37 UTC (rev 180992)
</span><span class="lines">@@ -0,0 +1,34 @@
</span><ins>+/*
+ * 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.
+ */
+
+#import &lt;Foundation/Foundation.h&gt;
+#import &lt;JavaScriptCore/JavaScriptCore.h&gt;
+
+#if JSC_OBJC_API_ENABLED
+
+void runRegress141275();
+
+#endif // JSC_OBJC_API_ENABLED
+
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreAPItestsRegress141275mm"></a>
<div class="addfile"><h4>Added: trunk/Source/JavaScriptCore/API/tests/Regress141275.mm (0 => 180992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/API/tests/Regress141275.mm                                (rev 0)
+++ trunk/Source/JavaScriptCore/API/tests/Regress141275.mm        2015-03-04 05:33:37 UTC (rev 180992)
</span><span class="lines">@@ -0,0 +1,388 @@
</span><ins>+/*
+ * 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.
+ */
+
+#import &quot;config.h&quot;
+#import &quot;Regress141275.h&quot;
+
+#import &lt;Foundation/Foundation.h&gt;
+#import &lt;objc/objc.h&gt;
+#import &lt;objc/runtime.h&gt;
+
+#if JSC_OBJC_API_ENABLED
+
+extern &quot;C&quot; void JSSynchronousGarbageCollectForDebugging(JSContextRef);
+
+extern int failed;
+
+static const NSUInteger scriptToEvaluate = 50;
+
+@interface JSTEvaluator : NSObject
+- (instancetype)initWithScript:(NSString*)script;
+
+- (void)insertSignPostWithCompletion:(void(^)(NSError* error))completionHandler;
+
+- (void)evaluateScript:(NSString*)script completion:(void(^)(NSError* error))completionHandler;
+- (void)evaluateBlock:(void(^)(JSContext* context))evaluationBlock completion:(void(^)(NSError* error))completionHandler;
+
+- (void)waitForTasksDoneAndReportResults;
+@end
+
+
+static const NSString* JSTEvaluatorThreadContextKey = @&quot;JSTEvaluatorThreadContextKey&quot;;
+
+/*
+ * A JSTEvaluatorThreadContext is kept in the thread dictionary of threads used by JSEvaluator.
+ *
+ * This includes the run loop thread, and any threads used by _jsSourcePerformQueue to execute a task.
+ */
+@interface JSTEvaluatorThreadContext : NSObject
+@property (weak) JSTEvaluator* evaluator;
+@property (strong) JSContext* jsContext;
+@end
+
+@implementation JSTEvaluatorThreadContext
+@end
+
+
+/*!
+ * A JSTEvaluatorTask is a single task to be executed.
+ *
+ * JSTEvaluator keeps a list of pending tasks. The run loop thread is repsonsible for feeding pending tasks to the _jsSourcePerformQueue, while respecting sign posts.
+ */
+@interface JSTEvaluatorTask : NSObject
+
+@property (nonatomic, copy) void (^evaluateBlock)(JSContext* jsContext);
+@property (nonatomic, copy) void (^completionHandler)(NSError* error);
+@property (nonatomic, copy) NSError* error;
+
++ (instancetype)evaluatorTaskWithEvaluateBlock:(void (^)(JSContext*))block completionHandler:(void (^)(NSError* error))completionBlock;
+
+@end
+
+@implementation JSTEvaluatorTask
+
++ (instancetype)evaluatorTaskWithEvaluateBlock:(void (^)(JSContext*))evaluationBlock completionHandler:(void (^)(NSError* error))completionHandler
+{
+    JSTEvaluatorTask* task = [self new];
+    task.evaluateBlock = evaluationBlock;
+    task.completionHandler = completionHandler;
+    return task;
+}
+
+@end
+
+@implementation JSTEvaluator {
+    dispatch_queue_t _jsSourcePerformQueue;
+    dispatch_semaphore_t _allScriptsDone;
+    CFRunLoopRef _jsThreadRunLoop;
+    CFRunLoopSourceRef _jsThreadRunLoopSource;
+    JSContext* _jsContext;
+    NSMutableArray* __pendingTasks;
+}
+
+- (instancetype)init
+{
+    self = [super init];
+    if (self) {
+        _jsSourcePerformQueue = dispatch_queue_create(&quot;JSTEval&quot;, DISPATCH_QUEUE_CONCURRENT);
+
+        _allScriptsDone = dispatch_semaphore_create(0);
+
+        _jsContext = [JSContext new];
+        _jsContext.name = @&quot;JSTEval&quot;;
+        __pendingTasks = [NSMutableArray new];
+
+        NSThread* jsThread = [[NSThread alloc] initWithTarget:self selector:@selector(_jsThreadMain) object:nil];
+        [jsThread setName:@&quot;JSTEval&quot;];
+        [jsThread start];
+
+    }
+    return self;
+}
+
+- (instancetype)initWithScript:(NSString*)script
+{
+    self = [self init];
+    if (self) {
+        __block NSError* scriptError = nil;
+        dispatch_semaphore_t dsema = dispatch_semaphore_create(0);
+        [self evaluateScript:script
+            completion:^(NSError* error) {
+                scriptError = error;
+                dispatch_semaphore_signal(dsema);
+            }];
+        dispatch_semaphore_wait(dsema, DISPATCH_TIME_FOREVER);
+    }
+    return self;
+}
+
+- (void)_accessPendingTasksWithBlock:(void(^)(NSMutableArray* pendingTasks))block
+{
+    @synchronized(self) {
+        block(__pendingTasks);
+        if (__pendingTasks.count &gt; 0) {
+            if (_jsThreadRunLoop &amp;&amp; _jsThreadRunLoopSource) {
+                CFRunLoopSourceSignal(_jsThreadRunLoopSource);
+                CFRunLoopWakeUp(_jsThreadRunLoop);
+            }
+        }
+    }
+}
+
+- (void)insertSignPostWithCompletion:(void(^)(NSError* error))completionHandler
+{
+    [self _accessPendingTasksWithBlock:^(NSMutableArray* pendingTasks) {
+        JSTEvaluatorTask* task = [JSTEvaluatorTask evaluatorTaskWithEvaluateBlock:nil
+            completionHandler:completionHandler];
+
+        [pendingTasks addObject:task];
+    }];
+}
+
+- (void)evaluateScript:(NSString*)script completion:(void(^)(NSError* error))completionHandler
+{
+    [self evaluateBlock:^(JSContext* context) {
+        [context evaluateScript:script];
+    } completion:completionHandler];
+}
+
+- (void)evaluateBlock:(void(^)(JSContext* context))evaluationBlock completion:(void(^)(NSError* error))completionHandler
+{
+    NSParameterAssert(evaluationBlock != nil);
+    [self _accessPendingTasksWithBlock:^(NSMutableArray* pendingTasks) {
+        JSTEvaluatorTask* task = [JSTEvaluatorTask evaluatorTaskWithEvaluateBlock:evaluationBlock
+            completionHandler:completionHandler];
+
+        [pendingTasks addObject:task];
+    }];
+}
+
+- (void)waitForTasksDoneAndReportResults
+{
+    NSString* passFailString = @&quot;PASSED&quot;;
+
+    if (!dispatch_semaphore_wait(_allScriptsDone, dispatch_time(DISPATCH_TIME_NOW, 30 * NSEC_PER_SEC))) {
+        int totalScriptsRun = [_jsContext[@&quot;counter&quot;] toInt32];
+
+        if (totalScriptsRun != scriptToEvaluate) {
+            passFailString = @&quot;FAILED&quot;;
+            failed = 1;
+        }
+
+        NSLog(@&quot;  Ran a total of %d scripts: %@&quot;, totalScriptsRun, passFailString);
+    } else {
+        passFailString = @&quot;FAILED&quot;;
+        failed = 1;
+        NSLog(@&quot;  Error, timeout waiting for all tasks to complete: %@&quot;, passFailString);
+    }
+}
+
+static void __JSTRunLoopSourceScheduleCallBack(void* info, CFRunLoopRef rl, CFStringRef)
+{
+    @autoreleasepool {
+        [(__bridge JSTEvaluator*)info _sourceScheduledOnRunLoop:rl];
+    }
+}
+
+static void __JSTRunLoopSourcePerformCallBack(void* info )
+{
+    @autoreleasepool {
+        [(__bridge JSTEvaluator*)info _sourcePerform];
+    }
+}
+
+static void __JSTRunLoopSourceCancelCallBack(void* info, CFRunLoopRef rl, CFStringRef)
+{
+    @autoreleasepool {
+        [(__bridge JSTEvaluator*)info _sourceCanceledOnRunLoop:rl];
+    }
+}
+
+- (void)_jsThreadMain
+{
+    @autoreleasepool {
+        const CFIndex kRunLoopSourceContextVersion = 0;
+        CFRunLoopSourceContext sourceContext = {
+            kRunLoopSourceContextVersion, (__bridge void*)(self),
+            NULL, NULL, NULL, NULL, NULL,
+            __JSTRunLoopSourceScheduleCallBack,
+            __JSTRunLoopSourceCancelCallBack,
+            __JSTRunLoopSourcePerformCallBack
+        };
+
+        @synchronized(self) {
+            _jsThreadRunLoop = CFRunLoopGetCurrent();
+            CFRetain(_jsThreadRunLoop);
+
+            _jsThreadRunLoopSource = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &amp;sourceContext);
+            CFRunLoopAddSource(_jsThreadRunLoop, _jsThreadRunLoopSource, kCFRunLoopDefaultMode);
+        }
+
+        CFRunLoopRun();
+
+        @synchronized(self) {
+            NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
+            [threadDict removeObjectForKey:threadDict[JSTEvaluatorThreadContextKey]];
+
+            CFRelease(_jsThreadRunLoopSource);
+            _jsThreadRunLoopSource = NULL;
+
+            CFRelease(_jsThreadRunLoop);
+            _jsThreadRunLoop = NULL;
+
+            __pendingTasks = nil;
+        }
+    }
+}
+
+- (void)_sourceScheduledOnRunLoop:(CFRunLoopRef)runLoop
+{
+    UNUSED_PARAM(runLoop);
+    assert([[[NSThread currentThread] name] isEqualToString:@&quot;JSTEval&quot;]);
+
+    // Wake up the run loop in case requests were submitted prior to the
+    // run loop &amp; run loop source getting created.
+    CFRunLoopSourceSignal(_jsThreadRunLoopSource);
+    CFRunLoopWakeUp(_jsThreadRunLoop);
+}
+
+- (void)_setupEvaluatorThreadContextIfNeeded
+{
+    NSMutableDictionary* threadDict = [[NSThread currentThread] threadDictionary];
+    JSTEvaluatorThreadContext* context = threadDict[JSTEvaluatorThreadContextKey];
+    // The evaluator may be other evualuator, or nil if this thread has not been used before. Eaither way take ownership.
+    if (context.evaluator != self) {
+        context = [JSTEvaluatorThreadContext new];
+        context.evaluator = self;
+        threadDict[JSTEvaluatorThreadContextKey] = context;
+    }
+}
+
+- (void)_callCompletionHandler:(void(^)(NSError* error))completionHandler ifNeededWithError:(NSError*)error
+{
+    if (completionHandler) {
+        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+            completionHandler(error);
+        });
+    }
+}
+
+- (void)_sourcePerform
+{
+    assert([[[NSThread currentThread] name] isEqualToString:@&quot;JSTEval&quot;]);
+
+    __block NSArray* tasks = nil;
+    [self _accessPendingTasksWithBlock:^(NSMutableArray* pendingTasks) {
+        // No signpost, take all tasks.
+        tasks = [pendingTasks copy];
+        [pendingTasks removeAllObjects];
+    }];
+
+    if (tasks.count &gt; 0) {
+        for (JSTEvaluatorTask* task in tasks) {
+            dispatch_block_t block = ^{
+                NSError* error = nil;
+                if (task.evaluateBlock) {
+                    [self _setupEvaluatorThreadContextIfNeeded];
+                    task.evaluateBlock(_jsContext);
+                    if (_jsContext.exception) {
+                        NSLog(@&quot;Did fail on JSContext: %@&quot;, _jsContext.name);
+                        NSDictionary* userInfo = @{ NSLocalizedDescriptionKey : [_jsContext.exception[@&quot;message&quot;] toString] };
+                        error = [NSError errorWithDomain:@&quot;JSTEvaluator&quot; code:1 userInfo:userInfo];
+                        _jsContext.exception = nil;
+                    }
+                }
+                [self _callCompletionHandler:task.completionHandler ifNeededWithError:error];
+            };
+
+            if (task.evaluateBlock)
+                dispatch_async(_jsSourcePerformQueue, block);
+            else
+                dispatch_barrier_async(_jsSourcePerformQueue, block);
+        }
+
+        dispatch_barrier_sync(_jsSourcePerformQueue, ^{
+            if ([_jsContext[@&quot;counter&quot;] toInt32] == scriptToEvaluate)
+                dispatch_semaphore_signal(_allScriptsDone);
+        });
+    }
+}
+
+- (void)_sourceCanceledOnRunLoop:(CFRunLoopRef)runLoop
+{
+    UNUSED_PARAM(runLoop);
+    assert([[[NSThread currentThread] name] isEqualToString:@&quot;JSTEval&quot;]);
+
+    @synchronized(self) {
+        assert(_jsThreadRunLoop);
+        assert(_jsThreadRunLoopSource);
+
+        CFRunLoopRemoveSource(_jsThreadRunLoop, _jsThreadRunLoopSource, kCFRunLoopDefaultMode);
+        CFRunLoopStop(_jsThreadRunLoop);
+    }
+}
+
+@end
+
+void runRegress141275()
+{
+    // Test that we can execute the same script from multiple threads with a shared context.
+    // See &lt;https://webkit.org/b/141275&gt;
+    NSLog(@&quot;TEST: Testing multiple threads executing the same script with a shared context&quot;);
+
+    @autoreleasepool {
+        JSTEvaluator* evaluator = [[JSTEvaluator alloc] initWithScript:@&quot;this['counter'] = 0;&quot;];
+
+        void (^showErrorIfNeeded)(NSError* error) = ^(NSError* error) {
+            if (error) {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSLog(@&quot;Error: %@&quot;, error);
+                });
+            }
+        };
+
+        [evaluator evaluateBlock:^(JSContext* context) {
+            JSSynchronousGarbageCollectForDebugging([context JSGlobalContextRef]);
+        } completion:showErrorIfNeeded];
+
+        [evaluator evaluateBlock:^(JSContext* context) {
+            context[@&quot;wait&quot;] = ^{
+                [NSThread sleepForTimeInterval:0.01];
+            };
+        } completion:^(NSError* error) {
+            if (error) {
+                dispatch_async(dispatch_get_main_queue(), ^{
+                    NSLog(@&quot;Error: %@&quot;, error);
+                });
+            }
+            for (unsigned i = 0; i &lt; scriptToEvaluate; i++)
+                [evaluator evaluateScript:@&quot;this['counter']++; this['wait']();&quot; completion:showErrorIfNeeded];
+        }];
+
+        [evaluator waitForTasksDoneAndReportResults];
+    }
+}
+
+#endif // JSC_OBJC_API_ENABLED
</ins></span></pre></div>
<a id="trunkSourceJavaScriptCoreAPIteststestapimm"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/API/tests/testapi.mm (180991 => 180992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/API/tests/testapi.mm        2015-03-04 05:31:23 UTC (rev 180991)
+++ trunk/Source/JavaScriptCore/API/tests/testapi.mm        2015-03-04 05:33:37 UTC (rev 180992)
</span><span class="lines">@@ -28,6 +28,7 @@
</span><span class="cx"> #import &quot;CurrentThisInsideBlockGetterTest.h&quot;
</span><span class="cx"> #import &quot;DateTests.h&quot;
</span><span class="cx"> #import &quot;JSExportTests.h&quot;
</span><ins>+#import &quot;Regress141275.h&quot;
</ins><span class="cx"> #import &quot;Regress141809.h&quot;
</span><span class="cx"> 
</span><span class="cx"> #import &lt;pthread.h&gt;
</span><span class="lines">@@ -486,7 +487,6 @@
</span><span class="cx"> void testObjectiveCAPI()
</span><span class="cx"> {
</span><span class="cx">     NSLog(@&quot;Testing Objective-C API&quot;);
</span><del>-
</del><span class="cx">     @autoreleasepool {
</span><span class="cx">         JSVirtualMachine* vm = [[JSVirtualMachine alloc] init];
</span><span class="cx">         JSContext* context = [[JSContext alloc] initWithVirtualMachine:vm];
</span><span class="lines">@@ -1386,6 +1386,7 @@
</span><span class="cx">     currentThisInsideBlockGetterTest();
</span><span class="cx">     runDateTests();
</span><span class="cx">     runJSExportTests();
</span><ins>+    runRegress141275();
</ins><span class="cx">     runRegress141809();
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/ChangeLog (180991 => 180992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/ChangeLog        2015-03-04 05:31:23 UTC (rev 180991)
+++ trunk/Source/JavaScriptCore/ChangeLog        2015-03-04 05:33:37 UTC (rev 180992)
</span><span class="lines">@@ -1,3 +1,64 @@
</span><ins>+2015-03-03  Michael Saboff  &lt;msaboff@apple.com&gt;
+
+        DelayedReleaseScope drops locks during GC which can cause a thread switch and code reentry
+        https://bugs.webkit.org/show_bug.cgi?id=141275
+
+        Reviewed by Geoffrey Garen.
+
+        The original issue is that the CodeCache uses an unsafe method to add new UnlinkedCodeBlocks.
+        It basically adds a null UnlinkedCodeBlock if there isn't a cached entry and then later
+        updates the null entry to the result of the compilation.  If during that compilation and
+        related processing we need to garbage collect, the DelayedReleaseScope would drop locks
+        possibly allowing another thread to try to get the same source out of the CodeCache.
+        This second thread would find the null entry and crash.  The fix is to move the processing of
+        DelayedReleaseScope to when we drop locks and not drop locks during GC.  That was done in
+        the original patch with the new function releaseDelayedReleasedObjects().
+
+        Updated releaseDelayedReleasedObjects() so that objects are released with all locks
+        dropped.  Now its processing follows these steps
+            Increment recursion counter and do recursion check and exit if recursing
+            While there are objects to release
+                ASSERT that lock is held by current thread
+                Take all items from delayed release Vector and put into temporary Vector
+                Release API lock
+                Release and clear items from temporary vector
+                Reaquire API lock
+        This meets the requirement that we release while the API lock is released and it is
+        safer processing of the delayed release Vector.
+
+        Added new regression test to testapi.
+
+        Also added comment describing how recursion into releaseDelayedReleasedObjects() is
+        prevented.
+
+        * API/tests/Regress141275.h: Added.
+        * API/tests/Regress141275.mm: Added.
+        (+[JSTEvaluatorTask evaluatorTaskWithEvaluateBlock:completionHandler:]):
+        (-[JSTEvaluator init]):
+        (-[JSTEvaluator initWithScript:]):
+        (-[JSTEvaluator _accessPendingTasksWithBlock:]):
+        (-[JSTEvaluator insertSignPostWithCompletion:]):
+        (-[JSTEvaluator evaluateScript:completion:]):
+        (-[JSTEvaluator evaluateBlock:completion:]):
+        (-[JSTEvaluator waitForTasksDoneAndReportResults]):
+        (__JSTRunLoopSourceScheduleCallBack):
+        (__JSTRunLoopSourcePerformCallBack):
+        (__JSTRunLoopSourceCancelCallBack):
+        (-[JSTEvaluator _jsThreadMain]):
+        (-[JSTEvaluator _sourceScheduledOnRunLoop:]):
+        (-[JSTEvaluator _setupEvaluatorThreadContextIfNeeded]):
+        (-[JSTEvaluator _callCompletionHandler:ifNeededWithError:]):
+        (-[JSTEvaluator _sourcePerform]):
+        (-[JSTEvaluator _sourceCanceledOnRunLoop:]):
+        (runRegress141275):
+        * API/tests/testapi.mm:
+        (testObjectiveCAPI):
+        * JavaScriptCore.xcodeproj/project.pbxproj:
+        * heap/Heap.cpp:
+        (JSC::Heap::releaseDelayedReleasedObjects):
+        * runtime/JSLock.cpp:
+        (JSC::JSLock::unlock):
+
</ins><span class="cx"> 2015-03-03  Filip Pizlo  &lt;fpizlo@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         DFG should constant fold GetScope, and accesses to the scope register in the ByteCodeParser should not pretend that it's a constant as that breaks OSR exit liveness tracking
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreJavaScriptCorexcodeprojprojectpbxproj"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj (180991 => 180992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2015-03-04 05:31:23 UTC (rev 180991)
+++ trunk/Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj        2015-03-04 05:33:37 UTC (rev 180992)
</span><span class="lines">@@ -934,6 +934,7 @@
</span><span class="cx">                 65525FC51A6DD801007B5495 /* NullSetterFunction.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 65525FC31A6DD3B3007B5495 /* NullSetterFunction.cpp */; };
</span><span class="cx">                 6553A33117A1F1EE008CF6F3 /* CommonSlowPathsExceptions.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 6553A32F17A1F1EE008CF6F3 /* CommonSlowPathsExceptions.cpp */; };
</span><span class="cx">                 6553A33217A1F1EE008CF6F3 /* CommonSlowPathsExceptions.h in Headers */ = {isa = PBXBuildFile; fileRef = 6553A33017A1F1EE008CF6F3 /* CommonSlowPathsExceptions.h */; };
</span><ins>+                65570F5A1AA4C3EA009B3C23 /* Regress141275.mm in Sources */ = {isa = PBXBuildFile; fileRef = 65570F591AA4C00A009B3C23 /* Regress141275.mm */; };
</ins><span class="cx">                 655EB29B10CE2581001A990E /* NodesCodegen.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 655EB29A10CE2581001A990E /* NodesCodegen.cpp */; };
</span><span class="cx">                 657CF45819BF6662004ACBF2 /* JSCallee.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 657CF45619BF6662004ACBF2 /* JSCallee.cpp */; };
</span><span class="cx">                 657CF45919BF6662004ACBF2 /* JSCallee.h in Headers */ = {isa = PBXBuildFile; fileRef = 657CF45719BF6662004ACBF2 /* JSCallee.h */; settings = {ATTRIBUTES = (Private, ); }; };
</span><span class="lines">@@ -2597,6 +2598,8 @@
</span><span class="cx">                 65525FC41A6DD3B3007B5495 /* NullSetterFunction.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NullSetterFunction.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 6553A32F17A1F1EE008CF6F3 /* CommonSlowPathsExceptions.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CommonSlowPathsExceptions.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 6553A33017A1F1EE008CF6F3 /* CommonSlowPathsExceptions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonSlowPathsExceptions.h; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><ins>+                65570F581AA4C00A009B3C23 /* Regress141275.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Regress141275.h; path = API/tests/Regress141275.h; sourceTree = &quot;&lt;group&gt;&quot;; };
+                65570F591AA4C00A009B3C23 /* Regress141275.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = Regress141275.mm; path = API/tests/Regress141275.mm; sourceTree = &quot;&lt;group&gt;&quot;; };
</ins><span class="cx">                 655EB29A10CE2581001A990E /* NodesCodegen.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = NodesCodegen.cpp; sourceTree = &quot;&lt;group&gt;&quot;; };
</span><span class="cx">                 6560A4CF04B3B3E7008AE952 /* CoreFoundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreFoundation.framework; path = /System/Library/Frameworks/CoreFoundation.framework; sourceTree = &quot;&lt;absolute&gt;&quot;; };
</span><span class="cx">                 65621E6B089E859700760F35 /* PropertySlot.cpp */ = {isa = PBXFileReference; fileEncoding = 30; indentWidth = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PropertySlot.cpp; sourceTree = &quot;&lt;group&gt;&quot;; tabWidth = 8; };
</span><span class="lines">@@ -3728,6 +3731,8 @@
</span><span class="cx">                                 C288B2DD18A54D3E007BE40B /* DateTests.mm */,
</span><span class="cx">                                 C2181FC018A948FB0025A235 /* JSExportTests.h */,
</span><span class="cx">                                 C2181FC118A948FB0025A235 /* JSExportTests.mm */,
</span><ins>+                                65570F581AA4C00A009B3C23 /* Regress141275.h */,
+                                65570F591AA4C00A009B3C23 /* Regress141275.mm */,
</ins><span class="cx">                                 FEB51F6A1A97B688001F921C /* Regress141809.h */,
</span><span class="cx">                                 FEB51F6B1A97B688001F921C /* Regress141809.mm */,
</span><span class="cx">                                 144005170A531CB50005F061 /* minidom */,
</span><span class="lines">@@ -6757,6 +6762,7 @@
</span><span class="cx">                         isa = PBXSourcesBuildPhase;
</span><span class="cx">                         buildActionMask = 2147483647;
</span><span class="cx">                         files = (
</span><ins>+                                65570F5A1AA4C3EA009B3C23 /* Regress141275.mm in Sources */,
</ins><span class="cx">                                 C29ECB031804D0ED00D2CBB4 /* CurrentThisInsideBlockGetterTest.mm in Sources */,
</span><span class="cx">                                 FEB51F6C1A97B688001F921C /* Regress141809.mm in Sources */,
</span><span class="cx">                                 C20328201981979D0088B499 /* CustomGlobalObjectClassTest.c in Sources */,
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreheapHeapcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/heap/Heap.cpp (180991 => 180992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/heap/Heap.cpp        2015-03-04 05:31:23 UTC (rev 180991)
+++ trunk/Source/JavaScriptCore/heap/Heap.cpp        2015-03-04 05:33:37 UTC (rev 180992)
</span><span class="lines">@@ -368,10 +368,25 @@
</span><span class="cx"> void Heap::releaseDelayedReleasedObjects()
</span><span class="cx"> {
</span><span class="cx"> #if USE(CF)
</span><ins>+    // We need to guard against the case that releasing an object can create more objects due to the
+    // release calling into JS. When those JS call(s) exit and all locks are being dropped we end up
+    // back here and could try to recursively release objects. We guard that with a recursive entry
+    // count. Only the initial call will release objects, recursive calls simple return and let the
+    // the initial call to the function take care of any objects created during release time.
+    // This also means that we need to loop until there are no objects in m_delayedReleaseObjects
+    // and use a temp Vector for the actual releasing.
</ins><span class="cx">     if (!m_delayedReleaseRecursionCount++) {
</span><span class="cx">         while (!m_delayedReleaseObjects.isEmpty()) {
</span><del>-            RetainPtr&lt;CFTypeRef&gt; objectToRelease = m_delayedReleaseObjects.takeLast();
-            objectToRelease.clear();
</del><ins>+            ASSERT(m_vm-&gt;currentThreadIsHoldingAPILock());
+
+            Vector&lt;RetainPtr&lt;CFTypeRef&gt;&gt; objectsToRelease = WTF::move(m_delayedReleaseObjects);
+
+            {
+                // We need to drop locks before calling out to arbitrary code.
+                JSLock::DropAllLocks dropAllLocks(m_vm);
+
+                objectsToRelease.clear();
+            }
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx">     m_delayedReleaseRecursionCount--;
</span></span></pre></div>
<a id="trunkSourceJavaScriptCoreruntimeJSLockcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/JavaScriptCore/runtime/JSLock.cpp (180991 => 180992)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/JavaScriptCore/runtime/JSLock.cpp        2015-03-04 05:31:23 UTC (rev 180991)
+++ trunk/Source/JavaScriptCore/runtime/JSLock.cpp        2015-03-04 05:33:37 UTC (rev 180992)
</span><span class="lines">@@ -157,10 +157,14 @@
</span><span class="cx">     RELEASE_ASSERT(currentThreadIsHoldingLock());
</span><span class="cx">     ASSERT(m_lockCount &gt;= unlockCount);
</span><span class="cx"> 
</span><ins>+    // Maintain m_lockCount while calling willReleaseLock() so that its callees know that
+    // they still have the lock.
+    if (unlockCount == m_lockCount)
+        willReleaseLock();
+
</ins><span class="cx">     m_lockCount -= unlockCount;
</span><span class="cx"> 
</span><span class="cx">     if (!m_lockCount) {
</span><del>-        willReleaseLock();
</del><span class="cx"> 
</span><span class="cx">         if (!m_hasExclusiveThread) {
</span><span class="cx">             m_ownerThreadID = std::thread::id();
</span></span></pre>
</div>
</div>

</body>
</html>