Bunch of deadlocking stuff
authorbenjamin@smedbergs.us
Thu, 13 Dec 2007 11:14:32 -0500
changeset 66 4161d51d7ccbb4019a5cb4d18a855094024a52db
parent 65 5674a3e586f318223a76af5d678101ee58d80805
child 67 5fcbda874b5844dafeed28ff6ad7f9cb500afea8
push id1
push userbsmedberg@mozilla.com
push dateTue, 15 Apr 2008 21:51:22 +0000
Bunch of deadlocking stuff
STAB-at-suspend-request-bug
STAB-at-suspend-request-bug-manual-fixup
compmgr-shutdown-asserts
debug-unittests
gc-default-context
gcAlloc-factoring
gccontexts
js-shell-threading
js-shell-threading-tests
jsexternalstring-fix
mark-dirservice2
mark-events
nsPipeEvents-stackobject
observe-before-thread-shutdown
root-layout-stuff
series
xpcom-thread-fixup
xpcom-timer-deadlock
xpconnect-finalizers
xpconnect-scope-finalizer-assertions
xptimanager-shutdown-reentry
--- a/STAB-at-suspend-request-bug
+++ b/STAB-at-suspend-request-bug
@@ -21,20 +21,20 @@ diff --git a/js/src/jsapi.cpp b/js/src/j
 @@ -953,8 +955,13 @@ JS_ResumeRequest(JSContext *cx, jsrefcou
  JS_ResumeRequest(JSContext *cx, jsrefcount saveDepth)
  {
      JS_ASSERT(!cx->requestDepth);
 -    while (--saveDepth >= 0)
 -        JS_BeginRequest(cx);
 +
 +    if (saveDepth) {
-+        js_PopStackRoot(cx);
-+
 +        while (--saveDepth >= 0)
 +            JS_BeginRequest(cx);
++
++        js_PopStackRoot(cx);
 +    }
  }
  
  #endif /* JS_THREADSAFE */
 diff --git a/js/src/jscntxt.h b/js/src/jscntxt.h
 --- a/js/src/jscntxt.h
 +++ b/js/src/jscntxt.h
 @@ -697,6 +697,10 @@ struct JSContext {
@@ -46,39 +46,40 @@ diff --git a/js/src/jscntxt.h b/js/src/j
 +    JSStackRoot         *stackRoot;
 +
      JSScope             *scopeToShare;      /* weak reference, see jslock.c */
      JSScope             *lockedSealedScope; /* weak ref, for low-cost sealed
                                                 scope locking */
 diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
 --- a/js/src/jsgc.cpp
 +++ b/js/src/jsgc.cpp
-@@ -601,6 +601,69 @@ js_MapGCRoots(JSRuntime *rt, JSGCRootMap
+@@ -605,6 +605,70 @@ js_MapGCRoots(JSRuntime *rt, JSGCRootMap
      return rv;
  }
  
 +#ifdef JS_THREADSAFE
 +struct JSStackRoot : public MMgc::GCRoot {
 +public:
 +    JSStackRoot(MMgc::GC *gc, void *buf, size_t size, JSStackRoot *up)
-+        : GCRoot(gc, buf, size), m_up(up) {}
++        : GCRoot(gc, buf, size), m_up(up), m_buf(buf) {}
 +
 +    /*
 +     * Because this is used in multiple threads, it must not be allocated from
 +     * MMgc's FixedMalloc allocator.  Use malloc instead.
 +     */
 +    void * operator new(size_t size) { return malloc(size); }
 +    void operator delete(void *p) { free(p); }
 +
 +    JSStackRoot *
 +    pop()
 +    {
 +        JSStackRoot *up = m_up;
-+        free(m_buf);
-+        delete this;
++        void *buf = m_buf;  /* Don't free until MMgc root is removed. */
++        delete this;  /* Destructor removes MMgc root. */
++        free(buf);
 +        return up;
 +    }
 +
 +private:
 +    JSStackRoot *m_up;
 +    void *m_buf;
 +};
 +
@@ -134,16 +135,53 @@ diff --git a/js/src/jsgc.h b/js/src/jsgc
 +
 +extern JSBool
 +js_PopStackRoot(JSContext *cx);
 +#endif
 +
  /* Table of pointers with count valid members. */
  typedef struct JSPtrTable {
      size_t      count;
+diff --git a/js/src/jslock.cpp b/js/src/jslock.cpp
+--- a/js/src/jslock.cpp
++++ b/js/src/jslock.cpp
+@@ -503,9 +503,25 @@ ClaimScope(JSScope *scope, JSContext *cx
+          * to call rt->gc->OnLeaveRequest if cx is active on the GC's thread,
+          * because the GC has already reduced cx->thread->requestCount to 0,
+          * to exclude all such such contexts.
++         *
++         * Unlike JS_SuspendRequest and JS_EndRequest, we can avoid the
++         * expensive call to js_PushStackRoot(), because we do not return
++         * with the request suspended.
+          */
++        using MMgc::uintptr; /* required for MMGC_GET_STACK_EXTENTS macro */
++        void *stack;
++        size_t size;
++        JSGC *gc = cx->runtime->gc;
++        MMgc::GCRoot *stackRoot = NULL;
++            
++        MMGC_GET_STACK_EXTENTS(gc, stack, size);
++
++        /* Suspend request. */
+         saveDepth = cx->requestDepth;
+         if (saveDepth) {
++            stackRoot = new MMgc::GCRoot(gc, stack, size);
++            if (!stackRoot)
++                break;
+             cx->requestDepth = 0;
+             if (rt->gcThread != cx->thread) {
+                 JS_ASSERT(cx->thread->requestCount > 0);
+@@ -537,6 +553,7 @@ ClaimScope(JSScope *scope, JSContext *cx
+                 if (cx->thread->requestCount == 1)
+                     rt->gc->OnEnterRequestAlreadyLocked();
+             }
++            delete stackRoot;
+         }
+ 
+         /*
 diff --git a/js/src/jsprvtd.h b/js/src/jsprvtd.h
 --- a/js/src/jsprvtd.h
 +++ b/js/src/jsprvtd.h
 @@ -102,6 +102,7 @@ typedef struct JSTokenStream        JSTo
  typedef struct JSTokenStream        JSTokenStream;
  typedef struct JSTreeContext        JSTreeContext;
  typedef struct JSTryNote            JSTryNote;
 +typedef struct JSStackRoot          JSStackRoot;
deleted file mode 100644
--- a/STAB-at-suspend-request-bug-manual-fixup
+++ /dev/null
@@ -1,12 +0,0 @@
-diff --git a/js/src/jsgc.cpp b/js/src/jsgc.cpp
---- a/js/src/jsgc.cpp
-+++ b/js/src/jsgc.cpp
-@@ -617,7 +617,7 @@ struct JSStackRoot : public MMgc::GCRoot
- struct JSStackRoot : public MMgc::GCRoot {
- public:
-     JSStackRoot(MMgc::GC *gc, void *buf, size_t size, JSStackRoot *up)
--        : GCRoot(gc, buf, size), m_up(up) {}
-+        : GCRoot(gc, buf, size), m_up(up), m_buf(buf) {}
- 
-     /*
-      * Because this is used in multiple threads, it must not be allocated from
new file mode 100644
--- /dev/null
+++ b/compmgr-shutdown-asserts
@@ -0,0 +1,16 @@
+diff --git a/xpcom/components/nsComponentManager.cpp b/xpcom/components/nsComponentManager.cpp
+--- a/xpcom/components/nsComponentManager.cpp
++++ b/xpcom/components/nsComponentManager.cpp
+@@ -305,10 +305,8 @@ factory_ClearEntry(PLDHashTable *aTable,
+ factory_ClearEntry(PLDHashTable *aTable, PLDHashEntryHdr *aHdr)
+ {
+     nsFactoryTableEntry* entry = static_cast<nsFactoryTableEntry*>(aHdr);
+-    // nsFactoryEntry is arena allocated. So we don't delete it.
+-    // We call the destructor by hand.
+-    entry->mFactoryEntry->~nsFactoryEntry();
+-    PL_DHashClearEntryStub(aTable, aHdr);
++    memset(entry->mFactoryEntry, 0, sizeof(nsFactoryEntry));
++    NS_GetGC()->Free(entry->mFactoryEntry);
+ }
+ 
+ static const PLDHashTableOps factory_DHashTableOps = {
new file mode 100644
--- /dev/null
+++ b/debug-unittests
@@ -0,0 +1,53 @@
+diff --git a/config/config.mk b/config/config.mk
+--- a/config/config.mk
++++ b/config/config.mk
+@@ -872,7 +872,7 @@ endif
+ endif
+ 
+ ifeq (,$(filter WINCE WINNT OS2,$(OS_ARCH)))
+-RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh
++RUN_TEST_PROGRAM = $(DIST)/bin/run-mozilla.sh $(DEBUG_TEST_PROGRAM)
+ endif
+ 
+ #
+diff --git a/config/rules.mk b/config/rules.mk
+--- a/config/rules.mk
++++ b/config/rules.mk
+@@ -1849,11 +1849,10 @@ endif # CYGWIN_WRAPPER
+ 
+ # Test execution
+ check::
+-	@$(EXIT_ON_ERROR) \
++	$(EXIT_ON_ERROR) \
+ 	for testdir in $(XPCSHELL_TESTS); do \
+-	  $(RUN_TEST_PROGRAM) \
+ 	    $(topsrcdir)/tools/test-harness/xpcshell-simple/test_all.sh \
+-	      $(DIST)/bin/xpcshell \
++	      "$(RUN_TEST_PROGRAM) $(DIST)/bin/xpcshell" \
+ 	      $(FWDSLASH_TOPSRCDIR) \
+ 	      $(NATIVE_TOPSRCDIR) \
+ 	      $(DEPTH)/_tests/xpcshell-simple/$(MODULE)/$$testdir; \
+@@ -1861,10 +1860,9 @@ check::
+ 
+ # Test execution
+ check-interactive::
+-	@$(EXIT_ON_ERROR) \
+-	$(RUN_TEST_PROGRAM) \
++	$(EXIT_ON_ERROR) \
+ 	  $(topsrcdir)/tools/test-harness/xpcshell-simple/test_one.sh \
+-	    $(DIST)/bin/xpcshell \
++	    "$(RUN_TEST_PROGRAM) $(DIST)/bin/xpcshell" \
+ 	    $(FWDSLASH_TOPSRCDIR) \
+ 	    $(NATIVE_TOPSRCDIR) \
+ 	    $(DEPTH)/_tests/xpcshell-simple/$(MODULE)/$$testdir \
+@@ -1873,9 +1871,8 @@ check-interactive::
+ # Test execution
+ check-one::
+ 	@$(EXIT_ON_ERROR) \
+-	$(RUN_TEST_PROGRAM) \
+ 	  $(topsrcdir)/tools/test-harness/xpcshell-simple/test_one.sh \
+-	    $(DIST)/bin/xpcshell \
++	    "$(RUN_TEST_PROGRAM) $(DIST)/bin/xpcshell" \
+ 	    $(FWDSLASH_TOPSRCDIR) \
+ 	    $(NATIVE_TOPSRCDIR) \
+ 	    $(DEPTH)/_tests/xpcshell-simple/$(MODULE)/$$testdir \
--- a/gc-default-context
+++ b/gc-default-context
@@ -71,50 +71,50 @@ diff --git a/js/src/jsgc.cpp b/js/src/js
 +    (PENDING_GC_CONTEXT(gc, thread)                                           \
 +     ? PENDING_GC_CONTEXT(gc, thread)                                         \
 +     : gc->defaultGCContext)
 +#define PENDING_GC_KIND(gc, thread)                                           \
 +    (PENDING_GC_SETTINGS(gc, thread)->pendingGCKind)
  
  JS_STATIC_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval));
  
-@@ -706,11 +707,12 @@ JSGC::JSGC(JSRuntime *_rt)
+@@ -719,11 +720,12 @@ JSGC::JSGC(JSRuntime *_rt)
  JSGC::JSGC(JSRuntime *_rt)
      : GC(MMgc::GCHeap::GetGCHeap()), 
        GCCallback(this),
 +      rt(_rt),
  #ifndef JS_THREADSAFE
        pendingGCContext(NULL),
        pendingGCKind(GC_NOT_RUNNING),
  #endif
 -      rt(_rt)
 +      defaultGCContext(NULL)
  {
      /* Incremental mode requires more work; see MMgc docs. */
      incremental = false;
-@@ -731,7 +733,8 @@ JSGC::precollect()
+@@ -744,7 +746,8 @@ JSGC::precollect()
      JSThread *thread = js_GetCurrentThread(rt);
  #endif
  
 -    cx = PENDING_GC_CONTEXT(this, thread);
 +    cx = DETERMINE_GC_CONTEXT(this, thread);
 +    JS_ASSERT(cx);
      gckind = PENDING_GC_KIND(this, thread);
  
      /*
-@@ -843,7 +846,7 @@ JSGC::enterExclusiveGC()
+@@ -856,7 +859,7 @@ JSGC::enterExclusiveGC()
  
      rt->gcPoke = JS_FALSE;
  
 -    rt->gcContext = PENDING_GC_CONTEXT(this, thread);
 +    rt->gcContext = DETERMINE_GC_CONTEXT(this, thread);
      rt->gcKind = PENDING_GC_KIND(this, thread);
  }
  
-@@ -910,7 +913,7 @@ JSGC::postcollect()
+@@ -923,7 +926,7 @@ JSGC::postcollect()
  #ifdef JS_THREADSAFE
      JSThread *thread = js_GetCurrentThread(rt);
  #endif
 -    JSContext *cx = PENDING_GC_CONTEXT(this, thread);
 +    JSContext *cx = DETERMINE_GC_CONTEXT(this, thread);
      JSGCInvocationKind gckind = PENDING_GC_KIND(this, thread);
  
      /* Execute JSGC_END callback outside the lock. */
--- a/gcAlloc-factoring
+++ b/gcAlloc-factoring
@@ -22,17 +22,17 @@ diff --git a/js/src/jsgc.cpp b/js/src/js
      if (!rt->gcMarker)
          return JS_FALSE;
 +#ifdef JS_THREADSAFE
      rt->gc->OnLeaveRequest();
 +#endif
  
      /*
       * Note: This allocation is from a non-threadsafe allocator.  This is only
-@@ -934,17 +941,44 @@ JSGC::postcollect()
+@@ -935,17 +942,44 @@ JSGC::postcollect()
      }
  }
  
 +static void *
 +gcAlloc(JSContext *cx, size_t nbytes, int flags)
 +{
 +    JSRuntime *rt = cx->runtime;
 +    JSGC *gc = rt->gc;
@@ -73,17 +73,17 @@ diff --git a/js/src/jsgc.cpp b/js/src/js
 -#ifdef JS_THREADSAFE
 -    JSThread *thread;
 -#endif
 -    JSContext *savedGCContext;
 -    JSGCInvocationKind savedGCKind;
  
      /*
       * Since we currently store gcThingFlags for a jsdouble at offset 8,
-@@ -953,32 +987,12 @@ js_NewGCThing(JSContext *cx, uintN flags
+@@ -954,32 +988,12 @@ js_NewGCThing(JSContext *cx, uintN flags
      if ((flags & GCF_TYPEMASK) == GCX_DOUBLE)
          nbytes++;
  
 -    rt = cx->runtime;
 -#ifdef JS_THREADSAFE
 -    JS_ASSERT(!rt->gc->IsGCRunning());
 -    if (rt->gc->IsGCRunning())
 -        return NULL;
--- a/gccontexts
+++ b/gccontexts
@@ -18,93 +18,93 @@ diff --git a/js/src/jsgc.cpp b/js/src/js
  
 -#define PENDING_GC_CONTEXT(ignore) g_pendingGCContext
 -#define PENDING_GC_KIND(ignore)    g_pendingGCKind
 +#define PENDING_GC_CONTEXT(gc, thread) ((gc)->pendingGCContext)
 +#define PENDING_GC_KIND(gc, thread)    ((gc)->pendingGCKind)
  #endif
  
  JS_STATIC_ASSERT(sizeof(JSStackHeader) >= 2 * sizeof(jsval));
-@@ -700,10 +700,16 @@ JSGC::JSGC(JSRuntime *_rt)
+@@ -712,10 +712,16 @@ JSGC::JSGC(JSRuntime *_rt)
  JSGC::JSGC(JSRuntime *_rt)
      : GC(MMgc::GCHeap::GetGCHeap()), 
        GCCallback(this),
 +#ifndef JS_THREADSAFE
 +      pendingGCContext(NULL),
 +      pendingGCKind(GC_NOT_RUNNING),
 +#endif
        rt(_rt)
  {
      /* Incremental mode requires more work; see MMgc docs. */
      incremental = false;
 +
 +    /* We leave the tracer uninitialized. */
  }
  
  bool
-@@ -719,8 +725,8 @@ JSGC::precollect()
+@@ -731,8 +737,8 @@ JSGC::precollect()
      JSThread *thread = js_GetCurrentThread(rt);
  #endif
  
 -    cx = PENDING_GC_CONTEXT(thread);
 -    gckind = PENDING_GC_KIND(thread);
 +    cx = PENDING_GC_CONTEXT(this, thread);
 +    gckind = PENDING_GC_KIND(this, thread);
  
      /*
       * Don't collect garbage if the runtime isn't up, and cx is not the last
-@@ -831,8 +837,8 @@ JSGC::enterExclusiveGC()
+@@ -843,8 +849,8 @@ JSGC::enterExclusiveGC()
  
      rt->gcPoke = JS_FALSE;
  
 -    rt->gcContext = PENDING_GC_CONTEXT(thread);
 -    rt->gcKind = PENDING_GC_KIND(thread);
 +    rt->gcContext = PENDING_GC_CONTEXT(this, thread);
 +    rt->gcKind = PENDING_GC_KIND(this, thread);
  }
  
  void
-@@ -898,8 +904,8 @@ JSGC::postcollect()
+@@ -910,8 +916,8 @@ JSGC::postcollect()
  #ifdef JS_THREADSAFE
      JSThread *thread = js_GetCurrentThread(rt);
  #endif
 -    JSContext *cx = PENDING_GC_CONTEXT(thread);
 -    JSGCInvocationKind gckind = PENDING_GC_KIND(thread);
 +    JSContext *cx = PENDING_GC_CONTEXT(this, thread);
 +    JSGCInvocationKind gckind = PENDING_GC_KIND(this, thread);
  
      /* Execute JSGC_END callback outside the lock. */
      if (rt->gcCallback) {
-@@ -947,10 +953,10 @@ js_NewGCThing(JSContext *cx, uintN flags
+@@ -959,10 +965,10 @@ js_NewGCThing(JSContext *cx, uintN flags
  #endif
  
      /* Enter possible GC region. */
 -    savedGCContext = PENDING_GC_CONTEXT(thread);
 -    PENDING_GC_CONTEXT(thread) = cx;
 -    savedGCKind = PENDING_GC_KIND(thread);
 -    PENDING_GC_KIND(thread) = GC_LAST_DITCH;
 +    savedGCContext = PENDING_GC_CONTEXT(rt->gc, thread);
 +    PENDING_GC_CONTEXT(rt->gc, thread) = cx;
 +    savedGCKind = PENDING_GC_KIND(rt->gc, thread);
 +    PENDING_GC_KIND(rt->gc, thread) = GC_LAST_DITCH;
  
      thing = rt->gc->Alloc(nbytes,
                            gc_type_is_finalized[flags & GCF_TYPEMASK]
-@@ -960,8 +966,8 @@ js_NewGCThing(JSContext *cx, uintN flags
+@@ -972,8 +978,8 @@ js_NewGCThing(JSContext *cx, uintN flags
                            : 0);
  
      /* Leave possible GC region. */
 -    PENDING_GC_CONTEXT(thread) = savedGCContext;
 -    PENDING_GC_KIND(thread) = savedGCKind;
 +    PENDING_GC_CONTEXT(rt->gc, thread) = savedGCContext;
 +    PENDING_GC_KIND(rt->gc, thread) = savedGCKind;
  
      if (!thing) {
          JS_ReportOutOfMemory(cx);
-@@ -1460,18 +1466,19 @@ js_GC(JSContext *cx, JSGCInvocationKind 
+@@ -1472,18 +1478,19 @@ js_GC(JSContext *cx, JSGCInvocationKind 
  {
      JSContext *savedGCContext;
      JSGCInvocationKind savedGCKind;
 +    JSGC *gc = cx->runtime->gc;
  
      /* Enter GC region. */
 -    savedGCContext = PENDING_GC_CONTEXT(cx->thread);
 -    PENDING_GC_CONTEXT(cx->thread) = cx;
--- a/js-shell-threading
+++ b/js-shell-threading
@@ -1,12 +1,12 @@
 diff --git a/js/src/js.cpp b/js/src/js.cpp
 --- a/js/src/js.cpp
 +++ b/js/src/js.cpp
-@@ -2452,6 +2452,253 @@ out:
+@@ -2452,6 +2452,255 @@ out:
      return ok;
  }
  
 +#ifdef JS_THREADSAFE
 +
 +static JSBool
 +Sleep(JSContext *cx, uintN argc, jsval *vp)
 +{
@@ -49,20 +49,22 @@ diff --git a/js/src/js.cpp b/js/src/js.c
 +    PRThread *thr;
 +    JSContext *cx;
 +    jsval fn;
 +};
 +
 +static void
 +DoScatteredWork(JSContext *cx, ScatterThreadData *td)
 +{
-+    JSFunction *fn = JS_ValueToFunction(cx, td->fn);
-+    if (fn != NULL) {
-+        JS_CallFunction(cx, NULL, fn, 0, NULL,
-+                        &td->shared->results[td->index]);
++    jsval *rval = &td->shared->results[td->index];
++
++    if (!JS_CallFunctionValue(cx, NULL, td->fn, 0, NULL, rval)) {
++        *rval = JSVAL_VOID;
++        JS_GetPendingException(cx, rval);
++        JS_ClearPendingException(cx);
 +    }
 +}
 +
 +static void
 +RunScatterThread(void *arg)
 +{
 +    ScatterThreadData *td;
 +    ScatterStatus st;
@@ -250,28 +252,28 @@ diff --git a/js/src/js.cpp b/js/src/js.c
 +}
 +
 +#endif
 +
 +
  /* We use a mix of JS_FS and JS_FN to test both kinds of natives. */
  static JSFunctionSpec shell_functions[] = {
      JS_FS("version",        Version,        0,0,0),
-@@ -2498,6 +2745,10 @@ static JSFunctionSpec shell_functions[] 
+@@ -2498,6 +2747,10 @@ static JSFunctionSpec shell_functions[] 
      JS_FN("getslx",         GetSLX,         1,1,0),
      JS_FN("toint32",        ToInt32,        1,1,0),
      JS_FS("evalcx",         EvalInContext,  1,0,0),
 +#ifdef JS_THREADSAFE
 +    JS_FN("sleep",          Sleep,          1,1,0),
 +    JS_FN("scatter",        Scatter,        1,1,0),
 +#endif
      JS_FS_END
  };
  
-@@ -2560,6 +2811,10 @@ static const char *const shell_help_mess
+@@ -2560,6 +2813,10 @@ static const char *const shell_help_mess
  "  Evaluate s in optional sandbox object o\n"
  "  if (s == '' && !o) return new o with eager standard classes\n"
  "  if (s == 'lazy' && !o) return new o with lazy standard classes",
 +#ifdef JS_THREADSAFE
 +"sleep(dt)                Sleep for dt seconds",
 +"scatter(fns)             Call functions concurrently (ignoring errors)",
 +#endif
  };
--- a/js-shell-threading-tests
+++ b/js-shell-threading-tests
@@ -265,17 +265,17 @@ new file mode 100644
 +
 +    if (start >= stop)
 +        return [];
 +    else if (start + 1 >= stop)
 +        return [start];
 +
 +    sleep(0.001);
 +
-+    var mid = Math.floor((stop - start) / 2);
++    var mid = start + Math.floor((stop - start) / 2);
 +    var halves = scatter([function () { return tree(start, mid); },
 +                          function () { return tree(mid, stop); }]);
 +    sleep(0.001);
 +    return Array.prototype.concat.apply([], halves);
 +}
 +
 +var expect = range(0, N).toSource();
 +var actual = tree(0, N).toSource();
new file mode 100644
--- /dev/null
+++ b/jsexternalstring-fix
@@ -0,0 +1,12 @@
+diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp
+--- a/js/src/jsapi.cpp
++++ b/js/src/jsapi.cpp
+@@ -2456,7 +2456,7 @@ JS_NewExternalString(JSContext *cx, jsch
+     CHECK_REQUEST(cx);
+     JS_ASSERT((uintN) type < (uintN) (GCX_NTYPES - GCX_EXTERNAL_STRING));
+ 
+-    return new(cx, (uintN) type, std::nothrow) JSExternalString(chars, length);
++    return new(cx, (uintN) type + GCX_EXTERNAL_STRING, std::nothrow) JSExternalString(chars, length);
+ }
+ 
+ JS_PUBLIC_API(intN)
new file mode 100644
--- /dev/null
+++ b/mark-dirservice2
@@ -0,0 +1,25 @@
+diff --git a/xpcom/build/nsXPCOM.h b/xpcom/build/nsXPCOM.h
+--- a/xpcom/build/nsXPCOM.h
++++ b/xpcom/build/nsXPCOM.h
+@@ -66,6 +66,7 @@
+ # define NS_CycleCollectorSuspect    NS_CycleCollectorSuspect_P
+ # define NS_CycleCollectorForget     NS_CycleCollectorForget_P
+ # define NS_GetGC                    NS_GetGC_P
++# define NS_RootUntilShutdown        NS_RootUntilShutdown_P
+ #endif
+ 
+ #include "nscore.h"
+diff --git a/xpcom/stub/nsXPComStub.cpp b/xpcom/stub/nsXPComStub.cpp
+--- a/xpcom/stub/nsXPComStub.cpp
++++ b/xpcom/stub/nsXPComStub.cpp
+@@ -553,3 +553,10 @@ NS_GetGC()
+ {
+   return NS_GetGC_P();
+ }
++
++#undef NS_RootUntilShutdown
++EXPORT_XPCOM_API(void)
++NS_RootUntilShutdown(void *obj)
++{
++  NS_RootUntilShutdown_P(obj);
++}
new file mode 100644
--- /dev/null
+++ b/mark-events
@@ -0,0 +1,63 @@
+diff --git a/xpcom/threads/nsEventQueue.cpp b/xpcom/threads/nsEventQueue.cpp
+--- a/xpcom/threads/nsEventQueue.cpp
++++ b/xpcom/threads/nsEventQueue.cpp
+@@ -54,6 +54,7 @@ nsEventQueue::nsEventQueue()
+   , mOffsetHead(0)
+   , mOffsetTail(0)
+ {
++  ASSERT_GCObject(this);
+ }
+ 
+ nsEventQueue::~nsEventQueue()
+@@ -61,9 +62,6 @@ nsEventQueue::~nsEventQueue()
+   // It'd be nice to be able to assert that no one else is holding the monitor,
+   // but NSPR doesn't really expose APIs for it.
+   NS_ASSERTION(IsEmpty(), "Non-empty event queue being destroyed; events being leaked.");
+-
+-  if (mHead)
+-    FreePage(mHead);
+ 
+   if (mMonitor)
+     nsAutoMonitor::DestroyMonitor(mMonitor);
+@@ -93,7 +91,6 @@ nsEventQueue::GetEvent(PRBool mayWait, n
+       if (mOffsetHead == EVENTS_PER_PAGE) {
+         Page *dead = mHead;
+         mHead = mHead->mNext;
+-        FreePage(dead);
+         mOffsetHead = 0;
+       }
+     }
+diff --git a/xpcom/threads/nsEventQueue.h b/xpcom/threads/nsEventQueue.h
+--- a/xpcom/threads/nsEventQueue.h
++++ b/xpcom/threads/nsEventQueue.h
+@@ -44,6 +44,8 @@
+ #include "nsIRunnable.h"
+ 
+ // A threadsafe FIFO event queue...
++// This object should be GC-allocated, but is typically part of a larger
++// class
+ class NS_COM nsEventQueue
+ {
+ public:
+@@ -97,18 +99,14 @@ private:
+ 
+   // Page objects are linked together to form a simple deque.
+ 
+-  struct Page; friend struct Page; // VC6!
+-  struct Page {
++  struct Page : public MMgc::GCObject {
+     struct Page *mNext;
+     nsIRunnable *mEvents[EVENTS_PER_PAGE];
+   };
+ 
+   static Page *NewPage() {
+-    return static_cast<Page *>(calloc(1, sizeof(Page)));
+-  }
+-
+-  static void FreePage(Page *p) {
+-    free(p);
++    return new(NS_GetGC(),
++               MMgc::GC::kContainsPointers | MMgc::GC::kZero) Page;
+   }
+ 
+   PRMonitor *mMonitor;
new file mode 100644
--- /dev/null
+++ b/nsPipeEvents-stackobject
@@ -0,0 +1,34 @@
+diff --git a/xpcom/io/nsPipe3.cpp b/xpcom/io/nsPipe3.cpp
+--- a/xpcom/io/nsPipe3.cpp
++++ b/xpcom/io/nsPipe3.cpp
+@@ -74,7 +74,14 @@ class nsPipeEvents
+ class nsPipeEvents
+ {
+ public:
+-    nsPipeEvents() { }
++    nsPipeEvents()
++	: mInputStream(nsnull)
++	, mInputCallback(nsnull)
++	, mOutputStream(nsnull)
++	, mOutputCallback(nsnull)
++    {
++	ASSERT_StackObject(this);
++    }
+    ~nsPipeEvents();
+ 
+     inline void NotifyInputReady(nsIAsyncInputStream *stream,
+@@ -94,10 +101,10 @@ public:
+     }
+ 
+ private:
+-    nsCOMPtr<nsIAsyncInputStream>     mInputStream;
+-    nsCOMPtr<nsIInputStreamCallback>  mInputCallback;
+-    nsCOMPtr<nsIAsyncOutputStream>    mOutputStream;
+-    nsCOMPtr<nsIOutputStreamCallback> mOutputCallback;
++    nsIAsyncInputStream*     mInputStream;
++    nsIInputStreamCallback*  mInputCallback;
++    nsIAsyncOutputStream*    mOutputStream;
++    nsIOutputStreamCallback* mOutputCallback;
+ };
+ 
+ //-----------------------------------------------------------------------------
new file mode 100644
--- /dev/null
+++ b/observe-before-thread-shutdown
@@ -0,0 +1,32 @@
+diff --git a/xpcom/build/nsXPComInit.cpp b/xpcom/build/nsXPComInit.cpp
+--- a/xpcom/build/nsXPComInit.cpp
++++ b/xpcom/build/nsXPComInit.cpp
+@@ -735,13 +735,6 @@ NS_ShutdownXPCOM(nsIServiceManager* serv
+ 
+         NS_ProcessPendingEvents(thread);
+ 
+-        // Shutdown all remaining threads.  This method does not return until
+-        // all threads created using the thread manager (with the exception of
+-        // the main thread) have exited.
+-        nsThreadManager::get()->Shutdown();
+-
+-        NS_ProcessPendingEvents(thread);
+-
+         // We save the "xpcom-shutdown-loaders" observers to notify after
+         // the observerservice is gone.
+         if (observerService) {
+@@ -751,6 +744,14 @@ NS_ShutdownXPCOM(nsIServiceManager* serv
+ 
+             observerService->Shutdown();
+         }
++
++
++        // Shutdown all remaining threads.  This method does not return until
++        // all threads created using the thread manager (with the exception of
++        // the main thread) have exited.
++        nsThreadManager::get()->Shutdown();
++
++        NS_ProcessPendingEvents(thread);
+     }
+ 
+     // XPCOM is officially in shutdown mode NOW
new file mode 100644
--- /dev/null
+++ b/root-layout-stuff
@@ -0,0 +1,126 @@
+diff --git a/content/base/public/nsContentUtils.h b/content/base/public/nsContentUtils.h
+--- a/content/base/public/nsContentUtils.h
++++ b/content/base/public/nsContentUtils.h
+@@ -55,6 +55,7 @@
+ #include "nsDataHashtable.h"
+ #include "nsIScriptRuntime.h"
+ #include "nsIScriptGlobalObject.h"
++#include "nsGkAtoms.h"
+ 
+ class nsIDOMScriptObjectFactory;
+ class nsIXPConnect;
+@@ -1107,6 +1108,8 @@ private:
+ 
+     nsIJSRuntimeService* sJSRuntimeService;
+     JSRuntime* sJSScriptRuntime;
++
++    nsGkAtoms sGKAtoms;
+   };
+ 
+   static LayoutRoots *sLayoutRoots;
+diff --git a/content/base/src/nsContentUtils.cpp b/content/base/src/nsContentUtils.cpp
+--- a/content/base/src/nsContentUtils.cpp
++++ b/content/base/src/nsContentUtils.cpp
+@@ -262,6 +262,8 @@ nsContentUtils::LayoutRoots::Init()
+   if (!InitializeEventTable())
+     return NS_ERROR_FAILURE;
+ 
++  sGKAtoms.RootAtoms();
++
+   if (!sEventListenerManagersHash.ops) {
+     static PLDHashTableOps hash_table_ops =
+     {
+diff --git a/content/base/src/nsGkAtoms.cpp b/content/base/src/nsGkAtoms.cpp
+--- a/content/base/src/nsGkAtoms.cpp
++++ b/content/base/src/nsGkAtoms.cpp
+@@ -49,14 +49,19 @@
+ #include "nsGkAtomList.h"
+ #undef GK_ATOM
+ 
+-static const nsStaticAtom GkAtoms_info[] = {
+-#define GK_ATOM(name_, value_) { value_, &nsGkAtoms::name_ },
++void nsGkAtoms::RootAtoms()
++{
++  nsStaticAtom GkAtoms_info[] = {
++#define GK_ATOM(name_, value_) { value_, &name_##root },
+ #include "nsGkAtomList.h"
+ #undef GK_ATOM
+-};
++  };
+ 
+-void nsGkAtoms::AddRefAtoms()
+-{
++  // This sets our members
+   NS_RegisterStaticAtoms(GkAtoms_info, NS_ARRAY_LENGTH(GkAtoms_info));
++
++  // Copy members to statics
++#define GK_ATOM(name_, value_) name_ = name_##root;
++#include "nsGkAtomList.h"
++#undef GK_ATOM
+ }
+-
+diff --git a/content/base/src/nsGkAtoms.h b/content/base/src/nsGkAtoms.h
+--- a/content/base/src/nsGkAtoms.h
++++ b/content/base/src/nsGkAtoms.h
+@@ -48,8 +48,9 @@
+ 
+ class nsGkAtoms {
+ public:
+-
+-  static void AddRefAtoms();
++  // A single instance of nsGkAtoms is maintained by the nsContentUtils root:
++  // this instance sets the static variable for general use
++  void RootAtoms();
+ 
+   /* Declare all atoms
+ 
+@@ -61,6 +62,10 @@ public:
+ #define GK_ATOM(_name, _value) static nsIAtom* _name;
+ #include "nsGkAtomList.h"
+ #undef GK_ATOM
++
++#define GK_ATOM(_name, _value) nsIAtom* _name##root;
++#include "nsGkAtomList.h"
++#undef GK_ATOM
+ };
+ 
+ #endif /* nsGkAtoms_h___ */
+diff --git a/content/xslt/src/xslt/txStylesheetCompileHandlers.cpp b/content/xslt/src/xslt/txStylesheetCompileHandlers.cpp
+--- a/content/xslt/src/xslt/txStylesheetCompileHandlers.cpp
++++ b/content/xslt/src/xslt/txStylesheetCompileHandlers.cpp
+@@ -3014,13 +3014,15 @@ txHandlerTable::find(PRInt32 aNamespaceI
+     return handler;
+ }
+ 
++// XXXbsmedberg: NO ROOTING-UNTIL-SHUTDOWN!
+ #define INIT_HANDLER(_name)                                          \
+     gTx##_name##Handler =                                            \
+         new txHandlerTable(gTx##_name##TableData.mTextHandler,       \
+                            &gTx##_name##TableData.mLREHandler,       \
+                            &gTx##_name##TableData.mOtherHandler);    \
+     if (!gTx##_name##Handler)                                        \
+-        return PR_FALSE
++        return PR_FALSE;                                             \
++    NS_RootUntilShutdown(gTx##_name##Handler);
+ 
+ #define INIT_HANDLER_WITH_ELEMENT_HANDLERS(_name)                    \
+     INIT_HANDLER(_name);                                             \
+diff --git a/layout/build/nsLayoutStatics.cpp b/layout/build/nsLayoutStatics.cpp
+--- a/layout/build/nsLayoutStatics.cpp
++++ b/layout/build/nsLayoutStatics.cpp
+@@ -62,7 +62,6 @@
+ #include "nsGenericElement.h"  // for nsDOMEventRTTearoff
+ #include "nsStyledElement.h"
+ #include "nsGlobalWindow.h"
+-#include "nsGkAtoms.h"
+ #include "nsImageFrame.h"
+ #include "nsLayoutStylesheetCache.h"
+ #include "nsNodeInfo.h"
+@@ -132,7 +131,6 @@ nsLayoutStatics::Initialize()
+   nsCSSKeywords::AddRefTable();
+   nsCSSProps::AddRefTable();
+   nsColorNames::AddRefTable();
+-  nsGkAtoms::AddRefAtoms();
+ 
+   nsDOMScriptObjectFactory::Startup();
+   rv = nsContentUtils::Init();
--- a/series
+++ b/series
@@ -63,18 +63,30 @@ more-template-typedefs
 automatic-gcobject
 automatic-garburator
 unbraced-if-fixes2.patch
 automatic-remove-addrefs
 revert-xpcomgc-cookie-madness
 system-metrics-comarray
 revert-xpccallcontext
 xpcom-request-contexts
-STAB-at-suspend-request-bug-manual-fixup
 set-default-jscontext
 no-socketts-deadlock
 xpconnect-allocations2
 root-jscomponentloader-script
 nativescriptableshared-gcobject
 root-contentutils
 mark-observerservice
 mark-dirservice
 more-hashtables
+mark-dirservice2
+root-layout-stuff
+mark-events
+xpconnect-finalizers
+observe-before-thread-shutdown
+compmgr-shutdown-asserts
+jsexternalstring-fix
+debug-unittests
+xpconnect-scope-finalizer-assertions
+xpcom-thread-fixup
+xpcom-timer-deadlock
+nsPipeEvents-stackobject
+xptimanager-shutdown-reentry
new file mode 100644
--- /dev/null
+++ b/xpcom-thread-fixup
@@ -0,0 +1,60 @@
+diff --git a/xpcom/threads/nsThread.cpp b/xpcom/threads/nsThread.cpp
+--- a/xpcom/threads/nsThread.cpp
++++ b/xpcom/threads/nsThread.cpp
+@@ -60,15 +60,32 @@ nsIThreadObserver* nsThread::sGlobalObse
+ // Because we do not have our own nsIFactory, we have to implement nsIClassInfo
+ // somewhat manually.
+ 
+-class nsThreadClassInfo : public nsIClassInfo {
++class nsThreadClassInfo : public XPCOMGCFinalizedObject
++                        , public nsIClassInfo {
+ public:
+   NS_DECL_ISUPPORTS_INHERITED  // no mRefCnt
+   NS_DECL_NSICLASSINFO
+ 
+-  nsThreadClassInfo() {}
++  nsThreadClassInfo() {
++    gThreadClassInfo = this;
++  }
++  ~nsThreadClassInfo() {
++    gThreadClassInfo = nsnull;
++  }
++
++  static nsThreadClassInfo* getSingleton()
++  {
++    if (gThreadClassInfo)
++      return gThreadClassInfo;
++
++    return new nsThreadClassInfo;
++  }
++
++private:
++  static nsThreadClassInfo *gThreadClassInfo;
+ };
+ 
+-static nsThreadClassInfo sThreadClassInfo;
++nsThreadClassInfo *nsThreadClassInfo::gThreadClassInfo;
+ 
+ NS_IMPL_QUERY_INTERFACE1(nsThreadClassInfo, nsIClassInfo)
+ 
+@@ -137,7 +154,7 @@ NS_INTERFACE_MAP_BEGIN(nsThread)
+   NS_INTERFACE_MAP_ENTRY(nsISupportsPriority)
+   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIThread)
+   if (aIID.Equals(NS_GET_IID(nsIClassInfo))) {
+-    foundInterface = static_cast<nsIClassInfo*>(&sThreadClassInfo);
++    foundInterface = static_cast<nsIClassInfo*>(nsThreadClassInfo::getSingleton());
+   } else
+ NS_INTERFACE_MAP_END
+ NS_IMPL_CI_INTERFACE_GETTER4(nsThread, nsIThread, nsIThreadInternal,
+@@ -447,7 +464,10 @@ nsThread::Shutdown()
+ 
+   // Now, it should be safe to join without fear of dead-locking.
+ 
+-  PR_JoinThread(mThread);
++  {
++    nsAutoSuspendRequest srq;
++    PR_JoinThread(mThread);
++  }
+   mThread = nsnull;
+   return NS_OK;
+ }
new file mode 100644
--- /dev/null
+++ b/xpcom-timer-deadlock
@@ -0,0 +1,180 @@
+diff --git a/xpcom/threads/TimerThread.cpp b/xpcom/threads/TimerThread.cpp
+--- a/xpcom/threads/TimerThread.cpp
++++ b/xpcom/threads/TimerThread.cpp
+@@ -48,6 +48,7 @@
+ #include "nsIObserverService.h"
+ #include "nsIServiceManager.h"
+ #include "nsIProxyObjectManager.h"
++#include "nsXPCOMRequests.h"
+ 
+ NS_IMPL_THREADSAFE_ISUPPORTS2(TimerThread, nsIRunnable, nsIObserver)
+ 
+@@ -80,6 +81,8 @@ nsresult
+ nsresult
+ TimerThread::InitLocks()
+ {
++  nsAutoSuspendRequest suspended;
++
+   NS_ASSERTION(!mLock, "InitLocks called twice?");
+   mLock = PR_NewLock();
+   if (!mLock)
+@@ -128,12 +131,14 @@ nsresult TimerThread::Init()
+       }
+     }
+ 
++    nsAutoSuspendRequest suspended;
+     PR_Lock(mLock);
+     mInitialized = PR_TRUE;
+     PR_NotifyAllCondVar(mCondVar);
+     PR_Unlock(mLock);
+   }
+   else {
++    nsAutoSuspendRequest suspended;
+     PR_Lock(mLock);
+     while (!mInitialized) {
+       PR_WaitCondVar(mCondVar, PR_INTERVAL_NO_TIMEOUT);
+@@ -155,6 +160,7 @@ nsresult TimerThread::Shutdown()
+     return NS_ERROR_NOT_INITIALIZED;
+ 
+   {   // lock scope
++    nsAutoSuspendRequest suspended;
+     nsAutoLock lock(mLock);
+ 
+     mShutdown = PR_TRUE;
+@@ -232,6 +238,8 @@ void TimerThread::UpdateFilter(PRUint32 
+ /* void Run(); */
+ NS_IMETHODIMP TimerThread::Run()
+ {
++  nsAutoSuspendRequest suspended;
++
+   nsAutoLock lock(mLock);
+ 
+   while (!mShutdown) {
+@@ -271,12 +279,11 @@ NS_IMETHODIMP TimerThread::Run()
+                   );
+           }
+ #endif
+-
+-          // We are going to let the call to PostTimerEvent here handle the
+-          // release of the timer so that we don't end up releasing the timer
+-          // on the TimerThread instead of on the thread it targets.
+-          timer->PostTimerEvent();
+-          timer = nsnull;
++          {
++            nsAutoResumeRequest resumed(suspended);
++            timer->PostTimerEvent();
++            timer = nsnull;
++          }
+ 
+           lock.lock();
+           if (mShutdown)
+@@ -322,6 +329,8 @@ NS_IMETHODIMP TimerThread::Run()
+ 
+ nsresult TimerThread::AddTimer(nsTimerImpl *aTimer)
+ {
++  nsAutoSuspendRequest suspended;
++
+   nsAutoLock lock(mLock);
+ 
+   // Add the timer to our list.
+@@ -338,6 +347,8 @@ nsresult TimerThread::AddTimer(nsTimerIm
+ 
+ nsresult TimerThread::TimerDelayChanged(nsTimerImpl *aTimer)
+ {
++  nsAutoSuspendRequest suspended;
++
+   nsAutoLock lock(mLock);
+ 
+   // Our caller has a strong ref to aTimer, so it can't go away here under
+@@ -357,6 +368,8 @@ nsresult TimerThread::TimerDelayChanged(
+ 
+ nsresult TimerThread::RemoveTimer(nsTimerImpl *aTimer)
+ {
++  nsAutoSuspendRequest suspended;
++
+   nsAutoLock lock(mLock);
+ 
+   // Remove the timer from our array.  Tell callers that aTimer was not found
+@@ -443,6 +456,17 @@ void TimerThread::DoAfterSleep()
+   mSleeping = PR_FALSE;
+ }
+ 
++bool
++TimerThread::CustomMark()
++{
++  for (PRInt32 i = mTimers.Count() - 1; i >= 0; --i) {
++    if (mTimers[i])
++      MMgc::GC::SetMark(mTimers[i]);
++  }
++
++  // Now do conservative marking also
++  return false;
++}
+ 
+ /* void observe (in nsISupports aSubject, in string aTopic, in wstring aData); */
+ NS_IMETHODIMP
+diff --git a/xpcom/threads/TimerThread.h b/xpcom/threads/TimerThread.h
+--- a/xpcom/threads/TimerThread.h
++++ b/xpcom/threads/TimerThread.h
+@@ -81,6 +81,8 @@ public:
+   void DoBeforeSleep();
+   void DoAfterSleep();
+ 
++  virtual bool CustomMark();
++
+ private:
+   ~TimerThread();
+ 
+@@ -100,7 +102,8 @@ private:
+   PRPackedBool mShutdown;
+   PRPackedBool mWaiting;
+   PRPackedBool mSleeping;
+-  
++
++  // This array is C-alalocated and custom-marked
+   nsVoidArray mTimers;
+ 
+ #define DELAY_LINE_LENGTH_LOG2  5
+diff --git a/xpcom/threads/nsXPCOMRequests.cpp b/xpcom/threads/nsXPCOMRequests.cpp
+--- a/xpcom/threads/nsXPCOMRequests.cpp
++++ b/xpcom/threads/nsXPCOMRequests.cpp
+@@ -83,3 +83,13 @@ nsAutoSuspendRequest::~nsAutoSuspendRequ
+   JS_ResumeRequest(mCX, mSaveDepth);
+ }
+ 
++nsAutoResumeRequest::nsAutoResumeRequest(nsAutoSuspendRequest &suspended)
++  : mCX(suspended.mCX)
++{
++  JS_BeginRequest(mCX);
++}
++
++nsAutoResumeRequest::~nsAutoResumeRequest()
++{
++  JS_EndRequest(mCX);
++}
+diff --git a/xpcom/threads/nsXPCOMRequests.h b/xpcom/threads/nsXPCOMRequests.h
+--- a/xpcom/threads/nsXPCOMRequests.h
++++ b/xpcom/threads/nsXPCOMRequests.h
+@@ -36,8 +36,22 @@ public:
+   ~nsAutoSuspendRequest();
+ 
+ private:
++  friend class nsAutoResumeRequest;
++
+   JSContext *mCX;
+   jsrefcount mSaveDepth;
+ };
+ 
++// Temporarily resume a request that has been suspended with nsAutoSuspendRequest
++class NS_COM nsAutoResumeRequest
++{
++public:
++  nsAutoResumeRequest(nsAutoSuspendRequest &suspended);
++  ~nsAutoResumeRequest();
++
++private:
++  JSContext *mCX;
++};
++
++
+ #endif // nsXPCOMRequests.h
new file mode 100644
--- /dev/null
+++ b/xpconnect-finalizers
@@ -0,0 +1,379 @@
+diff --git a/js/src/xpconnect/src/nsXPConnect.cpp b/js/src/xpconnect/src/nsXPConnect.cpp
+--- a/js/src/xpconnect/src/nsXPConnect.cpp
++++ b/js/src/xpconnect/src/nsXPConnect.cpp
+@@ -131,8 +131,6 @@ nsXPConnect::~nsXPConnect()
+         // XXX Call even if |mRuntime| null?
+         XPCWrappedNativeScope::SystemIsBeingShutDown(cx);
+ 
+-        mRuntime->SystemIsBeingShutDown(cx);
+-
+         JS_EndRequest(cx);
+         JS_DestroyContext(cx);
+     }
+diff --git a/js/src/xpconnect/src/xpcjsruntime.cpp b/js/src/xpconnect/src/xpcjsruntime.cpp
+--- a/js/src/xpconnect/src/xpcjsruntime.cpp
++++ b/js/src/xpconnect/src/xpcjsruntime.cpp
+@@ -335,24 +335,6 @@ WrappedJSShutdownMarker(JSDHashTable *ta
+     return JS_DHASH_NEXT;
+ }
+ 
+-JS_STATIC_DLL_CALLBACK(JSDHashOperator)
+-DetachedWrappedNativeProtoShutdownMarker(JSDHashTable *table, JSDHashEntryHdr *hdr,
+-                                         uint32 number, void *arg)
+-{
+-    XPCWrappedNativeProto* proto = 
+-        (XPCWrappedNativeProto*)((JSDHashEntryStub*)hdr)->key;
+-
+-    proto->SystemIsBeingShutDown((JSContext*)arg);
+-    return JS_DHASH_NEXT;
+-}
+-
+-void XPCJSRuntime::SystemIsBeingShutDown(JSContext* cx)
+-{
+-    if(mDetachedWrappedNativeProtoMap)
+-        mDetachedWrappedNativeProtoMap->
+-            Enumerate(DetachedWrappedNativeProtoShutdownMarker, cx);
+-}
+-
+ XPCJSRuntime::~XPCJSRuntime()
+ {
+ #ifdef XPC_DUMP_AT_SHUTDOWN
+@@ -460,16 +442,6 @@ XPCJSRuntime::~XPCJSRuntime()
+             printf("deleting XPCJSRuntime with %d live XPCNativeScriptableShared\n", (int)count);
+ #endif
+         delete mNativeScriptableSharedMap;
+-    }
+-
+-    if(mDyingWrappedNativeProtoMap)
+-    {
+-#ifdef XPC_DUMP_AT_SHUTDOWN
+-        uint32 count = mDyingWrappedNativeProtoMap->Count();
+-        if(count)
+-            printf("deleting XPCJSRuntime with %d live but dying XPCWrappedNativeProto\n", (int)count);
+-#endif
+-        delete mDyingWrappedNativeProtoMap;
+     }
+ 
+     if(mDetachedWrappedNativeProtoMap)
+@@ -513,7 +485,6 @@ XPCJSRuntime::XPCJSRuntime(nsXPConnect* 
+    mNativeSetMap(NativeSetMap::newMap(XPC_NATIVE_SET_MAP_SIZE)),
+    mThisTranslatorMap(IID2ThisTranslatorMap::newMap(XPC_THIS_TRANSLATOR_MAP_SIZE)),
+    mNativeScriptableSharedMap(XPCNativeScriptableSharedMap::newMap(XPC_NATIVE_JSCLASS_MAP_SIZE)),
+-   mDyingWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DYING_NATIVE_PROTO_MAP_SIZE)),
+    mDetachedWrappedNativeProtoMap(XPCWrappedNativeProtoMap::newMap(XPC_DETACHED_NATIVE_PROTO_MAP_SIZE)),
+    mExplicitNativeWrapperMap(XPCNativeWrapperMap::newMap(XPC_NATIVE_WRAPPER_MAP_SIZE)),
+    mMapLock(XPCAutoLock::NewLock("XPCJSRuntime::mMapLock")),
+@@ -574,7 +545,6 @@ XPCJSRuntime::newXPCJSRuntime(nsXPConnec
+        self->GetNativeSetMap()               &&
+        self->GetThisTranslatorMap()          &&
+        self->GetNativeScriptableSharedMap()  &&
+-       self->GetDyingWrappedNativeProtoMap() &&
+        self->GetExplicitNativeWrapperMap()   &&
+        self->GetMapLock())
+     {
+diff --git a/js/src/xpconnect/src/xpcprivate.h b/js/src/xpconnect/src/xpcprivate.h
+--- a/js/src/xpconnect/src/xpcprivate.h
++++ b/js/src/xpconnect/src/xpcprivate.h
+@@ -582,9 +582,6 @@ public:
+     XPCNativeScriptableSharedMap* GetNativeScriptableSharedMap() const
+         {return mNativeScriptableSharedMap;}
+ 
+-    XPCWrappedNativeProtoMap* GetDyingWrappedNativeProtoMap() const
+-        {return mDyingWrappedNativeProtoMap;}
+-
+     XPCWrappedNativeProtoMap* GetDetachedWrappedNativeProtoMap() const
+         {return mDetachedWrappedNativeProtoMap;}
+ 
+@@ -654,8 +651,6 @@ public:
+ 
+     void DebugDump(PRInt16 depth);
+ 
+-    void SystemIsBeingShutDown(JSContext* cx);
+-
+     PRThread* GetThreadRunningGC() const {return mThreadRunningGC;}
+ 
+     ~XPCJSRuntime();
+@@ -701,7 +696,6 @@ private:
+     NativeSetMap*            mNativeSetMap;
+     IID2ThisTranslatorMap*   mThisTranslatorMap;
+     XPCNativeScriptableSharedMap* mNativeScriptableSharedMap;
+-    XPCWrappedNativeProtoMap* mDyingWrappedNativeProtoMap;
+     XPCWrappedNativeProtoMap* mDetachedWrappedNativeProtoMap;
+     XPCNativeWrapperMap*     mExplicitNativeWrapperMap;
+     XPCLock* mMapLock;
+@@ -1716,10 +1710,6 @@ public:
+     void SetScriptableInfo(XPCNativeScriptableInfo* si)
+         {NS_ASSERTION(!mScriptableInfo, "leak here!"); mScriptableInfo = si;}
+ 
+-    void JSProtoObjectFinalized(JSContext *cx, JSObject *obj);
+-
+-    void SystemIsBeingShutDown(JSContext* cx);
+-
+     void DebugDump(PRInt16 depth);
+ 
+     ~XPCWrappedNativeProto();
+@@ -1775,8 +1765,6 @@ public:
+     void SetInterface(XPCNativeInterface*  Interface) {mInterface = Interface;}
+     void SetNative(nsISupports*  Native)              {mNative = Native;}
+     void SetJSObject(JSObject*  JSObj);
+-
+-    void JSObjectFinalized() {SetJSObject(nsnull);}
+ 
+ #ifdef XPC_IDISPATCH_SUPPORT
+     enum JSObject_flags
+@@ -1959,8 +1947,6 @@ public:
+                            JSObject* aNewParent,
+                            nsISupports* aCOMObj,
+                            XPCWrappedNative** aWrapper);
+-
+-    void FlatJSObjectFinalized(JSContext *cx);
+ 
+     void SystemIsBeingShutDown(JSContext* cx);
+ 
+diff --git a/js/src/xpconnect/src/xpcwrappednative.cpp b/js/src/xpconnect/src/xpcwrappednative.cpp
+--- a/js/src/xpconnect/src/xpcwrappednative.cpp
++++ b/js/src/xpconnect/src/xpcwrappednative.cpp
+@@ -859,46 +859,6 @@ NS_IMPL_THREADSAFE_RELEASE(XPCWrappedNat
+  */
+ 
+ void
+-XPCWrappedNative::FlatJSObjectFinalized(JSContext *cx)
+-{
+-    if(!IsValid())
+-        return;
+-
+-    // Iterate the tearoffs and null out each of their JSObject's privates.
+-    // This will keep them from trying to access their pointers to the
+-    // dying tearoff object. We can safely assume that those remaining
+-    // JSObjects are about to be finalized too.
+-
+-    XPCWrappedNativeTearOffChunk* chunk;
+-    for(chunk = &mFirstChunk; chunk; chunk = chunk->mNextChunk)
+-    {
+-        XPCWrappedNativeTearOff* to = chunk->mTearOffs;
+-        for(int i = XPC_WRAPPED_NATIVE_TEAROFFS_PER_CHUNK-1; i >= 0; i--, to++)
+-        {
+-            JSObject* jso = to->GetJSObject();
+-            if(jso)
+-            {
+-                NS_ASSERTION(JS_IsAboutToBeFinalized(cx, jso), "bad!");
+-                JS_SetPrivate(cx, jso, nsnull);
+-                to->JSObjectFinalized();
+-            }
+-
+-            // We also need to release any native pointers held...
+-            to->SetNative(nsnull);
+-            to->SetInterface(nsnull);
+-        }
+-    }
+-
+-    GetScope()->GetWrapperMap()->Remove(mFlatJSObject);
+-
+-    //This makes IsValid return false from now on...
+-    mFlatJSObject = nsnull;
+-
+-    // Note that it's not safe to touch mNativeWrapper here since it's
+-    // likely that it has already been finalized.
+-}
+-
+-void
+ XPCWrappedNative::SystemIsBeingShutDown(JSContext* cx)
+ {
+ #ifdef DEBUG_xpc_hacker
+@@ -928,11 +888,6 @@ XPCWrappedNative::SystemIsBeingShutDown(
+     // short circuit future finalization
+     JS_SetPrivate(cx, mFlatJSObject, nsnull);
+     mFlatJSObject = nsnull; // This makes 'IsValid()' return false.
+-
+-    XPCWrappedNativeProto* proto = GetProto();
+-
+-    if(HasProto())
+-        proto->SystemIsBeingShutDown(cx);
+ 
+     // cleanup the tearoffs...
+ 
+diff --git a/js/src/xpconnect/src/xpcwrappednativejsops.cpp b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
+--- a/js/src/xpconnect/src/xpcwrappednativejsops.cpp
++++ b/js/src/xpconnect/src/xpcwrappednativejsops.cpp
+@@ -641,15 +641,6 @@ XPC_WN_Shared_Enumerate(JSContext *cx, J
+ 
+ /***************************************************************************/
+ 
+-JS_STATIC_DLL_CALLBACK(void)
+-XPC_WN_NoHelper_Finalize(JSContext *cx, JSObject *obj)
+-{
+-    XPCWrappedNative* p = (XPCWrappedNative*) JS_GetPrivate(cx, obj);
+-    if(!p)
+-        return;
+-    p->FlatJSObjectFinalized(cx);
+-}
+-
+ static void
+ TraceScopeJSObjects(JSTracer *trc, XPCWrappedNativeScope* scope)
+ {
+@@ -895,7 +886,7 @@ JSExtendedClass XPC_WN_NoHelper_JSClass 
+         XPC_WN_Shared_Enumerate,        // enumerate;
+         XPC_WN_NoHelper_Resolve,        // resolve;
+         XPC_WN_Shared_Convert,          // convert;
+-        XPC_WN_NoHelper_Finalize,       // finalize;
++        JS_FinalizeStub,                // finalize;
+ 
+         /* Optionally non-null members start here. */
+         nsnull,                         // getObjectOps;
+@@ -1026,16 +1017,6 @@ XPC_WN_Helper_HasInstance(JSContext *cx,
+     PRE_HELPER_STUB
+     HasInstance(wrapper, cx, obj, v, bp, &retval);
+     POST_HELPER_STUB
+-}
+-
+-JS_STATIC_DLL_CALLBACK(void)
+-XPC_WN_Helper_Finalize(JSContext *cx, JSObject *obj)
+-{
+-    XPCWrappedNative* wrapper = (XPCWrappedNative*) JS_GetPrivate(cx, obj);
+-    if(!wrapper)
+-        return;
+-    wrapper->GetScriptableCallback()->Finalize(wrapper, cx, obj);
+-    wrapper->FlatJSObjectFinalized(cx);
+ }
+ 
+ JS_STATIC_DLL_CALLBACK(void)
+@@ -1400,10 +1381,7 @@ XPCNativeScriptableShared::PopulateJSCla
+     else
+         mJSClass.base.convert = XPC_WN_Shared_Convert;
+ 
+-    if(mFlags.WantFinalize())
+-        mJSClass.base.finalize = XPC_WN_Helper_Finalize;
+-    else
+-        mJSClass.base.finalize = XPC_WN_NoHelper_Finalize;
++    mJSClass.base.finalize = JS_FinalizeStub;
+ 
+     // We let the rest default to nsnull unless the helper wants them...
+     if(mFlags.WantCheckAccess())
+@@ -1549,15 +1527,6 @@ XPC_WN_Shared_Proto_Convert(JSContext *c
+ }
+ 
+ JS_STATIC_DLL_CALLBACK(void)
+-XPC_WN_Shared_Proto_Finalize(JSContext *cx, JSObject *obj)
+-{
+-    // This can be null if xpc shutdown has already happened
+-    XPCWrappedNativeProto* p = (XPCWrappedNativeProto*) JS_GetPrivate(cx, obj);
+-    if(p)
+-        p->JSProtoObjectFinalized(cx, obj);
+-}
+-
+-JS_STATIC_DLL_CALLBACK(void)
+ XPC_WN_Shared_Proto_Trace(JSTracer *trc, JSObject *obj)
+ {
+     // This can be null if xpc shutdown has already happened
+@@ -1610,7 +1579,7 @@ JSClass XPC_WN_ModsAllowed_Proto_JSClass
+     XPC_WN_Shared_Proto_Enumerate,         // enumerate;
+     XPC_WN_ModsAllowed_Proto_Resolve,      // resolve;
+     XPC_WN_Shared_Proto_Convert,           // convert;
+-    XPC_WN_Shared_Proto_Finalize,          // finalize;
++    JS_FinalizeStub,                       // finalize;
+ 
+     /* Optionally non-null members start here. */
+     nsnull,                         // getObjectOps;
+@@ -1691,7 +1660,7 @@ JSClass XPC_WN_NoMods_Proto_JSClass = {
+     XPC_WN_Shared_Proto_Enumerate,         // enumerate;
+     XPC_WN_NoMods_Proto_Resolve,           // resolve;
+     XPC_WN_Shared_Proto_Convert,           // convert;
+-    XPC_WN_Shared_Proto_Finalize,          // finalize;
++    JS_FinalizeStub,                       // finalize;
+ 
+     /* Optionally non-null members start here. */
+     nsnull,                         // getObjectOps;
+@@ -1752,16 +1721,6 @@ XPC_WN_TearOff_Resolve(JSContext *cx, JS
+                                  JSPROP_ENUMERATE, nsnull);
+ }
+ 
+-JS_STATIC_DLL_CALLBACK(void)
+-XPC_WN_TearOff_Finalize(JSContext *cx, JSObject *obj)
+-{
+-    XPCWrappedNativeTearOff* p = (XPCWrappedNativeTearOff*)
+-        JS_GetPrivate(cx, obj);
+-    if(!p)
+-        return;
+-    p->JSObjectFinalized();
+-}
+-
+ JSClass XPC_WN_Tearoff_JSClass = {
+     "WrappedNative_TearOff",            // name;
+     JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE, // flags;
+@@ -1774,7 +1733,7 @@ JSClass XPC_WN_Tearoff_JSClass = {
+     XPC_WN_TearOff_Enumerate,           // enumerate;
+     XPC_WN_TearOff_Resolve,             // resolve;
+     XPC_WN_Shared_Convert,              // convert;
+-    XPC_WN_TearOff_Finalize,            // finalize;
++    JS_FinalizeStub,                    // finalize;
+ 
+     /* Optionally non-null members start here. */
+     nsnull,                         // getObjectOps;
+diff --git a/js/src/xpconnect/src/xpcwrappednativeproto.cpp b/js/src/xpconnect/src/xpcwrappednativeproto.cpp
+--- a/js/src/xpconnect/src/xpcwrappednativeproto.cpp
++++ b/js/src/xpconnect/src/xpcwrappednativeproto.cpp
+@@ -114,52 +114,6 @@ XPCWrappedNativeProto::Init(
+     DEBUG_ReportShadowedMembers(mSet, nsnull, this);
+ 
+     return ok;
+-}
+-
+-void
+-XPCWrappedNativeProto::JSProtoObjectFinalized(JSContext *cx, JSObject *obj)
+-{
+-    NS_ASSERTION(obj == mJSProtoObject, "huh?");
+-
+-    // Map locking is not necessary since we are running gc.
+-
+-    if(IsShared())
+-    {
+-        // Only remove this proto from the map if it is the one in the map.
+-        ClassInfo2WrappedNativeProtoMap* map = 
+-            GetScope()->GetWrappedNativeProtoMap();
+-        if(map->Find(mClassInfo) == this)
+-            map->Remove(mClassInfo);
+-    }
+-
+-    GetRuntime()->GetDetachedWrappedNativeProtoMap()->Remove(this);
+-    GetRuntime()->GetDyingWrappedNativeProtoMap()->Add(this);
+-
+-    mJSProtoObject = nsnull;
+-}
+-
+-void
+-XPCWrappedNativeProto::SystemIsBeingShutDown(JSContext* cx)
+-{
+-    // Note that the instance might receive this call multiple times
+-    // as we walk to here from various places.
+-
+-#ifdef XPC_TRACK_PROTO_STATS
+-    static PRBool DEBUG_DumpedStats = PR_FALSE;
+-    if(!DEBUG_DumpedStats)
+-    {
+-        printf("%d XPCWrappedNativeProto(s) alive at shutdown\n",
+-               gDEBUG_LiveProtoCount);
+-        DEBUG_DumpedStats = PR_TRUE;
+-    }
+-#endif
+-
+-    if(mJSProtoObject)
+-    {
+-        // short circuit future finalization
+-        JS_SetPrivate(cx, mJSProtoObject, nsnull);
+-        mJSProtoObject = nsnull;
+-    }
+ }
+ 
+ // static
+diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp
+--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
++++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
+@@ -340,8 +340,6 @@ WrappedNativeProtoShutdownEnumerator(JSD
+                                      uint32 number, void *arg)
+ {
+     ShutdownData* data = (ShutdownData*) arg;
+-    ((ClassInfo2WrappedNativeProtoMap::Entry*)hdr)->value->
+-        SystemIsBeingShutDown(data->cx);
+     data->sharedProtoCount++;
+     return JS_DHASH_REMOVE;
+ }
new file mode 100644
--- /dev/null
+++ b/xpconnect-scope-finalizer-assertions
@@ -0,0 +1,49 @@
+diff --git a/js/src/xpconnect/src/xpcmaps.cpp b/js/src/xpconnect/src/xpcmaps.cpp
+--- a/js/src/xpconnect/src/xpcmaps.cpp
++++ b/js/src/xpconnect/src/xpcmaps.cpp
+@@ -753,5 +753,18 @@ WrappedNative2WrapperMap::DyingUnmapper(
+     return JS_DHASH_REMOVE;
+ }
+ 
+-
+-/***************************************************************************/
++// The following debug-only function allows you to enumerate the entries
++// of a hashtable from within a debugger
++#ifdef DEBUG
++JSDHashOperator
++PrintLiveEntries(JSDHashTable *table,
++                 JSDHashEntryHdr *hdr,
++                 uint32 number,
++                 void *arg)
++{
++    fprintf(stderr, "Live hashtable entry #%i: %p\n", number, hdr);
++    return JS_DHASH_NEXT;
++}
++#endif
++
++/***************************************************************************/
+diff --git a/js/src/xpconnect/src/xpcwrappednativescope.cpp b/js/src/xpconnect/src/xpcwrappednativescope.cpp
+--- a/js/src/xpconnect/src/xpcwrappednativescope.cpp
++++ b/js/src/xpconnect/src/xpcwrappednativescope.cpp
+@@ -291,14 +291,13 @@ XPCWrappedNativeScope::FinishedMarkPhase
+         cur;
+         prev = cur, cur = cur->mNext)
+     {
+-        if(NS_GetGC()->GetMark(cur))
+-        {
+-            // If the scope is marked, regulate its maps
+-            cur->UnmapDyingNatives();
+-        }
+-        else
+-        {
+-            // If the scope is not marked, remove it from our linked list
++        // This is technically not necessary for dying scopes... but
++        // it allows us to assert that the scopes are empty, which is
++        // useful. We should ifdef this out for release builds, I think
++        cur->UnmapDyingNatives();
++
++        if(!NS_GetGC()->GetMark(cur))
++        {
+             if(prev)
+                 prev->mNext = cur->mNext;
+             else
new file mode 100644
--- /dev/null
+++ b/xptimanager-shutdown-reentry
@@ -0,0 +1,18 @@
+diff --git a/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp b/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
+--- a/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
++++ b/xpcom/reflect/xptinfo/src/xptiInterfaceInfoManager.cpp
+@@ -158,8 +158,12 @@ xptiInterfaceInfoManager::xptiInterfaceI
+ 
+ xptiInterfaceInfoManager::~xptiInterfaceInfoManager()
+ {
+-    // We only do this on shutdown of the service.
+-    mWorkingSet.InvalidateInterfaceInfos();
++    // If we are being finalized after XPCOM shutdown, InvalidateInterfaceInfos could
++    // try to recreate us through GetInfoMonitor... so don't
++    if (gInterfaceInfoManager) {
++        // We only do this on shutdown of the service.
++        mWorkingSet.InvalidateInterfaceInfos();
++    }
+ 
+     if(mResolveLock)
+         PR_DestroyLock(mResolveLock);