Backout bug 506125.
authorAndreas Gal <gal@mozilla.com>
Tue, 18 Aug 2009 17:30:05 -0700
changeset 31888 7d788206e2ae7d98e462dab72c99061565f44e8d
parent 31887 0398f87956e0379be3a47563c542fc93035f5a5f
child 31889 a10a85319c3b4c2205e815024fbfb851411973a6
push id1
push userroot
push dateTue, 26 Apr 2011 22:38:44 +0000
treeherdermozilla-beta@bfdb6e623a36 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs506125
milestone1.9.3a1pre
Backout bug 506125.
dom/base/nsJSEnvironment.cpp
js/src/configure.in
js/src/jsapi.cpp
js/src/jsapi.h
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jsgc.cpp
js/src/jsgc.h
js/src/jsobj.cpp
js/src/jsscope.cpp
js/src/jsutil.h
js/src/shell/js.cpp
js/src/xpconnect/shell/xpcshell.cpp
memory/jemalloc/jemalloc.c
memory/jemalloc/jemalloc.h
--- a/dom/base/nsJSEnvironment.cpp
+++ b/dom/base/nsJSEnvironment.cpp
@@ -853,32 +853,28 @@ PrintWinCodebase(nsGlobalWindow *win)
   }
 
   nsCAutoString spec;
   uri->GetSpec(spec);
   printf("%s\n", spec.get());
 }
 #endif
 
-// Don't GC for the first 10s (startup).
-PRIntervalTime startTime = -1;
-
 static void
 MaybeGC(JSContext *cx)
 {
-  if (startTime) {
-    if (startTime == -1) {
-      startTime = PR_IntervalNow();
-      return;
-    }
-    if (PR_IntervalToMilliseconds(PR_IntervalNow() - startTime) < 10000)
-      return;
-    startTime = 0;
+  size_t bytes = cx->runtime->gcBytes;
+  size_t lastBytes = cx->runtime->gcLastBytes;
+  if ((bytes > 8192 && bytes > lastBytes * 16)
+#ifdef DEBUG
+      || cx->runtime->gcZeal > 0
+#endif
+      ) {
+    JS_GC(cx);
   }
-  JS_MaybeGC(cx);
 }
 
 static already_AddRefed<nsIPrompt>
 GetPromptFromContext(nsJSContext* ctx)
 {
   nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(ctx->GetGlobalObject()));
   NS_ENSURE_TRUE(win, nsnull);
 
@@ -3878,20 +3874,40 @@ ReportAllJSExceptionsPrefChangedCallback
   return 0;
 }
 
 static int
 SetMemoryHighWaterMarkPrefChangedCallback(const char* aPrefName, void* aClosure)
 {
   PRInt32 highwatermark = nsContentUtils::GetIntPref(aPrefName, 32);
 
-  JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES,
-                    (highwatermark >= 32)
-                    ? 0xffffffff
-                    : highwatermark * 1024L * 1024L);
+  if (highwatermark >= 32) {
+    // There are two options of memory usage in tracemonkey. One is
+    // to use malloc() and the other is to use memory for GC. (E.g.
+    // js_NewGCThing()/RefillDoubleFreeList()).
+    // Let's limit the high water mark for the first one to 32MB,
+    // and second one to 0xffffffff.
+    JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
+                      32L * 1024L * 1024L);
+    JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES,
+                      0xffffffff);
+  } else {
+    JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_MALLOC_BYTES,
+                      highwatermark * 1024L * 1024L);
+    JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_MAX_BYTES,
+                      highwatermark * 1024L * 1024L);
+  }
+  return 0;
+}
+
+static int
+SetMemoryGCFrequencyPrefChangedCallback(const char* aPrefName, void* aClosure)
+{
+  PRInt32 triggerFactor = nsContentUtils::GetIntPref(aPrefName, 1600);
+  JS_SetGCParameter(nsJSRuntime::sRuntime, JSGC_TRIGGER_FACTOR, triggerFactor);
   return 0;
 }
 
 static JSPrincipals *
 ObjectPrincipalFinder(JSContext *cx, JSObject *obj)
 {
   if (!sSecurityManager)
     return nsnull;
@@ -3974,16 +3990,22 @@ nsJSRuntime::Init()
                                            nsnull);
 
   nsContentUtils::RegisterPrefCallback("javascript.options.mem.high_water_mark",
                                        SetMemoryHighWaterMarkPrefChangedCallback,
                                        nsnull);
   SetMemoryHighWaterMarkPrefChangedCallback("javascript.options.mem.high_water_mark",
                                             nsnull);
 
+  nsContentUtils::RegisterPrefCallback("javascript.options.mem.gc_frequency",
+                                       SetMemoryGCFrequencyPrefChangedCallback,
+                                       nsnull);
+  SetMemoryGCFrequencyPrefChangedCallback("javascript.options.mem.gc_frequency",
+                                          nsnull);
+
   nsCOMPtr<nsIObserverService> obs =
     do_GetService("@mozilla.org/observer-service;1", &rv);
   NS_ENSURE_SUCCESS(rv, rv);
   nsIObserver* activityObserver = new nsUserActivityObserver();
   NS_ENSURE_TRUE(activityObserver, NS_ERROR_OUT_OF_MEMORY);
   obs->AddObserver(activityObserver, "user-interaction-inactive", PR_FALSE);
   obs->AddObserver(activityObserver, "user-interaction-active", PR_FALSE);
   obs->AddObserver(activityObserver, "xpcom-shutdown", PR_FALSE);
--- a/js/src/configure.in
+++ b/js/src/configure.in
@@ -1947,17 +1947,17 @@ case "$target" in
         MKSHLIB_FORCE_ALL=
         MKSHLIB_UNFORCE_ALL=
         DSO_LDOPTS=-SUBSYSTEM:WINDOWS
         _USE_CPP_INCLUDE_FLAG=1
         _DEFINES_CFLAGS='-FI $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT'
         _DEFINES_CXXFLAGS='-FI $(DEPTH)/js-confdefs.h -DMOZILLA_CLIENT'
         CFLAGS="$CFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)"
         CXXFLAGS="$CXXFLAGS -W3 -Gy -Fd\$(COMPILE_PDBFILE)"
