Bug 1189490 - Part 1: Add a JS::Traceable version of the FIFO queue for use with GC things; r=terrence
☠☠ backed out by af32792fe260 ☠ ☠
authorNick Fitzgerald <fitzgen@gmail.com>
Sat, 08 Aug 2015 15:19:52 -0700
changeset 288628 1a1ad27321de3383031cc3fdf38d042ada6c34ee
parent 288627 858b4f682c5a5367ee10d001c27badb1de887373
child 288629 8bf626d3a647f94a8512c831f7f634e8c98acee1
push id5067
push userraliiev@mozilla.com
push dateMon, 21 Sep 2015 14:04:52 +0000
treeherdermozilla-beta@14221ffe5b2f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersterrence
bugs1189490
milestone42.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 1189490 - Part 1: Add a JS::Traceable version of the FIFO queue for use with GC things; r=terrence
js/public/TraceableFifo.h
js/src/jsapi-tests/testGCExactRooting.cpp
js/src/moz.build
new file mode 100644
--- /dev/null
+++ b/js/public/TraceableFifo.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef js_TraceableFifo_h
+#define js_TraceableFifo_h
+
+#include "js/RootingAPI.h"
+#include "js/Fifo.h"
+
+namespace js {
+
+template <typename, typename> struct DefaultTracer;
+
+// A TraceableFifo is a Fifo with an additional trace method that knows how to
+// visit all of the items stored in the Fifo. For Fifos that contain GC things,
+// this is usually more convenient than manually iterating and marking the
+// contents.
+//
+// Most types of GC pointers as keys and values can be traced with no extra
+// infrastructure. For structs and non-gc-pointer members, ensure that there is
+// a specialization of DefaultTracer<T> with an appropriate trace method
+// available to handle the custom type.
+//
+// Note that although this Fifo's trace will deal correctly with moved items, it
+// does not itself know when to barrier or trace items. To function properly it
+// must either be used with Rooted, or barriered and traced manually.
+template <typename T,
+          size_t MinInlineCapacity = 0,
+          typename AllocPolicy = TempAllocPolicy,
+          typename TraceFunc = DefaultTracer<T>>
+class TraceableFifo
+  : public js::Fifo<T, MinInlineCapacity, AllocPolicy>,
+    public JS::Traceable
+{
+    using Base = js::Fifo<T, MinInlineCapacity, AllocPolicy>;
+
+  public:
+    explicit TraceableFifo(AllocPolicy alloc = AllocPolicy()) : Base(alloc) {}
+
+    TraceableFifo(TraceableFifo&& rhs) : Base(mozilla::Move(rhs)) { }
+    TraceableFifo& operator=(TraceableFifo&& rhs) { return Base::operator=(mozilla::Move(rhs)); }
+
+    TraceableFifo(const TraceableFifo&) = delete;
+    TraceableFifo& operator=(const TraceableFifo&) = delete;
+
+    static void trace(TraceableFifo* tf, JSTracer* trc) {
+        for (size_t i = 0; i < tf->front_.length(); ++i)
+            TraceFunc::trace(trc, &tf->front_[i], "fifo element");
+        for (size_t i = 0; i < tf->rear_.length(); ++i)
+            TraceFunc::trace(trc, &tf->rear_[i], "fifo element");
+    }
+};
+
+template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
+class TraceableFifoOperations
+{
+    using TF = TraceableFifo<T, Capacity, AllocPolicy, TraceFunc>;
+    const TF& fifo() const { return static_cast<const Outer*>(this)->extract(); }
+
+  public:
+    size_t length() const { return fifo().length(); }
+    bool empty() const { return fifo().empty(); }
+    const T& front() const { return fifo().front(); }
+};
+
+template <typename Outer, typename T, size_t Capacity, typename AllocPolicy, typename TraceFunc>
+class MutableTraceableFifoOperations
+  : public TraceableFifoOperations<Outer, T, Capacity, AllocPolicy, TraceFunc>
+{
+    using TF = TraceableFifo<T, Capacity, AllocPolicy, TraceFunc>;
+    TF& fifo() { return static_cast<Outer*>(this)->extract(); }
+
+  public:
+    T& front() { return fifo().front(); }
+
+    template<typename U> bool pushBack(U&& u) { return fifo().pushBack(mozilla::Forward<U>(u)); }
+    template<typename... Args> bool emplaceBack(Args&&... args) {
+        return fifo().emplaceBack(mozilla::Forward<Args...>(args...));
+    }
+
+    bool popFront() { return fifo().popFront(); }
+    void clear() { fifo().clear(); }
+};
+
+template <typename A, size_t B, typename C, typename D>
+class RootedBase<TraceableFifo<A,B,C,D>>
+  : public MutableTraceableFifoOperations<JS::Rooted<TraceableFifo<A,B,C,D>>, A,B,C,D>
+{
+    using TF = TraceableFifo<A,B,C,D>;
+
+    friend class TraceableFifoOperations<JS::Rooted<TF>, A,B,C,D>;
+    const TF& extract() const { return *static_cast<const JS::Rooted<TF>*>(this)->address(); }
+
+    friend class MutableTraceableFifoOperations<JS::Rooted<TF>, A,B,C,D>;
+    TF& extract() { return *static_cast<JS::Rooted<TF>*>(this)->address(); }
+};
+
+template <typename A, size_t B, typename C, typename D>
+class MutableHandleBase<TraceableFifo<A,B,C,D>>
+  : public MutableTraceableFifoOperations<JS::MutableHandle<TraceableFifo<A,B,C,D>>, A,B,C,D>
+{
+    using TF = TraceableFifo<A,B,C,D>;
+
+    friend class TraceableFifoOperations<JS::MutableHandle<TF>, A,B,C,D>;
+    const TF& extract() const {
+        return *static_cast<const JS::MutableHandle<TF>*>(this)->address();
+    }
+
+    friend class MutableTraceableFifoOperations<JS::MutableHandle<TF>, A,B,C,D>;
+    TF& extract() { return *static_cast<JS::MutableHandle<TF>*>(this)->address(); }
+};
+
+template <typename A, size_t B, typename C, typename D>
+class HandleBase<TraceableFifo<A,B,C,D>>
+  : public TraceableFifoOperations<JS::Handle<TraceableFifo<A,B,C,D>>, A,B,C,D>
+{
+    using TF = TraceableFifo<A,B,C,D>;
+
+    friend class TraceableFifoOperations<JS::Handle<TF>, A,B,C,D>;
+    const TF& extract() const {
+        return *static_cast<const JS::Handle<TF>*>(this)->address();
+    }
+};
+
+} // namespace js
+
+#endif // js_TraceableFifo_h
--- a/js/src/jsapi-tests/testGCExactRooting.cpp
+++ b/js/src/jsapi-tests/testGCExactRooting.cpp
@@ -1,16 +1,17 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
 * vim: set ts=8 sts=4 et sw=4 tw=99:
 */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "js/RootingAPI.h"
