Bug 835966 - Refactor {cx,rt,allocator}->malloc_ (r=luke)
authorBill McCloskey <wmccloskey@mozilla.com>
Wed, 30 Jan 2013 12:22:04 -0800
changeset 130273 da9f300569abf47d595dc3b985a11165c440d086
parent 130272 47354ad87a98bd26f5395bf61c39c52d7ad93ce9
child 130274 78ecce4ba21467747c77bb7293f3a071c44ad5d0
push id2323
push userbbajaj@mozilla.com
push dateMon, 01 Apr 2013 19:47:02 +0000
treeherdermozilla-beta@7712be144d91 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs835966
milestone21.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 835966 - Refactor {cx,rt,allocator}->malloc_ (r=luke)
js/src/jscntxt.cpp
js/src/jscntxt.h
js/src/jscompartment.h
js/src/jscompartmentinlines.h
js/src/jsgc.cpp
--- a/js/src/jscntxt.cpp
+++ b/js/src/jscntxt.cpp
@@ -1338,35 +1338,47 @@ JSRuntime::setGCMaxMallocBytes(size_t va
      * mean that value.
      */
     gcMaxMallocBytes = (ptrdiff_t(value) >= 0) ? value : size_t(-1) >> 1;
     for (CompartmentsIter c(this); !c.done(); c.next())
         c->setGCMaxMallocBytes(value);
 }
 
 void
-JSRuntime::updateMallocCounter(JSCompartment *comp, size_t nbytes)
+JSRuntime::updateMallocCounter(size_t nbytes)
+{
+    updateMallocCounter(NULL, nbytes);
+}
+
+void
+JSRuntime::updateMallocCounter(JS::Zone *zone, size_t nbytes)
 {
     /* We tolerate any thread races when updating gcMallocBytes. */
     ptrdiff_t oldCount = gcMallocBytes;
     ptrdiff_t newCount = oldCount - ptrdiff_t(nbytes);
     gcMallocBytes = newCount;
     if (JS_UNLIKELY(newCount <= 0 && oldCount > 0))
         onTooMuchMalloc();
-    else if (comp)
-        comp->updateMallocCounter(nbytes);
+    else if (zone)
+        zone->updateMallocCounter(nbytes);
 }
 
 JS_FRIEND_API(void)
 JSRuntime::onTooMuchMalloc()
 {
     TriggerGC(this, gcreason::TOO_MUCH_MALLOC);
 }
 
 JS_FRIEND_API(void *)
+JSRuntime::onOutOfMemory(void *p, size_t nbytes)
+{
+    return onOutOfMemory(p, nbytes, NULL);
+}
+
+JS_FRIEND_API(void *)
 JSRuntime::onOutOfMemory(void *p, size_t nbytes, JSContext *cx)
 {
     if (isHeapBusy())
         return NULL;
 
     /*
      * Retry when we are done with the background sweeping and have stopped
      * all the allocations and released the empty GC chunks.
--- a/js/src/jscntxt.h
+++ b/js/src/jscntxt.h
@@ -490,34 +490,106 @@ class PerThreadData : public js::PerThre
      */
     int32_t             suppressGC;
 
     PerThreadData(JSRuntime *runtime);
 
     bool associatedWith(const JSRuntime *rt) { return runtime_ == rt; }
 };
 
