# Global Metrics

path: .metrics.loc.ploc
old: 12.0
new: 164.0
path: .metrics.loc.cloc
old: 13.0
new: 159.0
path: .metrics.loc.lloc
old: 0.0
new: 58.0
path: .metrics.loc.sloc
old: 32.0
new: 359.0
path: .metrics.loc.blank
old: 7.0
new: 36.0
path: .metrics.nargs.sum
old: 0.0
new: 13.0
path: .metrics.nargs.average
old: null
new: 0.8125
path: .metrics.mi.mi_sei
old: 91.89145164890056
new: 2.970830832762843
path: .metrics.mi.mi_original
old: 87.09966912614469
new: 22.11968739679088
path: .metrics.mi.mi_visual_studio
old: 50.93547902113725
new: 12.93548970572566
path: .metrics.cognitive.sum
old: 0.0
new: 19.0
path: .metrics.cognitive.average
old: null
new: 1.1875
path: .metrics.nom.total
old: 0.0
new: 16.0
path: .metrics.nom.functions
old: 0.0
new: 16.0
path: .metrics.cyclomatic.average
old: 1.0
new: 1.7272727272727273
path: .metrics.cyclomatic.sum
old: 2.0
new: 38.0
path: .metrics.halstead.bugs
old: 0.036426825454917065
new: 1.4664238102302634
path: .metrics.halstead.difficulty
old: 6.0
new: 52.59090909090909
path: .metrics.halstead.volume
old: 190.3981037807637
new: 5548.30669150177
path: .metrics.halstead.n1
old: 10.0
new: 26.0
path: .metrics.halstead.effort
old: 1142.3886226845823
new: 291790.4928212522
path: .metrics.halstead.vocabulary
old: 25.0
new: 114.0
path: .metrics.halstead.N2
old: 18.0
new: 356.0
path: .metrics.halstead.length
old: 41.0
new: 812.0
path: .metrics.halstead.time
old: 63.466034593587906
new: 16210.582934514012
path: .metrics.halstead.purity_ratio
old: 2.2395765825122296
new: 0.8505436146696436
path: .metrics.halstead.N1
old: 23.0
new: 456.0
path: .metrics.halstead.estimated_program_length
old: 91.8226398830014
new: 690.6414151117506
path: .metrics.halstead.level
old: 0.16666666666666666
new: 0.01901469317199654
path: .metrics.halstead.n2
old: 15.0
new: 88.0
path: .metrics.nexits.average
old: null
new: 0.875
path: .metrics.nexits.sum
old: 0.0
new: 14.0

# Spaces Data

## Minimal test - lines (18, 357)

path: .spaces[0].metrics.nargs.average
old: null
new: 0.8125
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 13.0
path: .spaces[0].metrics.mi.mi_original
old: 102.45401379878842
new: 23.31863467893676
path: .spaces[0].metrics.mi.mi_visual_studio
old: 59.91462795250785
new: 13.63662846721448
path: .spaces[0].metrics.mi.mi_sei
old: 100.75758896741144
new: 4.722419111482999
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 19.0
path: .spaces[0].metrics.cognitive.average
old: null
new: 1.1875
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 16.0
path: .spaces[0].metrics.nom.total
old: 0.0
new: 16.0
path: .spaces[0].metrics.halstead.vocabulary
old: 23.0
new: 109.0
path: .spaces[0].metrics.halstead.length
old: 38.0
new: 806.0
path: .spaces[0].metrics.halstead.n2
old: 13.0
new: 83.0
path: .spaces[0].metrics.halstead.N1
old: 23.0
new: 456.0
path: .spaces[0].metrics.halstead.level
old: 0.17333333333333334
new: 0.01824175824175824
path: .spaces[0].metrics.halstead.effort
old: 991.703967289422
new: 299047.7394488485
path: .spaces[0].metrics.halstead.difficulty
old: 5.769230769230769
new: 54.81927710843374
path: .spaces[0].metrics.halstead.purity_ratio
old: 2.140131507492311
new: 0.808113778503056
path: .spaces[0].metrics.halstead.bugs
old: 0.03314872120214663
new: 1.490638822350695
path: .spaces[0].metrics.halstead.estimated_program_length
old: 81.32499728470782
new: 651.3397054734631
path: .spaces[0].metrics.halstead.n1
old: 10.0
new: 26.0
path: .spaces[0].metrics.halstead.volume
old: 171.8953543301665
new: 5455.156565770202
path: .spaces[0].metrics.halstead.time
old: 55.09466484941234
new: 16613.763302713804
path: .spaces[0].metrics.halstead.N2
old: 15.0
new: 350.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 1.761904761904762
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 37.0
path: .spaces[0].metrics.loc.blank
old: 5.0
new: 32.0
path: .spaces[0].metrics.loc.sloc
old: 13.0
new: 340.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 58.0
path: .spaces[0].metrics.loc.cloc
old: 2.0
new: 152.0
path: .spaces[0].metrics.loc.ploc
old: 6.0
new: 156.0
path: .spaces[0].metrics.nexits.average
old: null
new: 0.875
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 14.0

