# Global Metrics

path: .metrics.halstead.difficulty
old: 24.615384615384617
new: 37.85496183206107
path: .metrics.halstead.n1
old: 20.0
new: 29.0
path: .metrics.halstead.level
old: 0.040624999999999994
new: 0.02641661625327687
path: .metrics.halstead.n2
old: 26.0
new: 131.0
path: .metrics.halstead.purity_ratio
old: 1.3122641167887776
new: 1.322864087247952
path: .metrics.halstead.effort
old: 21618.37171724468
new: 222568.56078095257
path: .metrics.halstead.N2
old: 64.0
new: 342.0
path: .metrics.halstead.vocabulary
old: 46.0
new: 160.0
path: .metrics.halstead.bugs
old: 0.25867873708615174
new: 1.2242063379750914
path: .metrics.halstead.N1
old: 95.0
new: 461.0
path: .metrics.halstead.estimated_program_length
old: 208.64999456941564
new: 1062.2598620601054
path: .metrics.halstead.time
old: 1201.0206509580378
new: 12364.920043386252
path: .metrics.halstead.volume
old: 878.2463510130651
new: 5879.508260194552
path: .metrics.halstead.length
old: 159.0
new: 803.0
path: .metrics.loc.blank
old: 2.0
new: 31.0
path: .metrics.loc.ploc
old: 38.0
new: 170.0
path: .metrics.loc.cloc
old: 8.0
new: 32.0
path: .metrics.loc.sloc
old: 48.0
new: 233.0
path: .metrics.loc.lloc
old: 20.0
new: 75.0
path: .metrics.cognitive.average
old: 19.0
new: 1.7692307692307692
path: .metrics.cognitive.sum
old: 19.0
new: 23.0
path: .metrics.mi.mi_original
old: 69.82132251453854
new: 31.351189329715567
path: .metrics.mi.mi_sei
old: 56.011857640448035
new: -0.5667073477528497
path: .metrics.mi.mi_visual_studio
old: 40.831182757040075
new: 18.33402884778688
path: .metrics.nargs.sum
old: 3.0
new: 11.0
path: .metrics.nargs.average
old: 3.0
new: 0.8461538461538461
path: .metrics.nexits.sum
old: 0.0
new: 13.0
path: .metrics.nexits.average
old: 0.0
new: 1.0
path: .metrics.nom.closures
old: 0.0
new: 2.0
path: .metrics.nom.total
old: 1.0
new: 13.0
path: .metrics.nom.functions
old: 1.0
new: 11.0
path: .metrics.cyclomatic.sum
old: 14.0
new: 27.0
path: .metrics.cyclomatic.average
old: 7.0
new: 1.9285714285714288

# Spaces Data

## Minimal test - lines (24, 233)

path: .spaces[0].metrics.cognitive.sum
old: 19.0
new: 23.0
path: .spaces[0].metrics.cognitive.average
old: 19.0
new: 1.7692307692307692
path: .spaces[0].metrics.cyclomatic.average
old: 13.0
new: 2.0
path: .spaces[0].metrics.cyclomatic.sum
old: 13.0
new: 26.0
path: .spaces[0].metrics.nexits.average
old: 0.0
new: 1.0
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 13.0
path: .spaces[0].metrics.nargs.average
old: 3.0
new: 0.8461538461538461
path: .spaces[0].metrics.nargs.sum
old: 3.0
new: 11.0
path: .spaces[0].metrics.halstead.difficulty
old: 26.52173913043478
new: 40.52564102564103
path: .spaces[0].metrics.halstead.estimated_program_length
old: 190.48048688705853
new: 944.7141210499578
path: .spaces[0].metrics.halstead.n1
old: 20.0
new: 29.0
path: .spaces[0].metrics.halstead.n2
old: 23.0
new: 117.0
path: .spaces[0].metrics.halstead.N1
old: 95.0
new: 461.0
path: .spaces[0].metrics.halstead.vocabulary
old: 43.0
new: 146.0
path: .spaces[0].metrics.halstead.bugs
old: 0.2652754928121346
new: 1.2498608974573604
path: .spaces[0].metrics.halstead.length
old: 156.0
new: 788.0
path: .spaces[0].metrics.halstead.level
old: 0.03770491803278689
new: 0.024675735526732044
path: .spaces[0].metrics.halstead.effort
old: 22450.58061119355
new: 229601.33229908143
path: .spaces[0].metrics.halstead.N2
old: 61.0
new: 327.0
path: .spaces[0].metrics.halstead.purity_ratio
old: 1.221028762096529
new: 1.1988757881344645
path: .spaces[0].metrics.halstead.volume
old: 846.4973017335273
new: 5665.581752397454
path: .spaces[0].metrics.halstead.time
old: 1247.2544783996416
new: 12755.62957217119
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 30.0
path: .spaces[0].metrics.loc.cloc
old: 2.0
new: 26.0
path: .spaces[0].metrics.loc.sloc
old: 37.0
new: 210.0
path: .spaces[0].metrics.loc.lloc
old: 20.0
new: 75.0
path: .spaces[0].metrics.loc.ploc
old: 35.0
new: 154.0
path: .spaces[0].metrics.mi.mi_original
old: 74.45937334230254
new: 33.457600727355626
path: .spaces[0].metrics.mi.mi_visual_studio
old: 43.543493182633064
new: 19.56584837857054
path: .spaces[0].metrics.mi.mi_sei
old: 50.66711562080896
new: 1.1411059840569386
path: .spaces[0].metrics.nom.total
old: 1.0
new: 13.0
path: .spaces[0].metrics.nom.functions
old: 1.0
new: 11.0
path: .spaces[0].metrics.nom.closures
old: 0.0
new: 2.0

