Ignore:
Timestamp:
Mar 11, 2007, 4:57:11 PM (18 years ago)
Author:
ggaren
Message:

JavaScriptCore:

Reviewed by Darin Adler.

Fixed <rdar://problem/4587763> PAC file: lock inversion between QT and
JSCore causes a hang @ www.panoramas.dk


With a PAC file, run-webkit-tests --threaded passes, the reported site
works, and all the Quicktime/JavaScript and Flash/JavaScript examples
I found through Google work, too.


Any time JavaScript causes arbitrary non-JavaScript code to execute, it
risks deadlock, because that code may block, trying to acquire a lock
owned by a thread that is waiting to execute JavaScript. In this case,
the thread was a networking thread that was waiting to interpret a PAC file.


Because non-JavaScript code may execute in response to, well, anything,
a perfect solution to this problem is impossible. I've implemented an
optimistic solution, instead: JavaScript will drop its lock whenever it
makes a direct call to non-JavaScript code through a bridging/plug-in API,
but will blissfully ignore the indirect ways it may cause non-JavaScript
code to run (resizing a window, for example).


Unfortunately, this solution introduces significant locking overhead in
the bridging APIs. I don't see a way around that.

This patch includes some distinct bug fixes I saw along the way:


  • bindings/objc/objc_instance.mm: Fixed a bug where a nested begin() call would leak its autorelease pool, because it would NULL out _pool without draining it.
  • bindings/runtime_object.cpp: (RuntimeObjectImp::methodGetter): Don't copy an Identifier to ASCII only to turn around and make an Identifier from the ASCII. In an earlier version of this patch, the copy caused an assertion failure. Now it's just unnecessary work. (RuntimeObjectImp::getOwnPropertySlot): ditto
  • bindings/objc/objc_instance.h: Removed overrides of setVAlueOfField and getValueOfField, because they did exactly what the base class versions did. Removed overrides of Noncopyable declarations for the same reason.
  • bindings/runtime.h: Inherit from Noncopyable instead of rolling our own.
  • bindings/c/c_instance.h: ditto

And the actual patch:


  • API/JSCallbackConstructor.cpp: Drop all locks when calling out to C. (KJS::JSCallbackConstructor::construct):
  • API/JSCallbackFunction.cpp: ditto (KJS::JSCallbackFunction::callAsFunction):
  • API/JSCallbackObject.cpp: ditto (KJS::JSCallbackObject::init): (KJS::JSCallbackObject::~JSCallbackObject): (KJS::JSCallbackObject::getOwnPropertySlot): (KJS::JSCallbackObject::put): (KJS::JSCallbackObject::deleteProperty): (KJS::JSCallbackObject::construct): (KJS::JSCallbackObject::hasInstance): (KJS::JSCallbackObject::callAsFunction): (KJS::JSCallbackObject::getPropertyNames): (KJS::JSCallbackObject::toNumber): (KJS::JSCallbackObject::toString): (KJS::JSCallbackObject::staticValueGetter): (KJS::JSCallbackObject::callbackGetter):


  • bindings/c/c_instance.cpp: Drop all locks when calling out to C. (KJS::Bindings::CInstance::invokeMethod): (KJS::Bindings::CInstance::invokeDefaultMethod):
  • bindings/c/c_runtime.cpp: Drop all locks when calling out to C. (KJS::Bindings::CField::valueFromInstance): (KJS::Bindings::CField::setValueToInstance):
  • bindings/jni/jni_objc.mm: (KJS::Bindings::dispatchJNICall): Drop all locks when calling out to Java.
  • bindings/objc/objc_instance.mm: The changes here are to accomodate the fact that C++ unwinding of DropAllLocks goes crazy when you put it inside a @try block. I moved all JavaScript stuff outside of the @try blocks, and then prefixed the whole blocks with DropAllLocks objects. This required some supporting changes in other functions, which now acquire the JSLock for themselves, intead of relying on their callers to do so. (ObjcInstance::end): (ObjcInstance::invokeMethod): (ObjcInstance::invokeDefaultMethod): (ObjcInstance::setValueOfUndefinedField): (ObjcInstance::getValueOfUndefinedField):
  • bindings/objc/objc_runtime.mm: Same as above, except I didn't want to change throwError to acquire the JSLock for itself. (ObjcField::valueFromInstance): (ObjcField::setValueToInstance):
  • bindings/objc/objc_utility.mm: Supporting changes mentioned above. (KJS::Bindings::convertValueToObjcValue): (KJS::Bindings::convertObjcValueToValue):
  • kjs/JSLock.cpp: (1) Fixed DropAllLocks to behave as advertised, and drop the JSLock only if the current thread actually acquired it in the first place. This is important because WebKit needs to ensure that the JSLock has been dropped before it makes a plug-in call, even though it doesn't know if the current thread actually acquired the JSLock. (We don't want WebKit to accidentally drop a lock belonging to *another thread*.) (2) Used the new per-thread code written for (1) to make recursive calls to JSLock very cheap. JSLock now knows to call pthread_mutext_lock/ pthread_mutext_unlock only at nesting level 0. (KJS::createDidLockJSMutex): (KJS::JSLock::lock): (KJS::JSLock::unlock): (KJS::DropAllLocks::DropAllLocks): (KJS::DropAllLocks::~DropAllLocks): (KJS::JSLock::lockCount):
  • kjs/JSLock.h: Don't duplicate Noncopyable. (KJS::JSLock::~JSLock):
  • wtf/Assertions.h: Blind attempt at helping the Windows build.

