Ignore:
Timestamp:
Apr 28, 2012, 1:51:27 PM (13 years ago)
Author:
[email protected]
Message:

Clarified JSGlobalData (JavaScript VM) lifetime
https://bugs.webkit.org/show_bug.cgi?id=85142

Reviewed by Anders Carlsson.

Source/JavaScriptCore:

This was so confusing that I didn't feel like I could reason about
memory lifetime in the heap without fixing it.

The rules are:

(1) JSGlobalData owns the virtual machine and all memory in it.

(2) Deleting a JSGlobalData frees the virtual machine and all memory
in it.

(Caveat emptor: if you delete the virtual machine while you're running
JIT code or accessing GC objects, you're gonna have a bad time.)

(I opted not to make arbitrary sub-objects keep the virtual machine
alive automatically because:

(a) doing that right would be complex and slow;

(b) in the case of an exiting thread or process, there's no
clear way to give the garbage collector a chance to try again
later;

(c) continuing to run the garbage collector after we've been
asked to shut down the virtual machine seems rude;

(d) we've never really supported that feature, anyway.)

(3) Normal ref-counting will do. No need to call a battery of
specialty functions to tear down a JSGlobalData. Its foibles
notwithstanding, C++ does in fact know how to execute destructors in
order.

  • API/JSContextRef.cpp:

(JSGlobalContextCreate): Removed compatibility shim for older
operating systems because it's no longer used.

(JSGlobalContextRelease): Now that we can rely on JSGlobalData to "do
the right thing", this code is much simpler. We still have one special
case to notify the garbage collector if we're removing the last
reference to the global object, since this can improve memory behavior.

  • heap/CopiedSpace.cpp:

(JSC::CopiedSpace::freeAllBlocks):

  • heap/CopiedSpace.h:

(CopiedSpace): Renamed "destroy" => "freeAllBlocks" because true
destruction-time behaviors should be limited to our C++ destructor.

  • heap/Heap.cpp:

(JSC::Heap::~Heap):
(JSC):
(JSC::Heap::lastChanceToFinalize):

  • heap/Heap.h:

(Heap):
(JSC::Heap::heap): Renamed "destroy" => "lastChanceToFinalize" because
true destruction-time behaviors should be limited to our C++
destructor.

Reorganized the code, putting code that must run before any objects
get torn down into lastChanceToFinalize, and code that just tears down
objects into our destructor.

  • heap/Local.h:

(JSC::LocalStack::LocalStack):
(JSC::LocalStack::push):
(LocalStack): See rule (2).

  • jsc.cpp:

(functionQuit):
(main):
(printUsageStatement):
(parseArguments):
(jscmain):

  • testRegExp.cpp:

(main):
(printUsageStatement):
(parseArguments):
(realMain): See rule (3).

I removed the feature of ensuring orderly tear-down when calling quit()
or running in --help mode because it didn't seem very useful and
making it work with Windows structured exception handling and
NO_RETURN didn't seem like a fun way to spend a Saturday.

  • runtime/JSGlobalData.h:
  • runtime/JSGlobalData.cpp:

(JSC::JSGlobalData::JSGlobalData): Moved heap to be the first data
member in JSGlobalData to ensure that it's destructed last, so other
objects that reference it destruct without crashing. This allowed me
to remove clearBuiltinStructures() altogether, and helped guarantee
rule (3).

(JSC::JSGlobalData::~JSGlobalData): Explicitly call
lastChanceToFinalize() at the head of our destructor to ensure that
all pending finalizers run while the virtual machine is still in a
valid state. Trying to resurrect (re-ref) the virtual machine at this
point is not valid, but all other operations are.

Changed a null to a 0xbbadbeef to clarify just how bad this beef is.

  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::init):

  • runtime/JSGlobalObject.h:

(JSGlobalObject):
(JSC::JSGlobalObject::globalData): See rule (3).

Source/WebCore:

  • bindings/js/WorkerScriptController.cpp:

(WebCore::WorkerScriptController::~WorkerScriptController): Slightly
simpler than before. We can't just rely on our default destructor
because we need to hold the JSLock when we tear down the VM.

  • bridge/NP_jsobject.cpp:

(_NPN_InvokeDefault):
(_NPN_Invoke):
(_NPN_Evaluate):
(_NPN_Construct): Don't RefPtr<> the JSGlobalData because it makes it
seem like you know something the rest of our code doesn't know. The
plugin JSGlobalData is immortal, anyway.

I also removed some timeout checker related code because that feature
doesn't work anymore, so it was effectively dead code.

Source/WebKit/mac:

  • Plugins/Hosted/NetscapePluginInstanceProxy.mm:

(WebKit::NetscapePluginInstanceProxy::invoke):
(WebKit::NetscapePluginInstanceProxy::invokeDefault):
(WebKit::NetscapePluginInstanceProxy::construct):

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/API/JSContextRef.cpp

    r115156 r115579  
    3939#include <wtf/text/StringHash.h>
    4040
    41 
    42 #if OS(DARWIN)
    43 #include <mach-o/dyld.h>
    44 
    45 static const int32_t webkitFirstVersionWithConcurrentGlobalContexts = 0x2100500; // 528.5.0
    46 #endif
    47 
    4841using namespace JSC;
     42
     43// From the API's perspective, a context group remains alive iff
     44//     (a) it has been JSContextGroupRetained
     45//     OR
     46//     (b) one of its contexts has been JSContextRetained
    4947
    5048JSContextGroupRef JSContextGroupCreate()
     
    6563}
    6664
     65// From the API's perspective, a global context remains alive iff it has been JSGlobalContextRetained.
     66
    6767JSGlobalContextRef JSGlobalContextCreate(JSClassRef globalObjectClass)
    6868{
    6969    initializeThreading();
    70 #if OS(DARWIN)
    71     // When running on Tiger or Leopard, or if the application was linked before JSGlobalContextCreate was changed
    72     // to use a unique JSGlobalData, we use a shared one for compatibility.
    73 #ifndef BUILDING_ON_LEOPARD
    74     if (NSVersionOfLinkTimeLibrary("JavaScriptCore") <= webkitFirstVersionWithConcurrentGlobalContexts) {
    75 #else
    76     {
    77 #endif
    78         JSLock lock(LockForReal);
    79         return JSGlobalContextCreateInGroup(toRef(&JSGlobalData::sharedInstance()), globalObjectClass);
    80     }
    81 #endif // OS(DARWIN)
    82 
    8370    return JSGlobalContextCreateInGroup(0, globalObjectClass);
    8471}
     
    126113
    127114    JSGlobalData& globalData = exec->globalData();
    128     JSGlobalObject* dgo = exec->dynamicGlobalObject();
    129115    IdentifierTable* savedIdentifierTable = wtfThreadData().setCurrentIdentifierTable(globalData.identifierTable);
    130116
    131     // One reference is held by JSGlobalObject, another added by JSGlobalContextRetain().
    132     bool releasingContextGroup = globalData.refCount() == 2;
    133     bool releasingGlobalObject = Heap::heap(dgo)->unprotect(dgo);
    134     // If this is the last reference to a global data, it should also
    135     // be the only remaining reference to the global object too!
    136     ASSERT(!releasingContextGroup || releasingGlobalObject);
    137 
    138     // An API 'JSGlobalContextRef' retains two things - a global object and a
    139     // global data (or context group, in API terminology).
    140     // * If this is the last reference to any contexts in the given context group,
    141     //   call destroy on the heap (the global data is being  freed).
    142     // * If this was the last reference to the global object, then unprotecting
    143     //   it may release a lot of GC memory - tickle the activity callback to
    144     //   garbage collect soon.
    145     // * If there are more references remaining the the global object, then do nothing
    146     //   (specifically that is more protects, which we assume come from other JSGlobalContextRefs).
    147     if (releasingContextGroup) {
    148         globalData.clearBuiltinStructures();
    149         globalData.heap.destroy();
    150     } else if (releasingGlobalObject) {
     117    bool protectCountIsZero = Heap::heap(exec->dynamicGlobalObject())->unprotect(exec->dynamicGlobalObject());
     118    if (protectCountIsZero) {
    151119        globalData.heap.activityCallback()->synchronize();
    152120        globalData.heap.reportAbandonedObjectGraph();
    153121    }
    154 
    155122    globalData.deref();
    156123
Note: See TracChangeset for help on using the changeset viewer.