### Code

    namespace mozilla {

    // Created and destroyed on the main thread.
    static StaticAutoPtr sMonitor;

    // Hashtable, maps thread pool name to SharedThreadPool instance.
    // Modified only on the main thread.
    static StaticAutoPtr> sPools;

    static already_AddRefed CreateThreadPool(const nsCString& aName);

    class SharedThreadPoolShutdownObserver : public nsIObserver {
     public:
      NS_DECL_ISUPPORTS
      NS_DECL_NSIOBSERVER
     protected:
      virtual ~SharedThreadPoolShutdownObserver() = default;
    };

    NS_IMPL_ISUPPORTS(SharedThreadPoolShutdownObserver, nsIObserver, nsISupports)

    NS_IMETHODIMP
    SharedThreadPoolShutdownObserver::Observe(nsISupports* aSubject,
                                              const char* aTopic,
                                              const char16_t* aData) {
      MOZ_RELEASE_ASSERT(!strcmp(aTopic, "xpcom-shutdown-threads"));
    #ifdef EARLY_BETA_OR_EARLIER
      {
        ReentrantMonitorAutoEnter mon(*sMonitor);
        if (!sPools->Iter().Done()) {
          nsAutoCString str;
          for (auto i = sPools->Iter(); !i.Done(); i.Next()) {
            str.AppendPrintf("\"%s\" ", nsAutoCString(i.Key()).get());
          }
          printf_stderr(
              "SharedThreadPool in xpcom-shutdown-threads. Waiting for "
              "pools %s\n",
              str.get());
        }
      }
    #endif
      SharedThreadPool::SpinUntilEmpty();
      sMonitor = nullptr;
      sPools = nullptr;
      return NS_OK;
    }

    void SharedThreadPool::InitStatics() {
      MOZ_ASSERT(NS_IsMainThread());
      MOZ_ASSERT(!sMonitor && !sPools);
      sMonitor = new ReentrantMonitor("SharedThreadPool");
      sPools = new nsTHashMap();
      nsCOMPtr obsService =
          mozilla::services::GetObserverService();
      nsCOMPtr obs = new SharedThreadPoolShutdownObserver();
      obsService->AddObserver(obs, "xpcom-shutdown-threads", false);
    }

    /* static */
    bool SharedThreadPool::IsEmpty() {
      ReentrantMonitorAutoEnter mon(*sMonitor);
      return !sPools->Count();
    }

    /* static */
    void SharedThreadPool::SpinUntilEmpty() {
      MOZ_ASSERT(NS_IsMainThread());
      SpinEventLoopUntil([]() -> bool {
        sMonitor->AssertNotCurrentThreadIn();
        return IsEmpty();
      });
    }

    already_AddRefed SharedThreadPool::Get(
        const nsCString& aName, uint32_t aThreadLimit) {
      MOZ_ASSERT(sMonitor && sPools);
      ReentrantMonitorAutoEnter mon(*sMonitor);
      RefPtr pool;

      return sPools->WithEntryHandle(
          aName, [&](auto&& entry) -> already_AddRefed {
            if (entry) {
              pool = entry.Data();
              if (NS_FAILED(pool->EnsureThreadLimitIsAtLeast(aThreadLimit))) {
                NS_WARNING("Failed to set limits on thread pool");
              }
            } else {
              nsCOMPtr threadPool(CreateThreadPool(aName));
              if (NS_WARN_IF(!threadPool)) {
                sPools->Remove(aName);  // XXX entry.Remove()
                return nullptr;
              }
              pool = new SharedThreadPool(aName, threadPool);

              // Set the thread and idle limits. Note that we don't rely on the
              // EnsureThreadLimitIsAtLeast() call below, as the default thread
              // limit is 4, and if aThreadLimit is less than 4 we'll end up with a
              // pool with 4 threads rather than what we expected; so we'll have
              // unexpected behaviour.
              nsresult rv = pool->SetThreadLimit(aThreadLimit);
              if (NS_WARN_IF(NS_FAILED(rv))) {
                sPools->Remove(aName);  // XXX entry.Remove()
                return nullptr;
              }

              rv = pool->SetIdleThreadLimit(aThreadLimit);
              if (NS_WARN_IF(NS_FAILED(rv))) {
                sPools->Remove(aName);  // XXX entry.Remove()
                return nullptr;
              }

              entry.Insert(pool.get());
            }

            return pool.forget();
          });
    }

    NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::AddRef(void) {
      MOZ_ASSERT(sMonitor);
      ReentrantMonitorAutoEnter mon(*sMonitor);
      MOZ_ASSERT(int32_t(mRefCnt) >= 0, "illegal refcnt");
      nsrefcnt count = ++mRefCnt;
      NS_LOG_ADDREF(this, count, "SharedThreadPool", sizeof(*this));
      return count;
    }

    NS_IMETHODIMP_(MozExternalRefCountType) SharedThreadPool::Release(void) {
      MOZ_ASSERT(sMonitor);
      ReentrantMonitorAutoEnter mon(*sMonitor);
      nsrefcnt count = --mRefCnt;
      NS_LOG_RELEASE(this, count, "SharedThreadPool");
      if (count) {
        return count;
      }

      // Remove SharedThreadPool from table of pools.
      sPools->Remove(mName);
      MOZ_ASSERT(!sPools->Get(mName));

      // Dispatch an event to the main thread to call Shutdown() on
      // the nsIThreadPool. The Runnable here will add a refcount to the pool,
      // and when the Runnable releases the nsIThreadPool it will be deleted.
      NS_DispatchToMainThread(NewRunnableMethod("nsIThreadPool::Shutdown", mPool,
                                                &nsIThreadPool::Shutdown));

      // Stabilize refcount, so that if something in the dtor QIs, it won't explode.
      mRefCnt = 1;
      delete this;
      return 0;
    }

    NS_IMPL_QUERY_INTERFACE(SharedThreadPool, nsIThreadPool, nsIEventTarget)

    SharedThreadPool::SharedThreadPool(const nsCString& aName, nsIThreadPool* aPool)
        : mName(aName), mPool(aPool), mRefCnt(0) {
      mEventTarget = aPool;
    }

    SharedThreadPool::~SharedThreadPool() = default;

    nsresult SharedThreadPool::EnsureThreadLimitIsAtLeast(uint32_t aLimit) {
      // We limit the number of threads that we use. Note that we
      // set the thread limit to the same as the idle limit so that we're not
      // constantly creating and destroying threads (see Bug 881954). When the
      // thread pool threads shutdown they dispatch an event to the main thread
      // to call nsIThread::Shutdown(), and if we're very busy that can take a
      // while to run, and we end up with dozens of extra threads. Note that
      // threads that are idle for 60 seconds are shutdown naturally.
      uint32_t existingLimit = 0;
      nsresult rv;

      rv = mPool->GetThreadLimit(&existingLimit);
      NS_ENSURE_SUCCESS(rv, rv);
      if (aLimit > existingLimit) {
        rv = mPool->SetThreadLimit(aLimit);
        NS_ENSURE_SUCCESS(rv, rv);
      }

      rv = mPool->GetIdleThreadLimit(&existingLimit);
      NS_ENSURE_SUCCESS(rv, rv);
      if (aLimit > existingLimit) {
        rv = mPool->SetIdleThreadLimit(aLimit);
        NS_ENSURE_SUCCESS(rv, rv);
      }

      return NS_OK;
    }

    static already_AddRefed CreateThreadPool(
        const nsCString& aName) {
      nsCOMPtr pool = new nsThreadPool();

      nsresult rv = pool->SetName(aName);
      NS_ENSURE_SUCCESS(rv, nullptr);

      rv = pool->SetThreadStackSize(nsIThreadManager::kThreadPoolStackSize);
      NS_ENSURE_SUCCESS(rv, nullptr);

    #ifdef XP_WIN
      // Ensure MSCOM is initialized on the thread pools threads.
      nsCOMPtr listener = new MSCOMInitThreadPoolListener();
      rv = pool->SetListener(listener);
      NS_ENSURE_SUCCESS(rv, nullptr);
    #endif

      return pool.forget();
    }

    }  // namespace mozilla