+#include "js/TraceableFifo.h"
 #include "js/TraceableHashTable.h"
 #include "js/TraceableVector.h"
 
 #include "jsapi-tests/tests.h"
 
 BEGIN_TEST(testGCExactRooting)
 {
     JS::RootedObject rootCx(cx, JS_NewPlainObject(cx));
@@ -271,16 +272,57 @@ receiveMutableHandleToShapeVector(JS::Mu
     // Ensure range enumeration works through the handle.
     for (auto shape : handle) {
         CHECK(shape);
     }
     return true;
 }
 END_TEST(testGCRootedVector)
 
+BEGIN_TEST(testTraceableFifo)
+{
+    using ShapeFifo = TraceableFifo<Shape*>;
+    JS::Rooted<ShapeFifo> shapes(cx, ShapeFifo(cx));
+    CHECK(shapes.empty());
+
+    for (size_t i = 0; i < 10; ++i) {
+        RootedObject obj(cx, JS_NewObject(cx, nullptr));
+        RootedValue val(cx, UndefinedValue());
+        // Construct a unique property name to ensure that the object creates a
+        // new shape.
+        char buffer[2];
+        buffer[0] = 'a' + i;
+        buffer[1] = '\0';
+        CHECK(JS_SetProperty(cx, obj, buffer, val));
+        CHECK(shapes.pushBack(obj->as<NativeObject>().lastProperty()));
+    }
+
+    CHECK(shapes.length() == 10);
+
+    JS_GC(rt);
+    JS_GC(rt);
+
+    for (size_t i = 0; i < 10; ++i) {
+        // Check the shape to ensure it did not get collected.
+        char buffer[2];
+        buffer[0] = 'a' + i;
+        buffer[1] = '\0';
+        bool match;
+        CHECK(JS_StringEqualsAscii(cx, JSID_TO_STRING(shapes.front()->propid()), buffer, &match));
+        CHECK(match);
+        CHECK(shapes.popFront());
+    }
+
+    CHECK(shapes.empty());
+    return true;
+}
+END_TEST(testTraceableFifo)
+
+using ShapeVec = TraceableVector<Shape*>;
+
 static bool
 FillVector(JSContext* cx, MutableHandle<ShapeVec> shapes)
 {
     for (size_t i = 0; i < 10; ++i) {
         RootedObject obj(cx, JS_NewObject(cx, nullptr));
         RootedValue val(cx, UndefinedValue());
         // Construct a unique property name to ensure that the object creates a
         // new shape.
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -118,16 +118,17 @@ EXPORTS.js += [
     '../public/Principals.h',
     '../public/ProfilingFrameIterator.h',
     '../public/ProfilingStack.h',
     '../public/Proxy.h',
     '../public/RequiredDefines.h',
     '../public/RootingAPI.h',
     '../public/SliceBudget.h',
     '../public/StructuredClone.h',
+    '../public/TraceableFifo.h',
     '../public/TraceableHashTable.h',
     '../public/TraceableVector.h',
     '../public/TraceKind.h',
     '../public/TracingAPI.h',
     '../public/TrackedOptimizationInfo.h',
     '../public/TypeDecls.h',
     '../public/UbiNode.h',
     '../public/UbiNodeTraverse.h',