LCOV - code coverage report
Current view: top level - js/src/gc - NurseryAwareHashMap.h (source / functions) Hit Total Coverage
Test: output.info Lines: 47 66 71.2 %
Date: 2017-07-14 16:53:18 Functions: 17 23 73.9 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef gc_NurseryAwareHashMap_h
       8             : #define gc_NurseryAwareHashMap_h
       9             : 
      10             : #include "gc/Barrier.h"
      11             : #include "gc/Marking.h"
      12             : #include "js/GCHashTable.h"
      13             : #include "js/GCPolicyAPI.h"
      14             : #include "js/HashTable.h"
      15             : 
      16             : namespace js {
      17             : 
      18             : namespace detail {
      19             : // This class only handles the incremental case and does not deal with nursery
      20             : // pointers. The only users should be for NurseryAwareHashMap; it is defined
      21             : // externally because we need a GCPolicy for its use in the contained map.
      22             : template <typename T>
      23             : class UnsafeBareReadBarriered : public ReadBarrieredBase<T>
      24             : {
      25             :   public:
      26             :     UnsafeBareReadBarriered() : ReadBarrieredBase<T>(JS::GCPolicy<T>::initial()) {}
      27        9597 :     MOZ_IMPLICIT UnsafeBareReadBarriered(const T& v) : ReadBarrieredBase<T>(v) {}
      28             :     explicit UnsafeBareReadBarriered(const UnsafeBareReadBarriered& v) : ReadBarrieredBase<T>(v) {}
      29       12461 :     UnsafeBareReadBarriered(UnsafeBareReadBarriered&& v)
      30       12461 :       : ReadBarrieredBase<T>(mozilla::Move(v))
      31       12461 :     {}
      32             : 
      33           0 :     UnsafeBareReadBarriered& operator=(const UnsafeBareReadBarriered& v) {
      34           0 :         this->value = v.value;
      35           0 :         return *this;
      36             :     }
      37             : 
      38          12 :     UnsafeBareReadBarriered& operator=(const T& v) {
      39          12 :         this->value = v;
      40          12 :         return *this;
      41             :     }
      42             : 
      43       20105 :     const T get() const {
      44       20105 :         if (!InternalBarrierMethods<T>::isMarkable(this->value))
      45           0 :             return JS::GCPolicy<T>::initial();
      46       20105 :         this->read();
      47       20105 :         return this->value;
      48             :     }
      49             : 
      50             :     explicit operator bool() const {
      51             :         return bool(this->value);
      52             :     }
      53             : 
      54           0 :     const T unbarrieredGet() const { return this->value; }
      55          65 :     T* unsafeGet() { return &this->value; }
      56             :     T const* unsafeGet() const { return &this->value; }
      57             : };
      58             : } // namespace detail
      59             : 
      60             : // The "nursery aware" hash map is a special case of GCHashMap that is able to
      61             : // treat nursery allocated members weakly during a minor GC: e.g. it allows for
      62             : // nursery allocated objects to be collected during nursery GC where a normal
      63             : // hash table treats such edges strongly.
      64             : //
      65             : // Doing this requires some strong constraints on what can be stored in this
      66             : // table and how it can be accessed. At the moment, this table assumes that
      67             : // all values contain a strong reference to the key. It also requires the
      68             : // policy to contain an |isTenured| and |needsSweep| members, which is fairly
      69             : // non-standard. This limits its usefulness to the CrossCompartmentMap at the
      70             : // moment, but might serve as a useful base for other tables in future.
      71             : template <typename Key,
      72             :           typename Value,
      73             :           typename HashPolicy = DefaultHasher<Key>,
      74             :           typename AllocPolicy = TempAllocPolicy>
      75        6117 : class NurseryAwareHashMap
      76             : {
      77             :     using BarrieredValue = detail::UnsafeBareReadBarriered<Value>;
      78             :     using MapType = GCRekeyableHashMap<Key, BarrieredValue, HashPolicy, AllocPolicy>;
      79             :     MapType map;
      80             : 
      81             :     // Keep a list of all keys for which JS::GCPolicy<Key>::isTenured is false.
      82             :     // This lets us avoid a full traveral of the map on each minor GC, keeping
      83             :     // the minor GC times proportional to the nursery heap size.
      84             :     Vector<Key, 0, AllocPolicy> nurseryEntries;
      85             : 
      86             :   public:
      87             :     using Lookup = typename MapType::Lookup;
      88             :     using Ptr = typename MapType::Ptr;
      89             :     using Range = typename MapType::Range;
      90             :     using Entry = typename MapType::Entry;
      91             : 
      92        1681 :     explicit NurseryAwareHashMap(AllocPolicy a = AllocPolicy()) : map(a) {}
      93             : 
      94        1681 :     MOZ_MUST_USE bool init(uint32_t len = 16) { return map.init(len); }
      95             : 
      96       13681 :     bool empty() const { return map.empty(); }
      97       40220 :     Ptr lookup(const Lookup& l) const { return map.lookup(l); }
      98          10 :     void remove(Ptr p) { map.remove(p); }
      99             :     Range all() const { return map.all(); }
     100        1265 :     struct Enum : public MapType::Enum {
     101        1265 :         explicit Enum(NurseryAwareHashMap& namap) : MapType::Enum(namap.map) {}
     102             :     };
     103           0 :     size_t sizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     104           0 :         return map.sizeOfExcludingThis(mallocSizeOf) +
     105           0 :                nurseryEntries.sizeOfExcludingThis(mallocSizeOf);
     106             :     }
     107             :     size_t sizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const {
     108             :         return map.sizeOfIncludingThis(mallocSizeOf) +
     109             :                nurseryEntries.sizeOfIncludingThis(mallocSizeOf);
     110             :     }
     111             : 
     112        9609 :     MOZ_MUST_USE bool put(const Key& k, const Value& v) {
     113        9609 :         auto p = map.lookupForAdd(k);
     114        9609 :         if (p) {
     115          12 :             if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
     116           0 :                 if (!nurseryEntries.append(k))
     117           0 :                     return false;
     118             :             }
     119          12 :             p->value() = v;
     120          12 :             return true;
     121             :         }
     122             : 
     123        9597 :         bool ok = map.add(p, k, v);
     124        9597 :         if (!ok)
     125           0 :             return false;
     126             : 
     127        9597 :         if (!JS::GCPolicy<Key>::isTenured(k) || !JS::GCPolicy<Value>::isTenured(v)) {
     128        3387 :             if (!nurseryEntries.append(k)) {
     129           0 :                 map.remove(k);
     130           0 :                 return false;
     131             :             }
     132             :         }
     133             : 
     134        9597 :         return true;
     135             :     }
     136             : 
     137       12428 :     void sweepAfterMinorGC(JSTracer* trc) {
     138       15516 :         for (auto& key : nurseryEntries) {
     139        3088 :             auto p = map.lookup(key);
     140        3088 :             if (!p)
     141         987 :                 continue;
     142             : 
     143             :             // Drop the entry if the value is not marked.
     144        3088 :             if (JS::GCPolicy<BarrieredValue>::needsSweep(&p->value())) {
     145         987 :                 map.remove(key);
     146         987 :                 continue;
     147             :             }
     148             : 
     149             :             // Update and relocate the key, if the value is still needed.
     150             :             //
     151             :             // Note that this currently assumes that all Value will contain a
     152             :             // strong reference to Key, as per its use as the
     153             :             // CrossCompartmentWrapperMap. We may need to make the following
     154             :             // behavior more dynamic if we use this map in other nursery-aware
     155             :             // contexts.
     156        4202 :             Key copy(key);
     157        4202 :             mozilla::DebugOnly<bool> sweepKey = JS::GCPolicy<Key>::needsSweep(&copy);
     158        2101 :             MOZ_ASSERT(!sweepKey);
     159        2101 :             map.rekeyIfMoved(key, copy);
     160             :         }
     161       12428 :         nurseryEntries.clear();
     162       12428 :     }
     163             : 
     164           0 :     void sweep() {
     165           0 :         MOZ_ASSERT(nurseryEntries.empty());
     166           0 :         map.sweep();
     167           0 :     }
     168             : 
     169           0 :     bool hasNurseryEntries() const {
     170           0 :         return !nurseryEntries.empty();
     171             :     }
     172             : };
     173             : 
     174             : } // namespace js
     175             : 
     176             : namespace JS {
     177             : template <typename T>
     178             : struct GCPolicy<js::detail::UnsafeBareReadBarriered<T>>
     179             : {
     180             :     static void trace(JSTracer* trc, js::detail::UnsafeBareReadBarriered<T>* thingp,
     181             :                       const char* name)
     182             :     {
     183             :         js::TraceEdge(trc, thingp, name);
     184             :     }
     185        3088 :     static bool needsSweep(js::detail::UnsafeBareReadBarriered<T>* thingp) {
     186        3088 :         return js::gc::IsAboutToBeFinalized(thingp);
     187             :     }
     188             : };
     189             : } // namespace JS
     190             : 
     191             : #endif // gc_NurseryAwareHashMap_h

Generated by: LCOV version 1.13