-        LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib psapi.lib"
+        LIBS="$LIBS kernel32.lib user32.lib gdi32.lib winmm.lib wsock32.lib advapi32.lib"
         MOZ_DEBUG_FLAGS='-Zi'
         MOZ_DEBUG_LDFLAGS='-DEBUG -DEBUGTYPE:CV'
         WARNINGS_AS_ERRORS='-WX'
     	MOZ_OPTIMIZE_FLAGS='-O1'
         MOZ_JS_LIBS='$(libdir)/js$(MOZ_BITS)$(VERSION_NUMBER).lib'
         MOZ_FIX_LINK_PATHS=
         DYNAMIC_XPCOM_LIBS='$(LIBXUL_DIST)/lib/xpcom.lib $(LIBXUL_DIST)/lib/xpcom_core.lib'
         XPCOM_FROZEN_LDOPTS='$(LIBXUL_DIST)/lib/xpcom.lib'
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -2447,17 +2447,82 @@ JS_GC(JSContext *cx)
     if (cx->tempPool.current == &cx->tempPool.first)
         JS_FinishArenaPool(&cx->tempPool);
     js_GC(cx, GC_NORMAL);
 }
 
 JS_PUBLIC_API(void)
 JS_MaybeGC(JSContext *cx)
 {
-    js_MaybeGC(cx);
+    JSRuntime *rt;
+    uint32 bytes, lastBytes;
+
+    rt = cx->runtime;
+
+#ifdef JS_GC_ZEAL
+    if (rt->gcZeal > 0) {
+        JS_GC(cx);
+        return;
+    }
+#endif
+
+    bytes = rt->gcBytes;
+    lastBytes = rt->gcLastBytes;
+
+    /*
+     * We run the GC if we used all available free GC cells and had to
+     * allocate extra 1/3 of GC arenas since the last run of GC, or if
+     * we have malloc'd more bytes through JS_malloc than we were told
+     * to allocate by JS_NewRuntime.
+     *
+     * The reason for
+     *   bytes > 4/3 lastBytes
+     * condition is the following. Bug 312238 changed bytes and lastBytes
+     * to mean the total amount of memory that the GC uses now and right
+     * after the last GC.
+     *
+     * Before the bug the variables meant the size of allocated GC things
+     * now and right after the last GC. That size did not include the
+     * memory taken by free GC cells and the condition was
+     *   bytes > 3/2 lastBytes.
+     * That is, we run the GC if we have half again as many bytes of
+     * GC-things as the last time we GC'd. To be compatible we need to
+     * express that condition through the new meaning of bytes and
+     * lastBytes.
+     *
+     * We write the original condition as
+     *   B*(1-F) > 3/2 Bl*(1-Fl)
+     * where B is the total memory size allocated by GC and F is the free
+     * cell density currently and Sl and Fl are the size and the density
+     * right after GC. The density by definition is memory taken by free
+     * cells divided by total amount of memory. In other words, B and Bl
+     * are bytes and lastBytes with the new meaning and B*(1-F) and
+     * Bl*(1-Fl) are bytes and lastBytes with the original meaning.
+     *
+     * Our task is to exclude F and Fl from the last statement. According
+     * to the stats from bug 331966 comment 23, Fl is about 10-25% for a
+     * typical run of the browser. It means that the original condition
+     * implied that we did not run GC unless we exhausted the pool of
+     * free cells. Indeed if we still have free cells, then B == Bl since
+     * we did not yet allocated any new arenas and the condition means
+     *   1 - F > 3/2 (1-Fl) or 3/2Fl > 1/2 + F
+     * That implies 3/2 Fl > 1/2 or Fl > 1/3. That cannot be fulfilled
+     * for the state described by the stats. So we can write the original
+     * condition as:
+     *   F == 0 && B > 3/2 Bl(1-Fl)
+     * Again using the stats we see that Fl is about 11% when the browser
+     * starts up and when we are far from hitting rt->gcMaxBytes. With
+     * this F we have
+     * F == 0 && B > 3/2 Bl(1-0.11)
+     * or approximately F == 0 && B > 4/3 Bl.
+     */
+    if ((bytes > 8192 && bytes > lastBytes + lastBytes / 3) ||
+        rt->gcMallocBytes >= rt->gcMaxMallocBytes) {
+        JS_GC(cx);
+    }
 }
 
 JS_PUBLIC_API(JSGCCallback)
 JS_SetGCCallback(JSContext *cx, JSGCCallback cb)
 {
     CHECK_REQUEST(cx);
     return JS_SetGCCallbackRT(cx->runtime, cb);
 }
@@ -2481,31 +2546,42 @@ JS_IsAboutToBeFinalized(JSContext *cx, v
 
 JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value)
 {
     switch (key) {
       case JSGC_MAX_BYTES:
         rt->gcMaxBytes = value;
         break;
-      default:
-        JS_ASSERT(key == JSGC_STACKPOOL_LIFESPAN);
+      case JSGC_MAX_MALLOC_BYTES:
+        rt->gcMaxMallocBytes = value;
+        break;
+      case JSGC_STACKPOOL_LIFESPAN:
         rt->gcEmptyArenaPoolLifespan = value;
         break;
+      default:
+        JS_ASSERT(key == JSGC_TRIGGER_FACTOR);
+        JS_ASSERT(value >= 100);
+        rt->setGCTriggerFactor(value);
+        return;
     }
 }
 
 JS_PUBLIC_API(uint32)
 JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key)
 {
     switch (key) {
       case JSGC_MAX_BYTES:
         return rt->gcMaxBytes;
+      case JSGC_MAX_MALLOC_BYTES:
+        return rt->gcMaxMallocBytes;
       case JSGC_STACKPOOL_LIFESPAN:
         return rt->gcEmptyArenaPoolLifespan;
+      case JSGC_TRIGGER_FACTOR:
+        return rt->gcTriggerFactor;
       case JSGC_BYTES:
         return rt->gcBytes;
       default:
         JS_ASSERT(key == JSGC_NUMBER);
         return rt->gcNumber;
     }
 }
 