+template<class Client>
+struct MallocProvider
+{
+    void *malloc_(size_t bytes) {
+        Client *client = static_cast<Client *>(this);
+        client->updateMallocCounter(bytes);
+        void *p = js_malloc(bytes);
+        return JS_LIKELY(!!p) ? p : client->onOutOfMemory(NULL, bytes);
+    }
+
+    void *calloc_(size_t bytes) {
+        Client *client = static_cast<Client *>(this);
+        client->updateMallocCounter(bytes);
+        void *p = js_calloc(bytes);
+        return JS_LIKELY(!!p) ? p : client->onOutOfMemory(reinterpret_cast<void *>(1), bytes);
+    }
+
+    void *realloc_(void *p, size_t oldBytes, size_t newBytes) {
+        Client *client = static_cast<Client *>(this);
+        JS_ASSERT(oldBytes < newBytes);
+        client->updateMallocCounter(newBytes - oldBytes);
+        void *p2 = js_realloc(p, newBytes);
+        return JS_LIKELY(!!p2) ? p2 : client->onOutOfMemory(p, newBytes);
+    }
+
+    void *realloc_(void *p, size_t bytes) {
+        Client *client = static_cast<Client *>(this);
+        /*
+         * For compatibility we do not account for realloc that increases
+         * previously allocated memory.
+         */
+        if (!p)
+            client->updateMallocCounter(bytes);
+        void *p2 = js_realloc(p, bytes);
+        return JS_LIKELY(!!p2) ? p2 : client->onOutOfMemory(p, bytes);
+    }
+
+    template <class T>
+    T *pod_malloc() {
+        return (T *)malloc_(sizeof(T));
+    }
+
+    template <class T>
+    T *pod_calloc() {
+        return (T *)calloc_(sizeof(T));
+    }
+
+    template <class T>
+    T *pod_malloc(size_t numElems) {
+        if (numElems & js::tl::MulOverflowMask<sizeof(T)>::result) {
+            Client *client = static_cast<Client *>(this);
+            client->reportAllocationOverflow();
+            return NULL;
+        }
+        return (T *)malloc_(numElems * sizeof(T));
+    }
+
+    template <class T>
+    T *pod_calloc(size_t numElems, JSCompartment *comp = NULL, JSContext *cx = NULL) {
+        if (numElems & js::tl::MulOverflowMask<sizeof(T)>::result) {
+            Client *client = static_cast<Client *>(this);
+            client->reportAllocationOverflow();
+            return NULL;
+        }
+        return (T *)calloc_(numElems * sizeof(T));
+    }
+
+    JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE)
+};
+
 namespace gc {
 class MarkingValidator;
 } // namespace gc
 
 } // namespace js
 
-struct JSRuntime : js::RuntimeFriendFields
+struct JSRuntime : js::RuntimeFriendFields,
+                   public js::MallocProvider<JSRuntime>
 {
-    /* Per-thread data for the main thread that is associated with
+    /*
+     * Per-thread data for the main thread that is associated with
      * this JSRuntime, as opposed to any worker threads used in
      * parallel sections.  See definition of |PerThreadData| struct
      * above for more details.
      *
      * NB: This field is statically asserted to be at offset
      * sizeof(RuntimeFriendFields). See
      * PerThreadDataFriendFields::getMainThread.
      */
-    js::PerThreadData mainThread;
+    js::PerThreadData   mainThread;
 
     /* Default compartment. */
     JSCompartment       *atomsCompartment;
 
     /* List of compartments (protected by the GC lock). */
     js::CompartmentVector compartments;
 
     /* See comment for JS_AbortIfWrongThread in jsapi.h. */
@@ -1116,75 +1188,32 @@ struct JSRuntime : js::RuntimeFriendFiel
 
     JSRuntime(JSUseHelperThreads useHelperThreads);
     ~JSRuntime();
 
     bool init(uint32_t maxbytes);
 
     JSRuntime *thisFromCtor() { return this; }
 
-    /*
-     * Call the system malloc while checking for GC memory pressure and
-     * reporting OOM error when cx is not null. We will not GC from here.
-     */
-    inline void *malloc_(size_t bytes, JSCompartment *comp = NULL, JSContext *cx = NULL);
-
-    /*
-     * Call the system calloc while checking for GC memory pressure and
-     * reporting OOM error when cx is not null. We will not GC from here.
-     */
-    inline void *calloc_(size_t bytes, JSCompartment *comp = NULL, JSContext *cx = NULL);
-
-    inline void *realloc_(void *p, size_t oldBytes, size_t newBytes, JSCompartment *comp = NULL, JSContext *cx = NULL);
-
-    inline void *realloc_(void *p, size_t bytes, JSCompartment *comp = NULL, JSContext *cx = NULL);
-
-    template <class T>
-    T *pod_malloc(JSCompartment *comp = NULL, JSContext *cx = NULL) {
-        return (T *)malloc_(sizeof(T), comp, cx);
-    }
-
-    template <class T>
-    T *pod_calloc(JSCompartment *comp = NULL, JSContext *cx = NULL) {
-        return (T *)calloc_(sizeof(T), comp, cx);
-    }
-
-    template <class T>
-    T *pod_malloc(size_t numElems, JSCompartment *comp = NULL, JSContext *cx = NULL) {
-        if (numElems & js::tl::MulOverflowMask<sizeof(T)>::result) {
-            js_ReportAllocationOverflow(cx);
-            return NULL;
-        }
-        return (T *)malloc_(numElems * sizeof(T), comp, cx);
-    }
-
-    template <class T>
-    T *pod_calloc(size_t numElems, JSCompartment *comp = NULL, JSContext *cx = NULL) {
-        if (numElems & js::tl::MulOverflowMask<sizeof(T)>::result) {
-            js_ReportAllocationOverflow(cx);
-            return NULL;
-        }
-        return (T *)calloc_(numElems * sizeof(T), comp, cx);
-    }
-
-    JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE)
-
     void setGCMaxMallocBytes(size_t value);
 
     void resetGCMallocBytes() { gcMallocBytes = ptrdiff_t(gcMaxMallocBytes); }
 
     /*
      * Call this after allocating memory held by GC things, to update memory
      * pressure counters or report the OOM error if necessary. If oomError and
      * cx is not null the function also reports OOM error.
      *
      * The function must be called outside the GC lock and in case of OOM error
      * the caller must ensure that no deadlock possible during OOM reporting.
      */
