Reviewed by Darin.
Disable JSLock for per-thread contexts.
No change on SunSpider.
* kjs/JSGlobalData.h:
* kjs/JSGlobalData.cpp:
(KJS::JSGlobalData::JSGlobalData):
(KJS::JSGlobalData::sharedInstance):
Added isSharedInstance as a better way to tell whether the instance is shared (legacy).
* kjs/JSLock.cpp:
(KJS::createJSLockCount):
(KJS::JSLock::lockCount):
(KJS::setLockCount):
(KJS::JSLock::JSLock):
(KJS::JSLock::lock):
(KJS::JSLock::unlock):
(KJS::JSLock::currentThreadIsHoldingLock):
(KJS::JSLock::DropAllLocks::DropAllLocks):
(KJS::JSLock::DropAllLocks::~DropAllLocks):
* kjs/JSLock.h:
(KJS::JSLock::JSLock):
(KJS::JSLock::~JSLock):
Made JSLock and JSLock::DropAllLocks constructors take a parameter to decide whether to
actually lock a mutex, or only to increment recursion count. We cannot turn it into no-op
if we want to keep existing assertions working.
Made recursion count per-thread, now that locks may not lock.
* API/JSBase.cpp:
(JSEvaluateScript): Take JSLock after casting JSContextRef to ExecState* (which doesn't need
locking in any case), so that a decision whether to actually lock can be made.
(JSCheckScriptSyntax): Ditto.
(JSGarbageCollect): Only lock while collecting the shared heap, not the per-thread one.
* API/JSObjectRef.cpp:
(JSClassCreate): Don't lock, as there is no reason to.
(JSClassRetain): Ditto.
(JSClassRelease): Ditto.
(JSPropertyNameArrayRetain): Ditto.
(JSPropertyNameArrayRelease): Only lock while deleting the array, as that may touch
identifier table.
(JSPropertyNameAccumulatorAddName): Adding a string also involves an identifier table
lookup, and possibly modification.
* API/JSStringRef.cpp:
(JSStringCreateWithCharacters):
(JSStringCreateWithUTF8CString):
(JSStringRetain):
(JSStringRelease):
(JSStringGetUTF8CString):
(JSStringIsEqual):
* API/JSStringRefCF.cpp:
(JSStringCreateWithCFString):
JSStringRef operations other than releasing do not need locking.
* VM/Machine.cpp: Don't include unused JSLock.h.
* kjs/CollectorHeapIntrospector.cpp: (KJS::CollectorHeapIntrospector::statistics):
Don't take the lock for real, as heap introspection pauses the process anyway. It seems that
the existing code could cause deadlocks.
* kjs/Shell.cpp:
(functionGC):
(main):
(jscmain):
The test tool uses a per-thread context, so no real locking is required.
* kjs/collector.h:
(KJS::Heap::setGCProtectNeedsLocking): Optionally protect m_protectedValues access with a
per-heap mutex. This is only needed for WebCore Database code, which violates the "no data
migration between threads" by using ProtectedPtr on a background thread.
(KJS::Heap::isShared): Keep a shared flag here, as well.
* kjs/protect.h:
(KJS::::ProtectedPtr):
(KJS::::~ProtectedPtr):
(KJS::::operator):
(KJS::operator==):
(KJS::operator!=):
ProtectedPtr is ony used from WebCore, so it doesn't need to take JSLock. An assertion in
Heap::protect/unprotect guards agains possible future unlocked uses of ProtectedPtr in JSC.
* kjs/collector.cpp:
(KJS::Heap::Heap): Initialize m_isShared.
(KJS::Heap::~Heap): No need to lock for real during destruction, but must keep assertions
in sweep() working.
(KJS::destroyRegisteredThread): Registered thread list is only accessed for shared heap,
so locking is always needed here.
(KJS::Heap::registerThread): Ditto.
(KJS::Heap::markStackObjectsConservatively): Use m_isShared instead of comparing to a shared
instance for a small speedup.
(KJS::Heap::setGCProtectNeedsLocking): Create m_protectedValuesMutex. There is currently no
way to undo this - and ideally, Database code will be fixed to lo longer require this quirk.
(KJS::Heap::protect): Take m_protectedValuesMutex (if it exists) while accessing
m_protectedValues.
(KJS::Heap::unprotect): Ditto.
(KJS::Heap::markProtectedObjects): Ditto.
(KJS::Heap::protectedGlobalObjectCount): Ditto.
(KJS::Heap::protectedObjectCount): Ditto.
(KJS::Heap::protectedObjectTypeCounts): Ditto.
* kjs/ustring.cpp:
* kjs/ustring.h:
Don't include JSLock.h, which is no longer used here. As a result, an explicit include had
to be added to many files in JavaScriptGlue, WebCore and WebKit.
* kjs/JSGlobalObject.cpp:
(KJS::JSGlobalObject::init):
* API/JSCallbackConstructor.cpp:
(KJS::constructJSCallback):
* API/JSCallbackFunction.cpp:
(KJS::JSCallbackFunction::call):
* API/JSCallbackObjectFunctions.h:
(KJS::::init):
(KJS::::getOwnPropertySlot):
(KJS::::put):
(KJS::::deleteProperty):
(KJS::::construct):
(KJS::::hasInstance):
(KJS::::call):
(KJS::::getPropertyNames):
(KJS::::toNumber):
(KJS::::toString):
(KJS::::staticValueGetter):
(KJS::::callbackGetter):
* API/JSContextRef.cpp:
(JSGlobalContextCreate):
(JSGlobalContextRetain):
(JSGlobalContextRelease):
* API/JSValueRef.cpp:
(JSValueIsEqual):
(JSValueIsStrictEqual):
(JSValueIsInstanceOfConstructor):
(JSValueMakeNumber):
(JSValueMakeString):
(JSValueToNumber):
(JSValueToStringCopy):
(JSValueToObject):
(JSValueProtect):
(JSValueUnprotect):
* JavaScriptCore.exp:
* kjs/PropertyNameArray.h:
(KJS::PropertyNameArray::globalData):
* kjs/interpreter.cpp:
(KJS::Interpreter::checkSyntax):
(KJS::Interpreter::evaluate):
Pass a parameter to JSLock/JSLock::DropAllLocks to decide whether the lock needs to be taken.
git-svn-id: http://svn.webkit.org/repository/webkit/trunk@34947 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/JavaScriptCore/kjs/collector.cpp b/JavaScriptCore/kjs/collector.cpp
index e3846c9..65b6bf5 100644
--- a/JavaScriptCore/kjs/collector.cpp
+++ b/JavaScriptCore/kjs/collector.cpp
@@ -23,6 +23,7 @@
#include "ExecState.h"
#include "JSGlobalObject.h"
+#include "JSLock.h"
#include "JSString.h"
#include "JSValue.h"
#include "list.h"
@@ -90,8 +91,9 @@
static void freeHeap(CollectorHeap*);
-Heap::Heap()
+Heap::Heap(bool isShared)
: m_markListSet(0)
+ , m_isShared(isShared)
{
memset(&primaryHeap, 0, sizeof(CollectorHeap));
memset(&numberHeap, 0, sizeof(CollectorHeap));
@@ -99,7 +101,7 @@
Heap::~Heap()
{
- JSLock lock;
+ JSLock lock(false);
delete m_markListSet;
sweep<PrimaryHeap>();
@@ -438,7 +440,9 @@
// Can't use JSLock convenience object here because we don't want to re-register
// an exiting thread.
- JSLock::lock();
+ // JSLock is only used for code simplicity here, as we only need to protect registeredThreads
+ // list manipulation, not JS data structures.
+ JSLock::lock(true);
if (registeredThreads == thread) {
registeredThreads = registeredThreads->next;
@@ -455,7 +459,7 @@
ASSERT(t); // If t is NULL, we never found ourselves in the list.
}
- JSLock::unlock();
+ JSLock::unlock(true);
delete thread;
}
@@ -467,6 +471,7 @@
void Heap::registerThread()
{
+ // Since registerThread() is only called when using a shared heap, these locks will be real.
ASSERT(JSLock::lockCount() > 0);
ASSERT(JSLock::currentThreadIsHoldingLock());
@@ -728,7 +733,7 @@
#if USE(MULTIPLE_THREADS)
- if (this == JSGlobalData::sharedInstance().heap) {
+ if (m_isShared) {
#ifndef NDEBUG
// Forbid malloc during the mark phase. Marking a thread suspends it, so
@@ -736,6 +741,8 @@
// suspended while holding the malloc lock.
fastMallocForbid();
#endif
+ // It is safe to access the registeredThreads list, because we earlier asserted that locks are being held,
+ // and since this is a shared heap, they are real locks.
for (Thread* thread = registeredThreads; thread != NULL; thread = thread->next) {
if (!pthread_equal(thread->posixThread, pthread_self()))
markOtherThreadConservatively(thread);
@@ -747,28 +754,48 @@
#endif
}
+void Heap::setGCProtectNeedsLocking()
+{
+ // Most clients do not need to call this, with the notable exception of WebCore.
+ // Clients that use shared heap have JSLock protection, while others are not supposed
+ // to migrate JS objects between threads. WebCore violates this contract in Database code,
+ // which calls gcUnprotect from a secondary thread.
+ if (!m_protectedValuesMutex)
+ m_protectedValuesMutex.set(new Mutex);
+}
+
void Heap::protect(JSValue* k)
{
ASSERT(k);
- ASSERT(JSLock::lockCount() > 0);
- ASSERT(JSLock::currentThreadIsHoldingLock());
+ ASSERT(JSLock::currentThreadIsHoldingLock() || !m_isShared);
if (JSImmediate::isImmediate(k))
return;
- protectedValues.add(k->asCell());
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ m_protectedValues.add(k->asCell());
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
}
void Heap::unprotect(JSValue* k)
{
ASSERT(k);
- ASSERT(JSLock::lockCount() > 0);
- ASSERT(JSLock::currentThreadIsHoldingLock());
+ ASSERT(JSLock::currentThreadIsHoldingLock() || !m_isShared);
if (JSImmediate::isImmediate(k))
return;
- protectedValues.remove(k->asCell());
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ m_protectedValues.remove(k->asCell());
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
}
Heap* Heap::heap(const JSValue* v)
@@ -780,12 +807,18 @@
void Heap::markProtectedObjects()
{
- ProtectCountSet::iterator end = protectedValues.end();
- for (ProtectCountSet::iterator it = protectedValues.begin(); it != end; ++it) {
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ ProtectCountSet::iterator end = m_protectedValues.end();
+ for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it) {
JSCell* val = it->first;
if (!val->marked())
val->mark();
}
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
}
template <Heap::HeapType heapType> size_t Heap::sweep()
@@ -940,21 +973,36 @@
size_t Heap::protectedGlobalObjectCount()
{
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
size_t count = 0;
if (JSGlobalObject* head = JSGlobalData::threadInstance().head) {
JSGlobalObject* o = head;
do {
- if (protectedValues.contains(o))
+ if (m_protectedValues.contains(o))
++count;
o = o->next();
} while (o != head);
}
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
+
return count;
}
size_t Heap::protectedObjectCount()
{
- return protectedValues.size();
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ size_t result = m_protectedValues.size();
+
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
+
+ return result;
}
static const char* typeName(JSCell* val)
@@ -994,10 +1042,16 @@
{
HashCountedSet<const char*>* counts = new HashCountedSet<const char*>;
- ProtectCountSet::iterator end = protectedValues.end();
- for (ProtectCountSet::iterator it = protectedValues.begin(); it != end; ++it)
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->lock();
+
+ ProtectCountSet::iterator end = m_protectedValues.end();
+ for (ProtectCountSet::iterator it = m_protectedValues.begin(); it != end; ++it)
counts->add(typeName(it->first));
+ if (m_protectedValuesMutex)
+ m_protectedValuesMutex->unlock();
+
return counts;
}