--- a/js/src/jsapi.h
+++ b/js/src/jsapi.h
@@ -1266,27 +1266,40 @@ JS_IsGCMarkingTracer(JSTracer *trc);
 
 extern JS_PUBLIC_API(JSBool)
 JS_IsAboutToBeFinalized(JSContext *cx, void *thing);
 
 typedef enum JSGCParamKey {
     /* Maximum nominal heap before last ditch GC. */
     JSGC_MAX_BYTES          = 0,
 
+    /* Number of JS_malloc bytes before last ditch GC. */
+    JSGC_MAX_MALLOC_BYTES   = 1,
+
     /* Hoard stackPools for this long, in ms, default is 30 seconds. */
-    JSGC_STACKPOOL_LIFESPAN = 1,
+    JSGC_STACKPOOL_LIFESPAN = 2,
+
+    /*
+     * The factor that defines when the GC is invoked. The factor is a
+     * percent of the memory allocated by the GC after the last run of
+     * the GC. When the current memory allocated by the GC is more than
+     * this percent then the GC is invoked. The factor cannot be less
+     * than 100 since the current memory allocated by the GC cannot be less
+     * than the memory allocated after the last run of the GC.
+     */
+    JSGC_TRIGGER_FACTOR = 3,
 
     /* Amount of bytes allocated by the GC. */
-    JSGC_BYTES = 2,
+    JSGC_BYTES = 4,
 
     /* Number of times when GC was invoked. */
-    JSGC_NUMBER = 3,
+    JSGC_NUMBER = 5,
 
     /* Max size of the code cache in bytes. */
-    JSGC_MAX_CODE_CACHE_BYTES = 4
+    JSGC_MAX_CODE_CACHE_BYTES = 6
 } JSGCParamKey;
 
 extern JS_PUBLIC_API(void)
 JS_SetGCParameter(JSRuntime *rt, JSGCParamKey key, uint32 value);
 
 extern JS_PUBLIC_API(uint32)
 JS_GetGCParameter(JSRuntime *rt, JSGCParamKey key);
 
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1792,17 +1792,16 @@ js_InvokeOperationCallback(JSContext *cx
 void
 js_TriggerAllOperationCallbacks(JSRuntime *rt, JSBool gcLocked)
 {
     JSContext *acx, *iter;
 #ifdef JS_THREADSAFE
     if (!gcLocked)
         JS_LOCK_GC(rt);
 #endif
-
     iter = NULL;
     while ((acx = js_ContextIterator(rt, JS_FALSE, &iter)))
         JS_TriggerOperationCallback(acx);
 #ifdef JS_THREADSAFE
     if (!gcLocked)
         JS_UNLOCK_GC(rt);
 #endif
 }
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -257,16 +257,22 @@ struct JSThreadData {
 
     /* Lock-free hashed lists of scripts created by eval to garbage-collect. */
     JSScript            *scriptsToGC[JS_EVAL_CACHE_SIZE];
 
 #ifdef JS_EVAL_CACHE_METERING
     JSEvalCacheMeter    evalCacheMeter;
 #endif
 
+    /*
+     * Thread-local version of JSRuntime.gcMallocBytes to avoid taking
+     * locks on each JS_malloc.
+     */
+    size_t              gcMallocBytes;
+
 #ifdef JS_THREADSAFE
     /*
      * Deallocator task for this thread.
      */
     JSFreePointerListTask *deallocatorTask;
 #endif
 
     void mark(JSTracer *trc) {
@@ -379,24 +385,26 @@ struct JSRuntime {
 
     /* Garbage collector state, used by jsgc.c. */
     JSGCChunkInfo       *gcChunkList;
     JSGCArenaList       gcArenaList[GC_NUM_FREELISTS];
     JSGCDoubleArenaList gcDoubleArenaList;
     JSDHashTable        gcRootsHash;
     JSDHashTable        *gcLocksHash;
     jsrefcount          gcKeepAtoms;
+    size_t              gcBytes;
+    size_t              gcLastBytes;
+    size_t              gcMaxBytes;
+    size_t              gcMaxMallocBytes;
     uint32              gcEmptyArenaPoolLifespan;
     uint32              gcLevel;
     uint32              gcNumber;
     JSTracer            *gcMarkingTracer;
-    size_t              gcBytes;
-    size_t              gcMaxBytes;
-    size_t              gcLastRSS;
-    size_t              gcFreed;
+    uint32              gcTriggerFactor;
+    size_t              gcTriggerBytes;
     volatile JSBool     gcIsNeeded;
     volatile JSBool     gcFlushCodeCaches;
 
     /*
      * NB: do not pack another flag here by claiming gcPadding unless the new
      * flag is written only by the GC thread.  Atomic updates to packed bytes
      * are not guaranteed, so stores issued by one thread may be lost due to
      * unsynchronized read-modify-write cycles on other threads.
@@ -416,16 +424,17 @@ struct JSRuntime {
      */
     uint8              gcRegenShapesScopeFlag;
 
 #ifdef JS_GC_ZEAL
     jsrefcount          gcZeal;
 #endif
 
     JSGCCallback        gcCallback;
+    size_t              gcMallocBytes;
     JSGCArenaInfo       *gcUntracedArenaStackTop;
 #ifdef DEBUG
     size_t              gcTraceLaterCount;
 #endif
 
     /*
      * Table for tracking iterators to ensure that we close iterator's state
      * before finalizing the iterable object.
@@ -704,16 +713,19 @@ struct JSRuntime {
     JSGCStats           gcStats;
 #endif
 
 #ifdef JS_FUNCTION_METERING
     JSFunctionMeter     functionMeter;
     char                lastScriptFilename[1024];
 #endif
 
+    void setGCTriggerFactor(uint32 factor);
+    void setGCLastBytes(size_t lastBytes);
+
     inline void* malloc(size_t bytes) {
         return ::js_malloc(bytes);
     }
 
     inline void* calloc(size_t bytes) {
         return ::js_calloc(bytes);
     }
 
@@ -1063,58 +1075,72 @@ struct JSContext {
     VMSideExit          *bailExit;
 
     /* Used when calling natives from trace to root the vp vector. */
     uintN               nativeVpLen;
     jsval               *nativeVp;
 #endif
 
 #ifdef JS_THREADSAFE
-    inline void createDeallocatorTask(size_t *bytesp) {
-        JSThreadData *tls = JS_THREAD_DATA(this);
+    inline void createDeallocatorTask() {
+        JSThreadData* tls = JS_THREAD_DATA(this);
         JS_ASSERT(!tls->deallocatorTask);
         if (runtime->deallocatorThread && !runtime->deallocatorThread->busy())
-            tls->deallocatorTask = new JSFreePointerListTask(bytesp);
+            tls->deallocatorTask = new JSFreePointerListTask();
     }
 
     inline void submitDeallocatorTask() {
-        JSThreadData *tls = JS_THREAD_DATA(this);
+        JSThreadData* tls = JS_THREAD_DATA(this);
         if (tls->deallocatorTask) {
             runtime->deallocatorThread->schedule(tls->deallocatorTask);
             tls->deallocatorTask = NULL;
         }
     }
 #endif
 
-    inline void *malloc(size_t bytes) {
+    /* Call this after succesful malloc of memory for GC-related things. */
+    inline void updateMallocCounter(size_t nbytes) {
+        size_t *pbytes, bytes;
+
+        pbytes = &JS_THREAD_DATA(this)->gcMallocBytes;
+        bytes = *pbytes;
+        *pbytes = (size_t(-1) - bytes <= nbytes) ? size_t(-1) : bytes + nbytes;
+    }
+
+    inline void* malloc(size_t bytes) {
         JS_ASSERT(bytes != 0);
         void *p = runtime->malloc(bytes);
         if (!p) {
             JS_ReportOutOfMemory(this);
             return NULL;
         }
+        updateMallocCounter(bytes);
         return p;
     }
 
-    inline void *calloc(size_t bytes) {
+    inline void* calloc(size_t bytes) {
         JS_ASSERT(bytes != 0);
         void *p = runtime->calloc(bytes);
         if (!p) {
             JS_ReportOutOfMemory(this);
             return NULL;
         }
+        updateMallocCounter(bytes);
         return p;
     }
 
-    inline void *realloc(void* p, size_t bytes) {
+    inline void* realloc(void* p, size_t bytes) {
+        void *orig = p;
         p = runtime->realloc(p, bytes);
         if (!p) {
             JS_ReportOutOfMemory(this);
             return NULL;
         }
+        if (!orig)
+            updateMallocCounter(bytes);
         return p;
     }
 
 #ifdef JS_THREADSAFE
     inline void free(void* p) {
         if (!p)
             return;
         if (thread) {
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1293,23 +1293,34 @@ js_InitGC(JSRuntime *rt, uint32 maxbytes
     InitGCArenaLists(rt);
     if (!JS_DHashTableInit(&rt->gcRootsHash, JS_DHashGetStubOps(), NULL,
                            sizeof(JSGCRootHashEntry), GC_ROOTS_SIZE)) {
         rt->gcRootsHash.ops = NULL;
         return JS_FALSE;
     }
     rt->gcLocksHash = NULL;     /* create lazily */
 
-    rt->gcMaxBytes = maxbytes;
+    /*
+     * Separate gcMaxMallocBytes from gcMaxBytes but initialize to maxbytes
+     * for default backward API compatibility.
+     */
+    rt->gcMaxBytes = rt->gcMaxMallocBytes = maxbytes;
     rt->gcEmptyArenaPoolLifespan = 30000;
 
     /*
-     * Sample the current resident set size (RSS).
+     * By default the trigger factor gets maximum possible value. This
+     * means that GC will not be triggered by growth of GC memory (gcBytes).
      */
-    rt->gcLastRSS = js_GetRSS();
+    rt->setGCTriggerFactor((uint32) -1);
+
+    /*
+     * The assigned value prevents GC from running when GC memory is too low
+     * (during JS engine start).
+     */
+    rt->setGCLastBytes(8192);
 
     METER(memset(&rt->gcStats, 0, sizeof rt->gcStats));
     return JS_TRUE;
 }
 
 #ifdef JS_GCMETER
 
 static void
@@ -1708,32 +1719,69 @@ CloseNativeIterators(JSContext *cx)
 static struct GCHist {
     bool        lastDitch;
     JSGCThing   *freeList;
 } gchist[NGCHIST];
 
 unsigned gchpos = 0;
 #endif
 
+void
+JSRuntime::setGCTriggerFactor(uint32 factor)
+{
+    JS_ASSERT(factor >= 100);
+
+    gcTriggerFactor = factor;
+    setGCLastBytes(gcLastBytes);
+}
+
+void
+JSRuntime::setGCLastBytes(size_t lastBytes)
+{
+    gcLastBytes = lastBytes;
+    uint64 triggerBytes = uint64(lastBytes) * uint64(gcTriggerFactor / 100);
+    if (triggerBytes != size_t(triggerBytes))
+        triggerBytes = size_t(-1);
+    gcTriggerBytes = size_t(triggerBytes);
+}
+
+static JS_INLINE bool
+IsGCThresholdReached(JSRuntime *rt)
+{
+#ifdef JS_GC_ZEAL
+    if (rt->gcZeal >= 1)
+        return true;
+#endif
+
+    /*
+     * Since the initial value of the gcLastBytes parameter is not equal to
+     * zero (see the js_InitGC function) the return value is false when
+     * the gcBytes value is close to zero at the JS engine start.
+     */
+    return rt->gcMallocBytes >= rt->gcMaxMallocBytes ||
+           rt->gcBytes >= rt->gcTriggerBytes;
+}
+
 template <class T> static JS_INLINE T*
 NewGCThing(JSContext *cx, uintN flags)
 {
     JSRuntime *rt;
     bool doGC;
     JSGCThing *thing;
     uint8 *flagp;
     JSGCArenaList *arenaList;
     JSGCArenaInfo *a;
     uintN thingsLimit;
     JSLocalRootStack *lrs;
 #ifdef JS_GCMETER
     JSGCArenaStats *astats;
 #endif
 #ifdef JS_THREADSAFE
     JSBool gcLocked;
+    uintN localMallocBytes;
     JSGCThing **lastptr;
     JSGCThing *tmpthing;
     uint8 *tmpflagp;
     uintN maxFreeThings;         /* max to take from the global free list */
 #endif
 
     JS_ASSERT((flags & GCF_TYPEMASK) != GCX_DOUBLE);
     rt = cx->runtime;
@@ -1746,41 +1794,50 @@ NewGCThing(JSContext *cx, uintN flags)
     METER(astats->alloc++);
 
 #ifdef JS_THREADSAFE
     gcLocked = JS_FALSE;
     JS_ASSERT(cx->thread);
 
     JSGCThing *&freeList = cx->thread->gcFreeLists[flindex];
     thing = freeList;
-    if (thing) {
+    localMallocBytes = JS_THREAD_DATA(cx)->gcMallocBytes;
+    if (thing && rt->gcMaxMallocBytes - rt->gcMallocBytes > localMallocBytes) {
         flagp = thing->flagp;
         freeList = thing->next;
         METER(astats->localalloc++);
         goto success;
     }
 
     JS_LOCK_GC(rt);
     gcLocked = JS_TRUE;
 
+    /* Transfer thread-local counter to global one. */
+    if (localMallocBytes != 0) {
+        JS_THREAD_DATA(cx)->gcMallocBytes = 0;
+        if (rt->gcMaxMallocBytes - rt->gcMallocBytes < localMallocBytes)
+            rt->gcMallocBytes = rt->gcMaxMallocBytes;
+        else
+            rt->gcMallocBytes += localMallocBytes;
+    }
 #endif
     JS_ASSERT(!rt->gcRunning);
     if (rt->gcRunning) {
         METER(rt->gcStats.finalfail++);
         JS_UNLOCK_GC(rt);
         return NULL;
     }
 
 #if defined JS_GC_ZEAL && defined JS_TRACER
     if (rt->gcZeal >= 1 && JS_TRACE_MONITOR(cx).useReservedObjects)
         goto testReservedObjects;
 #endif
 
     arenaList = &rt->gcArenaList[flindex];
-    doGC = rt->gcIsNeeded;
+    doGC = IsGCThresholdReached(rt);
     for (;;) {
         if (doGC
 #ifdef JS_TRACER
             && !JS_ON_TRACE(cx) && !JS_TRACE_MONITOR(cx).useReservedObjects
 #endif
             ) {
             /*
              * Keep rt->gcLock across the call into js_GC so we don't starve
@@ -1799,20 +1856,23 @@ NewGCThing(JSContext *cx, uintN flags)
         if (thing) {
             arenaList->freeList = thing->next;
             flagp = thing->flagp;
             JS_ASSERT(*flagp & GCF_FINAL);
 
 #ifdef JS_THREADSAFE
             /*
              * Refill the local free list by taking several things from the
-             * global free list unless the free list is already populated.
-             * This is caused by allocating new things in gcCallback(cx, JSGC_END).
+             * global free list unless the free list is already populated or
+             * we are still at rt->gcMaxMallocBytes barrier. The former is
+             * caused via allocating new things in gcCallback(cx, JSGC_END).
+             * The latter happens when GC is canceled due to
+             * gcCallback(cx, JSGC_BEGIN) returning false.
              */
-            if (freeList)
+            if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
                 break;
 
             tmpthing = arenaList->freeList;
             if (tmpthing) {
                 maxFreeThings = MAX_THREAD_LOCAL_THINGS;
                 do {
                     if (!tmpthing->next)
                         break;
@@ -1872,17 +1932,17 @@ testReservedObjects:
         arenaList->lastCount++;
 
 #ifdef JS_THREADSAFE
         /*
          * Refill the local free list by taking free things from the last
          * arena. Prefer to order free things by ascending address in the
          * (unscientific) hope of better cache locality.
          */
-        if (freeList)
+        if (freeList || rt->gcMallocBytes >= rt->gcMaxMallocBytes)
             break;
         lastptr = &freeList;
         maxFreeThings = thingsLimit - arenaList->lastCount;
         if (maxFreeThings > MAX_THREAD_LOCAL_THINGS)
             maxFreeThings = MAX_THREAD_LOCAL_THINGS;
         uint32 lastCount = arenaList->lastCount;
         while (maxFreeThings != 0) {
             --maxFreeThings;
@@ -1999,17 +2059,17 @@ RefillDoubleFreeList(JSContext *cx)
 
     JS_ASSERT(!rt->gcRunning);
     if (rt->gcRunning) {
         METER(rt->gcStats.finalfail++);
         JS_UNLOCK_GC(rt);
         return NULL;
     }
 
-    if (rt->gcIsNeeded)
+    if (IsGCThresholdReached(rt))
         goto do_gc;
 
     /*
      * Loop until we find a flag bitmap byte with unset bits indicating free
      * double cells, then set all bits as used and put the cells to the free
      * list for the current context.
      */
     doubleFlags = rt->gcDoubleArenaList.nextDoubleFlags;
@@ -2186,16 +2246,55 @@ js_ReserveObjects(JSContext *cx, size_t 
         obj->fslots[1] = INT_TO_JSVAL(i);
         head = obj;
     }
 
     return JS_TRUE;
 }
 #endif
 
+JSBool
+js_AddAsGCBytes(JSContext *cx, size_t sz)
+{
+    JSRuntime *rt;
+
+    rt = cx->runtime;
+    if (rt->gcBytes >= rt->gcMaxBytes ||
+        sz > (size_t) (rt->gcMaxBytes - rt->gcBytes) ||
+        IsGCThresholdReached(rt)) {
+        if (JS_ON_TRACE(cx)) {
+            /*
+             * If we can't leave the trace, signal OOM condition, otherwise
+             * exit from trace and proceed with GC.
+             */
+            if (!js_CanLeaveTrace(cx)) {
+                JS_UNLOCK_GC(rt);
+                return JS_FALSE;
+            }
+            js_LeaveTrace(cx);
+        }
+        js_GC(cx, GC_LAST_DITCH);
+        if (rt->gcBytes >= rt->gcMaxBytes ||
+            sz > (size_t) (rt->gcMaxBytes - rt->gcBytes)) {
+            JS_UNLOCK_GC(rt);
+            JS_ReportOutOfMemory(cx);
+            return JS_FALSE;
+        }
+    }
+    rt->gcBytes += (uint32) sz;
+    return JS_TRUE;
+}
+
+void
+js_RemoveAsGCBytes(JSRuntime *rt, size_t sz)
+{
+    JS_ASSERT((size_t) rt->gcBytes >= sz);
+    rt->gcBytes -= (uint32) sz;
+}
+
 /*
  * Shallow GC-things can be locked just by setting the GCF_LOCK bit, because
  * they have no descendants to mark during the GC. Currently the optimization
  * is only used for non-dependant strings.
  */
 #define GC_THING_IS_SHALLOW(flagp, thing)                                     \
     ((flagp) &&                                                               \
      ((*(flagp) & GCF_TYPEMASK) >= GCX_EXTERNAL_STRING ||                     \
@@ -3392,16 +3491,19 @@ js_GC(JSContext *cx, JSGCInvocationKind 
     if (JS_ON_TRACE(cx))
         goto out;
 #endif
     VOUCH_HAVE_STACK();
 
     /* Clear gcIsNeeded now, when we are about to start a normal GC cycle. */
     rt->gcIsNeeded = JS_FALSE;
 
+    /* Reset malloc counter. */
+    rt->gcMallocBytes = 0;
+
 #ifdef JS_DUMP_SCOPE_METERS
   { extern void js_DumpScopeMeters(JSRuntime *rt);
     js_DumpScopeMeters(rt);
   }
 #endif
 
 #ifdef JS_TRACER
     js_PurgeJITOracle();
@@ -3452,24 +3554,17 @@ js_GC(JSContext *cx, JSGCInvocationKind 
         JS_ASSERT(cx->insideGCMarkCallback);
         cx->insideGCMarkCallback = JS_FALSE;
     }
     JS_ASSERT(rt->gcTraceLaterCount == 0);
 
     rt->gcMarkingTracer = NULL;
 
 #ifdef JS_THREADSAFE
-    /*
-     * Deallocations occur in the background. The background thread will set
-     * gcFreed once it is done. At the next GC we will substract the amount
-     * of data we freed in the background from the previous GC cycle's
-     * RSS sample.
-     */
-    rt->gcFreed = 0;
-    cx->createDeallocatorTask(&rt->gcFreed);
+    cx->createDeallocatorTask();
 #endif
 
     /*
      * Sweep phase.
      *
      * Finalize as we sweep, outside of rt->gcLock but with rt->gcRunning set
      * so that any attempt to allocate a GC-thing from a finalizer will fail,
      * rather than nest badly and leave the unmarked newborn to be swept.
@@ -3640,26 +3735,17 @@ js_GC(JSContext *cx, JSGCInvocationKind 
     js_SweepScriptFilenames(rt);
 
     /*
      * Destroy arenas after we finished the sweeping sofinalizers can safely
      * use js_IsAboutToBeFinalized().
      */
     DestroyGCArenas(rt, emptyArenas);
 
-    /* Sample Resident Set Size (RSS). */
-    rt->gcLastRSS = js_GetRSS();
-
 #ifdef JS_THREADSAFE
-    /*
-     * It is important that we sample rt->gcLastRSS before submitting the
-     * deallocator task. This way we know that the RSS we sample ddoes not
-     * contain the bytes we will free in the background, which we will
-     * account for separately in rt->gcFreed.
-     */
     cx->submitDeallocatorTask();
 #endif
 
     if (rt->gcCallback)
         (void) rt->gcCallback(cx, JSGC_FINALIZE_END);
 #ifdef DEBUG_srcnotesize
   { extern void DumpSrcNoteSizeHist();
     DumpSrcNoteSizeHist();
@@ -3707,16 +3793,17 @@ out:
     if (!JS_ON_TRACE(cx) && (rt->gcLevel > 1 || rt->gcPoke)) {
         VOUCH_HAVE_STACK();
         rt->gcLevel = 1;
         rt->gcPoke = JS_FALSE;
         JS_UNLOCK_GC(rt);
         goto restart;
     }
 
+    rt->setGCLastBytes(rt->gcBytes);
   done_running:
     rt->gcLevel = 0;
     rt->gcRunning = rt->gcRegenShapes = false;
 
 #ifdef JS_THREADSAFE
     rt->gcThread = NULL;
     JS_NOTIFY_GC_DONE(rt);
 
@@ -3758,139 +3845,8 @@ out:
             /*
              * On shutdown iterate until JSGC_END callback stops creating
              * garbage.
              */
             goto restart_at_beginning;
         }
     }
 }
-
-void
-js_MaybeGC(JSContext *cx)
-{
-    JSRuntime *rt;
-
-    rt = cx->runtime;
-
-#ifdef JS_GC_ZEAL
-    if (rt->gcZeal > 0) {
-        JS_GC(cx);
-        return;
-    }
-#endif
-
-    size_t lastRSS = rt->gcLastRSS;
-    size_t freed = rt->gcFreed;
-    if (lastRSS > freed)
-        lastRSS -= freed;
-    size_t rss = js_GetRSS();
-    /* Trigger a GC if the working set grew by more than 32MB and at least 25%. */
-    if (rss > lastRSS + 32*1024*1024 && rss > lastRSS + lastRSS/4)
-        JS_GC(cx);
-}
-
-#ifdef JS_THREADSAFE
-void
-JSFreePointerListTask::add(void *ptr)
-{
-#ifdef DEBUG
-    memset(ptr, 0xcd, js_malloc_size(ptr));
-#endif
-    *(void**)ptr = head;
-    head = ptr;
-}
-
-void
-JSFreePointerListTask::run()
-{
-    size_t bytes = 0;
-    void *ptr = head;
-    while (ptr) {
-        void* next = *(void **)ptr;
-        bytes += js_malloc_size(ptr);
-        js_free(ptr);
-        ptr = next;
-    }
-    JS_ATOMIC_ADD(bytesp, bytes);
-}
-#endif
-
-#ifdef __APPLE__
-#include <mach/mach.h>
-#include <mach/task_info.h>
-size_t
-js_GetRSS()
-{
-    task_t task;
-    task_basic_info ti;
-    mach_msg_type_number_t count;
-
-    task_for_pid(mach_task_self (), getpid(), &task);
-    count = TASK_BASIC_INFO_COUNT;
-    task_info(task, TASK_BASIC_INFO, (task_info_t)&ti, &count);
-    return ti.resident_size;
-}
-#endif
-
-#ifdef WINCE
-#include "windows.h"
-
-size_t
-js_GetRSS()
-{
-    MEMORYSTATUS ms;
-    GlobalMemoryStatus(&ms);
-    return ms.dwTotalVirtual - ms.dwAvailVirtual;
-}
-#else
-#ifdef WIN32
-#include "windows.h"
-#include "psapi.h"
-size_t
-js_GetRSS()
-{
-    PROCESS_MEMORY_COUNTERS pmc;
-    GetProcessMemoryInfo(GetCurrentProcess(), &pmc, sizeof(pmc));
-    return pmc.WorkingSetSize;
-}
-#endif
-#endif
-
-#ifdef __linux__
-#include <unistd.h>
-#include <fcntl.h>
-static const char*
-skipFields(const char* p, unsigned n)
-{
-    while (*++p) {
-        if ((*p == ' ') && (--n == 0))
-            return p+1;
-    }
-    return NULL;
-}
-
-static int statfd = 0;
-
-size_t
-js_GetRSS()
-{
-    char buf[128];
-    if (!statfd) {
-        snprintf(buf, 100, "/proc/%d/stat", getpid());
-        statfd = open(buf, O_RDONLY);
-    }
-    if (statfd < 0)
-        return 0;
-    lseek(statfd, 0, SEEK_SET);
-    int n = read(statfd, buf, sizeof(buf)-1);
-    if (n < 0)
-        return 0;
-    buf[n] = 0;
-    const char* p = strrchr(buf, ')');
-    if (!p)
-        return 0;
-    p = skipFields(p + 2, 20);
-    if (!p)
-        return 0;
-    return atol(p);
-}
-#endif
--- a/js/src/jsgc.h
+++ b/js/src/jsgc.h
@@ -279,19 +279,16 @@ typedef enum JSGCInvocationKind {
      * jsgc.c just before js_GC's definition for details.
      */
     GC_LAST_DITCH       = GC_LOCK_HELD | 2
 } JSGCInvocationKind;
 
 extern void
 js_GC(JSContext *cx, JSGCInvocationKind gckind);
 
-extern void
-js_MaybeGC(JSContext *cx);
-
 typedef struct JSGCArenaInfo JSGCArenaInfo;
 typedef struct JSGCArenaList JSGCArenaList;
 typedef struct JSGCChunkInfo JSGCChunkInfo;
 
 struct JSGCArenaList {
     JSGCArenaInfo   *last;          /* last allocated GC arena */
     uint32          lastCount;      /* number of allocated things in the last
                                        arena */
@@ -324,41 +321,64 @@ struct JSWeakRoots {
     jsval           lastAtom;
 
     /* Root for the result of the most recent js_InternalInvoke call. */
     jsval           lastInternalResult;
 };
 
 #define JS_CLEAR_WEAK_ROOTS(wr) (memset((wr), 0, sizeof(JSWeakRoots)))
 
+/*
+ * Increase runtime->gcBytes by sz bytes to account for an allocation outside
+ * the GC that will be freed only after the GC is run. The function may run
+ * the last ditch GC to ensure that gcBytes does not exceed gcMaxBytes. It will
+ * fail if the latter is not possible.
+ *
+ * This function requires that runtime->gcLock is held on entry. On successful
+ * return the lock is still held and on failure it will be released with
+ * the error reported.
+ */
+extern JSBool
+js_AddAsGCBytes(JSContext *cx, size_t sz);
+
+extern void
+js_RemoveAsGCBytes(JSRuntime* rt, size_t sz);
+
 #ifdef JS_THREADSAFE
 class JSFreePointerListTask : public JSBackgroundTask {
     void *head;
-    size_t *bytesp;
   public:
-  JSFreePointerListTask(size_t *bytesp) : head(NULL), bytesp(bytesp) {}
+    JSFreePointerListTask() : head(NULL) {}
+
+    void add(void* ptr) {
+        *(void**)ptr = head;
+        head = ptr;
+    }
 
-    void add(void* ptr);
-    void run();
+    void run() {
+        void *ptr = head;
+        while (ptr) {
+            void *next = *(void **)ptr;
+            js_free(ptr);
+            ptr = next;
+        }
+    }
 };
 #endif
 
 /*
  * Free the chars held by str when it is finalized by the GC. When type is
  * less then zero, it denotes an internal string. Otherwise it denotes the
  * type of the external string allocated with JS_NewExternalString.
  *
  * This function always needs rt but can live with null cx.
  */
 extern void
 js_FinalizeStringRT(JSRuntime *rt, JSString *str, intN type, JSContext *cx);
 
-extern size_t
-js_GetRSS();
-
 #ifdef DEBUG_notme
 #define JS_GCMETER 1
 #endif
 
 #ifdef JS_GCMETER
 
 typedef struct JSGCArenaStats {
     uint32  alloc;          /* allocation attempts */
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -4966,16 +4966,21 @@ js_Enumerate(JSContext *cx, JSObject *ob
             JS_ASSERT(length == 0);
             JS_ASSERT(allocated == 0);
             *statep = JSVAL_ZERO;
         } else {
             JS_ASSERT(length != 0);
             JS_ASSERT(ne->cursor == (jsword) length);
             if (allocated != 0) {
                 JS_LOCK_GC(cx->runtime);
+                if (!js_AddAsGCBytes(cx, allocated)) {
+                    /* js_AddAsGCBytes releases the GC lock on failures. */
+                    cx->free(ne);
+                    return JS_FALSE;
+                }
                 ne->next = cx->runtime->nativeEnumerators;
                 cx->runtime->nativeEnumerators = ne;
                 JS_ASSERT(((jsuword) ne & (jsuword) 1) == (jsuword) 0);
                 *cachep = (jsuword) ne;
                 JS_UNLOCK_GC(cx->runtime);
             }
             *statep = PRIVATE_TO_JSVAL(ne);
         }
@@ -5054,16 +5059,17 @@ js_TraceNativeEnumerators(JSTracer *trc)
         if (ne->cursor != 0) {
             /* Trace ids of the running enumerator. */
             cursor = ne->ids;
             end = cursor + ne->length;
             do {
                 js_TraceId(trc, *cursor);
             } while (++cursor != end);
         } else if (doGC) {
+            js_RemoveAsGCBytes(rt, NativeEnumeratorSize(ne->length));
             *nep = ne->next;
             trc->context->free(ne);
             continue;
         }
         nep = &ne->next;
     }
 }
 
--- a/js/src/jsscope.cpp
+++ b/js/src/jsscope.cpp
@@ -167,16 +167,17 @@ JSScope::createTable(JSContext *cx, bool
     }
 
     table = (JSScopeProperty **) js_calloc(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
     if (!table) {
         if (report)
             JS_ReportOutOfMemory(cx);
         return false;
     }
+    cx->updateMallocCounter(JS_BIT(sizeLog2) * sizeof(JSScopeProperty *));
 
     hashShift = JS_DHASH_BITS - sizeLog2;
     for (sprop = lastProp; sprop; sprop = sprop->parent) {
         spp = search(sprop->id, true);
         SPROP_STORE_PRESERVING_COLLISION(spp, sprop);
     }
     return true;
 }
@@ -405,16 +406,19 @@ JSScope::changeTable(JSContext *cx, int 
         return false;
 
     /* Now that we have newtable allocated, update members. */
     hashShift = JS_DHASH_BITS - newlog2;
     removedCount = 0;
     oldtable = table;
     table = newtable;
 
+    /* Treat the above calloc as a JS_malloc, to match CreateScopeTable. */
+    cx->runtime->gcMallocBytes += nbytes;
+
     /* Copy only live entries, leaving removed and free ones behind. */
     for (oldspp = oldtable; oldsize != 0; oldspp++) {
         sprop = SPROP_FETCH(oldspp);
         if (sprop) {
             spp = search(sprop->id, true);
             JS_ASSERT(SPROP_IS_FREE(*spp));
             *spp = sprop;
         }
--- a/js/src/jsutil.h
+++ b/js/src/jsutil.h
@@ -197,31 +197,11 @@ static JS_INLINE void* js_realloc(void* 
         bytes = sizeof(void*);
     return realloc(p, bytes);
 }
 
 static JS_INLINE void js_free(void* p) {
     free(p);
 }
 
-#ifdef XP_UNIX
-#ifdef __APPLE__
-#include <malloc/malloc.h>
-static JS_INLINE size_t js_malloc_size(void* p) {
-    return malloc_size(p);
-}
-#else
-#include <malloc.h>
-static JS_INLINE size_t js_malloc_size(void* p) {
-    return malloc_usable_size(p);
-}
-#endif
-#endif
-#ifdef XP_WIN
-#include <malloc.h>
-static JS_INLINE size_t js_malloc_size(void* p) {
-    return _msize(p);
-}
-#endif
-
 JS_END_EXTERN_C
 
 #endif /* jsutil_h___ */
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -1139,22 +1139,26 @@ GCParameter(JSContext *cx, uintN argc, j
             return JS_FALSE;
         vp[2] = STRING_TO_JSVAL(str);
     }
     paramName = JS_GetStringBytes(str);
     if (!paramName)
         return JS_FALSE;
     if (strcmp(paramName, "maxBytes") == 0) {
         param = JSGC_MAX_BYTES;
+    } else if (strcmp(paramName, "maxMallocBytes") == 0) {
+        param = JSGC_MAX_MALLOC_BYTES;
     } else if (strcmp(paramName, "gcStackpoolLifespan") == 0) {
         param = JSGC_STACKPOOL_LIFESPAN;
     } else if (strcmp(paramName, "gcBytes") == 0) {
         param = JSGC_BYTES;
     } else if (strcmp(paramName, "gcNumber") == 0) {
         param = JSGC_NUMBER;
+    } else if (strcmp(paramName, "gcTriggerFactor") == 0) {
+        param = JSGC_TRIGGER_FACTOR;
     } else {
         JS_ReportError(cx,
                        "the first argument argument must be maxBytes, "
                        "maxMallocBytes, gcStackpoolLifespan, gcBytes, "
                        "gcNumber or gcTriggerFactor");
         return JS_FALSE;
     }
 
@@ -1171,16 +1175,21 @@ GCParameter(JSContext *cx, uintN argc, j
     }
 
     if (!JS_ValueToECMAUint32(cx, vp[3], &value)) {
         JS_ReportError(cx,
                        "the second argument must be convertable to uint32 "
                        "with non-zero value");
         return JS_FALSE;
     }
+    if (param == JSGC_TRIGGER_FACTOR && value < 100) {
+        JS_ReportError(cx,
+                       "the gcTriggerFactor value must be >= 100");
+        return JS_FALSE;
+    }
     JS_SetGCParameter(cx->runtime, param, value);
     *vp = JSVAL_VOID;
     return JS_TRUE;
 }
 
 #ifdef JS_GC_ZEAL
 static JSBool
 GCZeal(JSContext *cx, uintN argc, jsval *vp)
--- a/js/src/xpconnect/shell/xpcshell.cpp
+++ b/js/src/xpconnect/shell/xpcshell.cpp
@@ -145,96 +145,16 @@ FILE *gInFile = NULL;
 int gExitCode = 0;
 JSBool gQuitting = JS_FALSE;
 static JSBool reportWarnings = JS_TRUE;
 static JSBool compileOnly = JS_FALSE;
 
 JSPrincipals *gJSPrincipals = nsnull;
 nsAutoString *gWorkingDirectory = nsnull;
 
-static JSContext *gWatchdogContext = nsnull;
-
-#ifdef XP_WIN
-static HANDLE gTimerHandle = 0;
-
-VOID CALLBACK
-TimerCallback(PVOID lpParameter, BOOLEAN TimerOrWaitFired)
-{
-    JS_TriggerOperationCallback(gWatchdogContext);
-}
-
-static void
-EnableWatchdog(JSContext *cx)
-{
-    gWatchdogContext = cx;
-
-    if (gTimerHandle)
-        return;
-
-    if (!CreateTimerQueueTimer(&gTimerHandle,
-                               NULL,
-                               (WAITORTIMERCALLBACK)TimerCallback,
-                               cx->runtime,
-                               DWORD(1000),
-                               0,
-                               WT_EXECUTEINTIMERTHREAD | WT_EXECUTEONLYONCE))
-        gTimerHandle = 0;
-}
-
-static void
-DisableWatchdog()
-{
-    if (gTimerHandle) {
-        DeleteTimerQueueTimer(NULL, gTimerHandle, NULL);
-        gTimerHandle = 0;
-    }
-}
-#else
-static void
-AlarmHandler(int sig)
-{
-    JS_TriggerOperationCallback(gWatchdogContext);
-}
-
-static void
-EnableWatchdog(JSContext *cx)
-{
-    gWatchdogContext = cx;
-
-    signal(SIGALRM, AlarmHandler); /* set the Alarm signal capture */
-    alarm(1);
-}
-
-static void
-DisableWatchdog()
-{
-    alarm(0);
-    signal(SIGALRM, NULL);
-}
-#endif
-
-class Watchdog {
-public:
-    Watchdog(JSContext *cx) {
-        EnableWatchdog(cx);
-    }
-
-    ~Watchdog() {
-        DisableWatchdog();
-    }
-};
-
-static JSBool
-ShellOperationCallback(JSContext *cx)
-{
-    JS_MaybeGC(cx);
-
-    return JS_TRUE;
-}
-
 static JSBool
 GetLocationProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
 {
 #if (!defined(XP_WIN) && !defined(XP_UNIX)) || defined(WINCE)
     //XXX: your platform should really implement this
     return JS_FALSE;
 #else
     JSStackFrame *fp = JS_GetScriptedCaller(cx, NULL);
@@ -1593,17 +1513,16 @@ static JSBool
 ContextCallback(JSContext *cx, uintN contextOp)
 {
     if (gOldJSContextCallback && !gOldJSContextCallback(cx, contextOp))
         return JS_FALSE;
 
     if (contextOp == JSCONTEXT_NEW) {
         JS_SetErrorReporter(cx, my_ErrorReporter);
         JS_SetVersion(cx, JSVERSION_LATEST);
-        JS_SetOperationCallback(cx, ShellOperationCallback);
     }
     return JS_TRUE;
 }
 
 static bool
 GetCurrentWorkingDirectory(nsAString& workingDirectory)
 {
 #if (!defined(XP_WIN) && !defined(XP_UNIX)) || defined(WINCE)
@@ -1721,18 +1640,16 @@ main(int argc, char **argv, char **envp)
         gOldJSContextCallback = JS_SetContextCallback(rt, ContextCallback);
 
         cx = JS_NewContext(rt, 8192);
         if (!cx) {
             printf("JS_NewContext failed!\n");
             return 1;
         }
 
-        Watchdog watchdog(cx);
-
         nsCOMPtr<nsIXPConnect> xpc = do_GetService(nsIXPConnect::GetCID());
         if (!xpc) {
             printf("failed to get nsXPConnect service!\n");
             return 1;
         }
 
         // Since the caps security system might set a default security manager
         // we will be sure that the secman on this context gives full trust.
--- a/memory/jemalloc/jemalloc.c
+++ b/memory/jemalloc/jemalloc.c
@@ -6392,21 +6392,17 @@ free(void *ptr)
  * End malloc(3)-compatible functions.
  */
 /******************************************************************************/
 /*
  * Begin non-standard functions.
  */
 
 size_t
-#ifdef __linux__
-malloc_usable_size(void *ptr)
-#else
 malloc_usable_size(const void *ptr)
-#endif
 {
 
 #ifdef MALLOC_VALIDATE
 	return (isalloc_validate(ptr));
 #else
 	assert(ptr != NULL);
 
 	return (isalloc(ptr));
--- a/memory/jemalloc/jemalloc.h
+++ b/memory/jemalloc/jemalloc.h
@@ -87,21 +87,17 @@ void	*malloc(size_t size);
 void	*valloc(size_t size);
 void	*calloc(size_t num, size_t size);
 void	*realloc(void *ptr, size_t size);
 void	free(void *ptr);
 #endif
 
 int	posix_memalign(void **memptr, size_t alignment, size_t size);
 void	*memalign(size_t alignment, size_t size);
-#ifdef __linux
-size_t	malloc_usable_size(void *ptr);
-#else
 size_t	malloc_usable_size(const void *ptr);
-#endif
 void	jemalloc_stats(jemalloc_stats_t *stats);
 
 /* The x*() functions never return NULL. */
 void	*xmalloc(size_t size);
 void	*xcalloc(size_t num, size_t size);
 void	*xrealloc(void *ptr, size_t size);
 void	*xmemalign(size_t alignment, size_t size);