-    void updateMallocCounter(JSCompartment *comp, size_t nbytes);
+    void updateMallocCounter(size_t nbytes);
+    void updateMallocCounter(JS::Zone *zone, size_t nbytes);
+
+    void reportAllocationOverflow() { js_ReportAllocationOverflow(NULL); }
 
     bool isTooMuchMalloc() const {
         return gcMallocBytes <= 0;
     }
 
     /*
      * The function must be called outside the GC lock.
      */
@@ -1193,16 +1222,17 @@ struct JSRuntime : js::RuntimeFriendFiel
     /*
      * This should be called after system malloc/realloc returns NULL to try
      * to recove some memory or to report an error. Failures in malloc and
      * calloc are signaled by p == null and p == reinterpret_cast<void *>(1).
      * Other values of p mean a realloc failure.
      *
      * The function must be called outside the GC lock.
      */
+    JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes);
     JS_FRIEND_API(void *) onOutOfMemory(void *p, size_t nbytes, JSContext *cx);
 
     void triggerOperationCallback();
 
     void setJitHardening(bool enabled);
     bool getJitHardening() const {
         return jitHardening;
     }
@@ -1371,17 +1401,18 @@ FreeOp::free_(void *p)
         return;
     }
     js_free(p);
 }
 
 } /* namespace js */
 
 struct JSContext : js::ContextFriendFields,
