Bug 1189490 - Part 1: Add a StaticTraceable version of the FIFO queue for use with GC things; r=terrence
☠☠ backed out by 91cb27a1be1e ☠ ☠
authorNick Fitzgerald <fitzgen@gmail.com>
Fri, 31 Jul 2015 13:27:44 -0700
changeset 287407 78f80496c70ac7005fd0ab0de91944f3df672106
parent 287406 cebec1f7c9dba4fe6d17270ae905108695396ba4
child 287408 5158c4514c346377f6327b6bf8a3fa9937e0815d
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 StaticTraceable 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,124 @@
+/* -*- 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> 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::StaticTraceable
+{
+    using Base = js::Fifo<T, MinInlineCapacity, AllocPolicy>;
+
+  public:
+    explicit TraceableFifo(AllocPolicy alloc = AllocPolicy()) : Base(alloc) {}
+
+    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));
@@ -270,16 +271,55 @@ BEGIN_TEST(testGCRootedVector)
     for (auto shape : shapes) {
         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());
--- 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',