### Code

    namespace mozilla {

    /**
     * DeadlockDetector
     *
     * The following is an approximate description of how the deadlock detector
     * works.
     *
     * The deadlock detector ensures that all blocking resources are
     * acquired according to a partial order P.  One type of blocking
     * resource is a lock.  If a lock l1 is acquired (locked) before l2,
     * then we say that |l1 <_P l2|.  The detector flags an error if two
     * locks l1 and l2 have an inconsistent ordering in P; that is, if
     * both |l1 <_P l2| and |l2 <_P l1|.  This is a potential error
     * because a thread acquiring l1,l2 according to the first order might
     * race with a thread acquiring them according to the second order.
     * If this happens under the right conditions, then the acquisitions
     * will deadlock.
     *
     * This deadlock detector doesn't know at compile-time what P is.  So,
     * it tries to discover the order at run time.  More precisely, it
     * finds some order P, then tries to find chains of resource
     * acquisitions that violate P.  An example acquisition sequence, and
     * the orders they impose, is
     *   l1.lock()   // current chain: [ l1 ]
     *               // order: { }
     *
     *   l2.lock()   // current chain: [ l1, l2 ]
     *               // order: { l1 <_P l2 }
     *
     *   l3.lock()   // current chain: [ l1, l2, l3 ]
     *               // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
     *               // (note: <_P is transitive, so also |l1 <_P l3|)
     *
     *   l2.unlock() // current chain: [ l1, l3 ]
     *               // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3 }
     *               // (note: it's OK, but weird, that l2 was unlocked out
     *               //  of order.  we still have l1 <_P l3).
     *
     *   l2.lock()   // current chain: [ l1, l3, l2 ]
     *               // order: { l1 <_P l2, l2 <_P l3, l1 <_P l3,
     *                                      l3 <_P l2 (!!!) }
     * BEEP BEEP!  Here the detector will flag a potential error, since
     * l2 and l3 were used inconsistently (and potentially in ways that
     * would deadlock).
     */
    template
    class DeadlockDetector {
     public:
      typedef nsTArray ResourceAcquisitionArray;

     private:
      struct OrderingEntry;
      typedef nsTArray HashEntryArray;
      typedef typename HashEntryArray::index_type index_type;
      typedef typename HashEntryArray::size_type size_type;
      static const index_type NoIndex = HashEntryArray::NoIndex;

      /**
       * Value type for the ordering table.  Contains the other
       * resources on which an ordering constraint |key < other|
       * exists.  The catch is that we also store the calling context at
       * which the other resource was acquired; this improves the
       * quality of error messages when potential deadlock is detected.
       */
      struct OrderingEntry {
        explicit OrderingEntry(const T* aResource)
            : mOrderedLT()  // FIXME bug 456272: set to empirical dep size?
              ,
              mExternalRefs(),
              mResource(aResource) {}
        ~OrderingEntry() {}

        size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
          size_t n = aMallocSizeOf(this);
          n += mOrderedLT.ShallowSizeOfExcludingThis(aMallocSizeOf);
          n += mExternalRefs.ShallowSizeOfExcludingThis(aMallocSizeOf);
          return n;
        }

        HashEntryArray mOrderedLT;     // this <_o Other
        HashEntryArray mExternalRefs;  // hash entries that reference this
        const T* mResource;
      };

      // Throwaway RAII lock to make the following code safer.
      struct PRAutoLock {
        explicit PRAutoLock(PRLock* aLock) : mLock(aLock) { PR_Lock(mLock); }
        ~PRAutoLock() { PR_Unlock(mLock); }
        PRLock* mLock;
      };

     public:
      static const uint32_t kDefaultNumBuckets;

      /**
       * DeadlockDetector
       * Create a new deadlock detector.
       *
       * @param aNumResourcesGuess Guess at approximate number of resources
       *        that will be checked.
       */
      explicit DeadlockDetector(uint32_t aNumResourcesGuess = kDefaultNumBuckets)
          : mOrdering(aNumResourcesGuess) {
        mLock = PR_NewLock();
        if (!mLock) {
          MOZ_CRASH("couldn't allocate deadlock detector lock");
        }
      }

      /**
       * ~DeadlockDetector
       *
       * *NOT* thread safe.
       */
      ~DeadlockDetector() { PR_DestroyLock(mLock); }

      size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const {
        size_t n = aMallocSizeOf(this);

        {
          PRAutoLock _(mLock);
          n += mOrdering.ShallowSizeOfExcludingThis(aMallocSizeOf);
          for (auto iter = mOrdering.ConstIter(); !iter.Done(); iter.Next()) {
            // NB: Key is accounted for in the entry.
            n += iter.Data()->SizeOfIncludingThis(aMallocSizeOf);
          }
        }

        return n;
      }

      /**
       * Add
       * Make the deadlock detector aware of |aResource|.
       *
       * WARNING: The deadlock detector owns |aResource|.
       *
       * Thread safe.
       *
       * @param aResource Resource to make deadlock detector aware of.
       */
      void Add(const T* aResource) {
        PRAutoLock _(mLock);
        mOrdering.InsertOrUpdate(aResource, MakeUnique(aResource));
      }

      void Remove(const T* aResource) {
        PRAutoLock _(mLock);

        OrderingEntry* entry = mOrdering.Get(aResource);

        // Iterate the external refs and remove the entry from them.
        HashEntryArray& refs = entry->mExternalRefs;
        for (index_type i = 0; i < refs.Length(); i++) {
          refs[i]->mOrderedLT.RemoveElementSorted(entry);
        }

        // Iterate orders and remove this entry from their refs.
        HashEntryArray& orders = entry->mOrderedLT;
        for (index_type i = 0; i < orders.Length(); i++) {
          orders[i]->mExternalRefs.RemoveElementSorted(entry);
        }

        // Now the entry can be safely removed.
        mOrdering.Remove(aResource);
      }

      /**
       * CheckAcquisition This method is called after acquiring |aLast|,
       * but before trying to acquire |aProposed|.
       * It determines whether actually trying to acquire |aProposed|
       * will create problems.  It is OK if |aLast| is nullptr; this is
       * interpreted as |aProposed| being the thread's first acquisition
       * of its current chain.
       *
       * Iff acquiring |aProposed| may lead to deadlock for some thread
       * interleaving (including the current one!), the cyclical
       * dependency from which this was deduced is returned.  Otherwise,
       * 0 is returned.
       *
       * If a potential deadlock is detected and a resource cycle is
       * returned, it is the *caller's* responsibility to free it.
       *
       * Thread safe.
       *
       * @param aLast Last resource acquired by calling thread (or 0).
       * @param aProposed Resource calling thread proposes to acquire.
       */
      ResourceAcquisitionArray* CheckAcquisition(const T* aLast,
                                                 const T* aProposed) {
        if (!aLast) {
          // don't check if |0 < aProposed|; just vamoose
          return 0;
        }

        NS_ASSERTION(aProposed, "null resource");
        PRAutoLock _(mLock);

        OrderingEntry* proposed = mOrdering.Get(aProposed);
        NS_ASSERTION(proposed, "missing ordering entry");

        OrderingEntry* current = mOrdering.Get(aLast);
        NS_ASSERTION(current, "missing ordering entry");

        // this is the crux of the deadlock detector algorithm

        if (current == proposed) {
          // reflexive deadlock.  fastpath b/c InTransitiveClosure is
          // not applicable here.
          ResourceAcquisitionArray* cycle = new ResourceAcquisitionArray();
          if (!cycle) {
            MOZ_CRASH("can't allocate dep. cycle array");
          }
          cycle->AppendElement(current->mResource);
          cycle->AppendElement(aProposed);
          return cycle;
        }
        if (InTransitiveClosure(current, proposed)) {
          // we've already established |aLast < aProposed|.  all is well.
          return 0;
        }
        if (InTransitiveClosure(proposed, current)) {
          // the order |aProposed < aLast| has been deduced, perhaps
          // transitively.  we're attempting to violate that
          // constraint by acquiring resources in the order
          // |aLast < aProposed|, and thus we may deadlock under the
          // right conditions.
          ResourceAcquisitionArray* cycle = GetDeductionChain(proposed, current);
          // show how acquiring |aProposed| would complete the cycle
          cycle->AppendElement(aProposed);
          return cycle;
        }
        // |aLast|, |aProposed| are unordered according to our
        // poset.  this is fine, but we now need to add this
        // ordering constraint.
        current->mOrderedLT.InsertElementSorted(proposed);
        proposed->mExternalRefs.InsertElementSorted(current);
        return 0;
      }

      /**
       * Return true iff |aTarget| is in the transitive closure of |aStart|
       * over the ordering relation `<_this'.
       *
       * @precondition |aStart != aTarget|
       */
      bool InTransitiveClosure(const OrderingEntry* aStart,
                               const OrderingEntry* aTarget) const {
        // NB: Using a static comparator rather than default constructing one shows
        //     a 9% improvement in scalability tests on some systems.
        static nsDefaultComparator comp;
        if (aStart->mOrderedLT.BinaryIndexOf(aTarget, comp) != NoIndex) {
          return true;
        }

        index_type i = 0;
        size_type len = aStart->mOrderedLT.Length();
        for (auto it = aStart->mOrderedLT.Elements(); i < len; ++i, ++it) {
          if (InTransitiveClosure(*it, aTarget)) {
            return true;
          }
        }
        return false;
      }

      /**
       * Return an array of all resource acquisitions
       *   aStart <_this r1 <_this r2 <_ ... <_ aTarget
       * from which |aStart <_this aTarget| was deduced, including
       * |aStart| and |aTarget|.
       *
       * Nb: there may be multiple deductions of |aStart <_this
       * aTarget|.  This function returns the first ordering found by
       * depth-first search.
       *
       * Nb: |InTransitiveClosure| could be replaced by this function.
       * However, this one is more expensive because we record the DFS
       * search stack on the heap whereas the other doesn't.
       *
       * @precondition |aStart != aTarget|
       */
      ResourceAcquisitionArray* GetDeductionChain(const OrderingEntry* aStart,
                                                  const OrderingEntry* aTarget) {
        ResourceAcquisitionArray* chain = new ResourceAcquisitionArray();
        if (!chain) {
          MOZ_CRASH("can't allocate dep. cycle array");
        }
        chain->AppendElement(aStart->mResource);

        NS_ASSERTION(GetDeductionChain_Helper(aStart, aTarget, chain),
                     "GetDeductionChain called when there's no deadlock");
        return chain;
      }

      // precondition: |aStart != aTarget|
      // invariant: |aStart| is the last element in |aChain|
      bool GetDeductionChain_Helper(const OrderingEntry* aStart,
                                    const OrderingEntry* aTarget,
                                    ResourceAcquisitionArray* aChain) {
        if (aStart->mOrderedLT.BinaryIndexOf(aTarget) != NoIndex) {
          aChain->AppendElement(aTarget->mResource);
          return true;
        }

        index_type i = 0;
        size_type len = aStart->mOrderedLT.Length();
        for (auto it = aStart->mOrderedLT.Elements(); i < len; ++i, ++it) {
          aChain->AppendElement((*it)->mResource);
          if (GetDeductionChain_Helper(*it, aTarget, aChain)) {
            return true;
          }
          aChain->RemoveLastElement();
        }
        return false;
      }

      /**
       * The partial order on resource acquisitions used by the deadlock
       * detector.
       */
      nsClassHashtable, OrderingEntry> mOrdering;

      /**
       * Protects contentious methods.
       * Nb: can't use mozilla::Mutex since we are used as its deadlock
       * detector.
       */
      PRLock* mLock;

     private:
      DeadlockDetector(const DeadlockDetector& aDD) = delete;
      DeadlockDetector& operator=(const DeadlockDetector& aDD) = delete;
    };

    template
    // FIXME bug 456272: tune based on average workload
    const uint32_t DeadlockDetector::kDefaultNumBuckets = 32;

    }  // namespace mozilla