-                   public mozilla::LinkedListElement<JSContext>
+                   public mozilla::LinkedListElement<JSContext>,
+                   public js::MallocProvider<JSContext>
 {
     explicit JSContext(JSRuntime *rt);
     JSContext *thisDuringConstruction() { return this; }
     ~JSContext();
 
     inline JS::Zone *zone();
     js::PerThreadData &mainThread() { return runtime->mainThread; }
 
@@ -1650,51 +1681,26 @@ struct JSContext : js::ContextFriendFiel
   private:
     /* Innermost-executing generator or null if no generator are executing. */
     JSGenerator *innermostGenerator_;
   public:
     JSGenerator *innermostGenerator() const { return innermostGenerator_; }
     void enterGenerator(JSGenerator *gen);
     void leaveGenerator(JSGenerator *gen);
 
-    inline void *malloc_(size_t bytes) {
-        return runtime->malloc_(bytes, compartment, this);
-    }
-
-    inline void *calloc_(size_t bytes) {
-        return runtime->calloc_(bytes, compartment, this);
-    }
-
-    inline void *realloc_(void *p, size_t bytes) {
-        return runtime->realloc_(p, bytes, compartment, this);
-    }
-
-    inline void *realloc_(void *p, size_t oldBytes, size_t newBytes) {
-        return runtime->realloc_(p, oldBytes, newBytes, compartment, this);
+    void *onOutOfMemory(void *p, size_t nbytes) {
+        return runtime->onOutOfMemory(p, nbytes, this);
     }
-
-    template <class T> T *pod_malloc() {
-        return runtime->pod_malloc<T>(compartment, this);
-    }
-
-    template <class T> T *pod_calloc() {
-        return runtime->pod_calloc<T>(compartment, this);
+    void updateMallocCounter(size_t nbytes) {
+        runtime->updateMallocCounter(compartment, nbytes);
     }
-
-    template <class T> T *pod_malloc(size_t numElems) {
-        return runtime->pod_malloc<T>(numElems, compartment, this);
+    void reportAllocationOverflow() {
+        js_ReportAllocationOverflow(this);
     }
 
-    template <class T>
-    T *pod_calloc(size_t numElems) {
-        return runtime->pod_calloc<T>(numElems, compartment, this);
-    }
-
-    JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE)
-
     void purge();
 
     bool isExceptionPending() {
         return throwing;
     }
 
     js::Value getPendingException() {
         JS_ASSERT(throwing);
@@ -2301,51 +2307,9 @@ class ContextAllocPolicy
 
 } /* namespace js */
 
 #ifdef _MSC_VER
 #pragma warning(pop)
 #pragma warning(pop)
 #endif
 
-void *
-JSRuntime::malloc_(size_t bytes, JSCompartment *comp, JSContext *cx)
-{
-    JS_ASSERT_IF(cx != NULL, cx->compartment == comp);
-    updateMallocCounter(comp, bytes);
-    void *p = js_malloc(bytes);
-    return JS_LIKELY(!!p) ? p : onOutOfMemory(NULL, bytes, cx);
-}
-
-void *
-JSRuntime::calloc_(size_t bytes, JSCompartment *comp, JSContext *cx)
-{
-    JS_ASSERT_IF(cx != NULL, cx->compartment == comp);
-    updateMallocCounter(comp, bytes);
-    void *p = js_calloc(bytes);
-    return JS_LIKELY(!!p) ? p : onOutOfMemory(reinterpret_cast<void *>(1), bytes, cx);
-}
-
-void *
-JSRuntime::realloc_(void *p, size_t oldBytes, size_t newBytes, JSCompartment *comp, JSContext *cx)
-{
-    JS_ASSERT_IF(cx != NULL, cx->compartment == comp);
-    JS_ASSERT(oldBytes < newBytes);
-    updateMallocCounter(comp, newBytes - oldBytes);
-    void *p2 = js_realloc(p, newBytes);
-    return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, newBytes, cx);
-}
-
-void *
-JSRuntime::realloc_(void *p, size_t bytes, JSCompartment *comp, JSContext *cx)
-{
-    JS_ASSERT_IF(cx != NULL, cx->compartment == comp);
-    /*
-     * For compatibility we do not account for realloc that increases
-     * previously allocated memory.
-     */
-    if (!p)
-        updateMallocCounter(comp, bytes);
-    void *p2 = js_realloc(p, bytes);
-    return JS_LIKELY(!!p2) ? p2 : onOutOfMemory(p, bytes, cx);
-}
-
 #endif /* jscntxt_h___ */
--- a/js/src/jscompartment.h
+++ b/js/src/jscompartment.h
@@ -127,36 +127,30 @@ namespace js {
  * Encapsulates the data needed to perform allocation.  Typically
  * there is precisely one of these per compartment
  * (|compartment.allocator|).  However, in parallel execution mode,
  * there will be one per worker thread.  In general, if a piece of
  * code must perform execution and should work safely either in
  * parallel or sequential mode, you should make it take an
  * |Allocator*| rather than a |JSContext*|.
  */
-class Allocator
+class Allocator : public MallocProvider<Allocator>
 {
-    JSCompartment*const compartment;
+    JS::Zone *zone;
 
   public:
-    explicit Allocator(JSCompartment *compartment);
+    explicit Allocator(JS::Zone *zone);
 
     js::gc::ArenaLists arenas;
 
     inline void *parallelNewGCThing(gc::AllocKind thingKind, size_t thingSize);
 
-    inline void *malloc_(size_t bytes);
-    inline void *calloc_(size_t bytes);
-    inline void *realloc_(void *p, size_t bytes);
-    inline void *realloc_(void *p, size_t oldBytes, size_t newBytes);
-    template <class T> inline T *pod_malloc();
-    template <class T> inline T *pod_calloc();
-    template <class T> inline T *pod_malloc(size_t numElems);
-    template <class T> inline T *pod_calloc(size_t numElems);
-    JS_DECLARE_NEW_METHODS(new_, malloc_, JS_ALWAYS_INLINE)
+    inline void *onOutOfMemory(void *p, size_t nbytes);
+    inline void updateMallocCounter(size_t nbytes);
+    inline void reportAllocationOverflow();
 };
 
 } /* namespace js */
 
 struct JSCompartment : private JS::shadow::Zone, public js::gc::GraphNodeBase<JSCompartment>
 {
     JSRuntime                    *rt;
     JSPrincipals                 *principals;
--- a/js/src/jscompartmentinlines.h
+++ b/js/src/jscompartmentinlines.h
@@ -22,67 +22,33 @@ js::AutoCompartment::AutoCompartment(JSC
     cx_->enterCompartment(target->compartment());
 }
 
 js::AutoCompartment::~AutoCompartment()
 {
     cx_->leaveCompartment(origin_);
 }
 
-inline void *
-js::Allocator::malloc_(size_t bytes)
-{
-    return compartment->rt->malloc_(bytes, compartment);
-}
-
-inline void *
-js::Allocator::calloc_(size_t bytes)
+void *
+js::Allocator::onOutOfMemory(void *p, size_t nbytes)
 {
-    return compartment->rt->calloc_(bytes, compartment);
-}
-
-inline void *
-js::Allocator::realloc_(void *p, size_t bytes)
-{
-    return compartment->rt->realloc_(p, bytes, compartment);
-}
-
-inline void *
-js::Allocator::realloc_(void* p, size_t oldBytes, size_t newBytes)
-{
-    return compartment->rt->realloc_(p, oldBytes, newBytes, compartment);
+    return zone->rt->onOutOfMemory(p, nbytes);
 }
 
-template <class T>
-inline T *
-js::Allocator::pod_malloc()
+void
+js::Allocator::updateMallocCounter(size_t nbytes)
 {
-    return compartment->rt->pod_malloc<T>(compartment);
+    zone->rt->updateMallocCounter(zone, nbytes);
 }
 
-template <class T>
-inline T *
-js::Allocator::pod_calloc()
+void
+js::Allocator::reportAllocationOverflow()
 {
-    return compartment->rt->pod_calloc<T>(compartment);
-}
-
-template <class T>
-inline T *
-js::Allocator::pod_malloc(size_t numElems)
-{
-    return compartment->rt->pod_malloc<T>(numElems, compartment);
-}
-
-template <class T>
-inline T *
-js::Allocator::pod_calloc(size_t numElems)
-{
-    return compartment->rt->pod_calloc<T>(numElems, compartment);
+    js_ReportAllocationOverflow(NULL);
 }
 
 inline void *
 js::Allocator::parallelNewGCThing(gc::AllocKind thingKind, size_t thingSize)
 {
-    return arenas.parallelAllocate(compartment, thingKind, thingSize);
+    return arenas.parallelAllocate(zone, thingKind, thingSize);
 }
 
 #endif /* jscompartment_inlines_h___ */
--- a/js/src/jsgc.cpp
+++ b/js/src/jsgc.cpp
@@ -1155,18 +1155,18 @@ JSCompartment::reduceGCTriggerBytes(size
 {
     JS_ASSERT(amount > 0);
     JS_ASSERT(gcTriggerBytes >= amount);
     if (gcTriggerBytes - amount < rt->gcAllocationThreshold * gcHeapGrowthFactor)
         return;
     gcTriggerBytes -= amount;
 }
 
-Allocator::Allocator(JSCompartment *compartment)
-  : compartment(compartment)
+Allocator::Allocator(Zone *zone)
+  : zone(zone)
 {}
 
 inline void
 ArenaLists::prepareForIncrementalGC(JSRuntime *rt)
 {
     for (size_t i = 0; i != FINALIZE_LIMIT; ++i) {
         FreeSpan *headSpan = &freeLists[i];
         if (!headSpan->isEmpty()) {