WebCore:

Reviewed by Darin Adler.

Fixed <rdar://problem/4587763> PAC file: lock inversion between QT and
JSCore causes a hang @ www.panoramas.dk


See JavaScriptCore ChangeLog for details.


  • bindings/objc/WebScriptObject.mm: (_didExecute): Added helpful ASSERT. (+[WebScriptObject throwException:]): Added missing JSLock.

WebKit:

Reviewed by Darin Adler.

Fixed <rdar://problem/4587763> PAC file: lock inversion between QT and
JSCore causes a hang @ www.panoramas.dk


See JavaScriptCore ChangeLog for details.

Drop the JSLock before making calls through the plug-in API from functions
that may have been called by JavaScript.


  • Plugins/WebBaseNetscapePluginView.mm: (-[WebBaseNetscapePluginView sendEvent:]): (-[WebBaseNetscapePluginView setWindowIfNecessary]): (-[WebBaseNetscapePluginView initWithFrame:pluginPackage:URL:baseURL:MIMEType:attributeKeys:attributeValues:loadManually:DOMElement:]): (-[WebBaseNetscapePluginView createPluginScriptableObject]): (-[WebBaseNetscapePluginView evaluateJavaScriptPluginRequest:]): (-[WebBaseNetscapePluginView webFrame:didFinishLoadWithReason:]): (-[WebBaseNetscapePluginView loadPluginRequest:]): (-[WebBaseNetscapePluginView _printedPluginBitmap]):
  • Plugins/WebPluginController.mm: (+[WebPluginController plugInViewWithArguments:fromPluginPackage:]): (-[WebPluginController startAllPlugins]): (-[WebPluginController stopAllPlugins]): (-[WebPluginController addPlugin:]): (-[WebPluginController destroyPlugin:]): (-[WebPluginController destroyAllPlugins]):
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/API/JSCallbackFunction.cpp

    r17372 r20104  
    5959    for (int i = 0; i < argumentCount; i++)
    6060        arguments[i] = toRef(args[i]);
     61
     62    JSLock::DropAllLocks dropAllLocks;
    6163    return toJS(m_callback(execRef, thisRef, thisObjRef, argumentCount, arguments, toRef(exec->exceptionSlot())));
    6264}
Note: See TracChangeset for help on using the changeset viewer.