# Global Metrics

path: .metrics.loc.cloc
old: 122.0
new: 45.0
path: .metrics.loc.lloc
old: 314.0
new: 82.0
path: .metrics.loc.ploc
old: 642.0
new: 177.0
path: .metrics.loc.blank
old: 98.0
new: 32.0
path: .metrics.loc.sloc
old: 862.0
new: 254.0
path: .metrics.mi.mi_visual_studio
old: 0.0
new: 15.25121037632902
path: .metrics.mi.mi_sei
old: -69.96485618437933
new: -2.439655587715187
path: .metrics.mi.mi_original
old: -25.402866933343404
new: 26.079569743522622
path: .metrics.nom.total
old: 8.0
new: 14.0
path: .metrics.nom.functions
old: 8.0
new: 14.0
path: .metrics.cognitive.average
old: 32.75
new: 2.9285714285714284
path: .metrics.cognitive.sum
old: 262.0
new: 41.0
path: .metrics.nargs.average
old: 2.5
new: 0.9285714285714286
path: .metrics.nargs.sum
old: 20.0
new: 13.0
path: .metrics.nexits.sum
old: 20.0
new: 14.0
path: .metrics.nexits.average
old: 2.5
new: 1.0
path: .metrics.cyclomatic.average
old: 14.6
new: 3.25
path: .metrics.cyclomatic.sum
old: 146.0
new: 52.0
path: .metrics.halstead.level
old: 0.011859961227049834
new: 0.023240589198036007
path: .metrics.halstead.n1
old: 37.0
new: 26.0
path: .metrics.halstead.length
old: 3363.0
new: 621.0
path: .metrics.halstead.effort
old: 2395247.3659822783
new: 176352.92462140167
path: .metrics.halstead.volume
old: 28407.540889743064
new: 4098.545874998206
path: .metrics.halstead.bugs
old: 5.967315398270811
new: 1.048255596017438
path: .metrics.halstead.N2
old: 1422.0
new: 235.0
path: .metrics.halstead.difficulty
old: 84.3173076923077
new: 43.028169014084504
path: .metrics.halstead.N1
old: 1941.0
new: 386.0
path: .metrics.halstead.purity_ratio
old: 0.8259872931350857
new: 0.8999089825386487
path: .metrics.halstead.estimated_program_length
old: 2777.795266813293
new: 558.8434781565009
path: .metrics.halstead.time
old: 133069.29811012658
new: 9797.38470118898
path: .metrics.halstead.n2
old: 312.0
new: 71.0
path: .metrics.halstead.vocabulary
old: 349.0
new: 97.0

# Spaces Data

## Minimal test - lines (18, 254)

path: .spaces[0].metrics.mi.mi_visual_studio
old: 69.87155172934096
new: 16.16409040415271
path: .spaces[0].metrics.mi.mi_original
old: 119.48035345717308
new: 27.640594591101134
path: .spaces[0].metrics.mi.mi_sei
old: 96.77468128371584
new: -1.236918499309212
path: .spaces[0].metrics.nargs.sum
old: 0.0
new: 13.0
path: .spaces[0].metrics.nargs.average
old: null
new: 0.9285714285714286
path: .spaces[0].metrics.nexits.average
old: null
new: 1.0
path: .spaces[0].metrics.nexits.sum
old: 0.0
new: 14.0
path: .spaces[0].metrics.nom.functions
old: 0.0
new: 14.0
path: .spaces[0].metrics.nom.total
old: 0.0
new: 14.0
path: .spaces[0].metrics.cyclomatic.average
old: 1.0
new: 3.4
path: .spaces[0].metrics.cyclomatic.sum
old: 1.0
new: 51.0
path: .spaces[0].metrics.loc.cloc
old: 0.0
new: 39.0
path: .spaces[0].metrics.loc.ploc
old: 6.0
new: 168.0
path: .spaces[0].metrics.loc.sloc
old: 6.0
new: 237.0
path: .spaces[0].metrics.loc.blank
old: 0.0
new: 30.0
path: .spaces[0].metrics.loc.lloc
old: 0.0
new: 82.0
path: .spaces[0].metrics.cognitive.sum
old: 0.0
new: 41.0
path: .spaces[0].metrics.cognitive.average
old: null
new: 2.9285714285714284
path: .spaces[0].metrics.halstead.bugs
old: 0.009186499424109184
new: 1.0736531125025055
path: .spaces[0].metrics.halstead.vocabulary
old: 14.0
new: 89.0
path: .spaces[0].metrics.halstead.N1
old: 9.0
new: 383.0
path: .spaces[0].metrics.halstead.volume
old: 72.33974351909448
new: 3937.2459260275696
path: .spaces[0].metrics.halstead.time
old: 8.037749279899387
new: 10155.594650467938
path: .spaces[0].metrics.halstead.estimated_program_length
old: 41.219280948873624
new: 498.78006785216314
path: .spaces[0].metrics.halstead.level
old: 0.5
new: 0.021538461538461538
path: .spaces[0].metrics.halstead.effort
old: 144.67948703818897
new: 182800.7037084229
path: .spaces[0].metrics.halstead.length
old: 19.0
new: 608.0
path: .spaces[0].metrics.halstead.difficulty
old: 2.0
new: 46.42857142857143
path: .spaces[0].metrics.halstead.n2
old: 10.0
new: 63.0
path: .spaces[0].metrics.halstead.purity_ratio
old: 2.169435839414401
new: 0.8203619537042157
path: .spaces[0].metrics.halstead.N2
old: 10.0
new: 225.0
path: .spaces[0].metrics.halstead.n1
old: 4.0
new: 26.0

### Code

    namespace mozilla {

    IdlePeriodState::IdlePeriodState(already_AddRefed&& aIdlePeriod)
        : mIdlePeriod(aIdlePeriod) {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");
    }

    IdlePeriodState::~IdlePeriodState() {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");
      if (mIdleScheduler) {
        mIdleScheduler->Disconnect();
      }
    }

    size_t IdlePeriodState::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const {
      size_t n = 0;
      if (mIdlePeriod) {
        n += aMallocSizeOf(mIdlePeriod);
      }

      return n;
    }

    void IdlePeriodState::FlagNotIdle() {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");

      EnsureIsActive();
      if (mIdleToken && mIdleToken < TimeStamp::Now()) {
        ClearIdleToken();
      }
    }

    void IdlePeriodState::RanOutOfTasks(const MutexAutoUnlock& aProofOfUnlock) {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");
      MOZ_ASSERT(!mHasPendingEventsPromisedIdleEvent);
      EnsureIsPaused(aProofOfUnlock);
      ClearIdleToken();
    }

    TimeStamp IdlePeriodState::GetIdleDeadlineInternal(
        bool aIsPeek, const MutexAutoUnlock& aProofOfUnlock) {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");

      bool shuttingDown;
      TimeStamp localIdleDeadline =
          GetLocalIdleDeadline(shuttingDown, aProofOfUnlock);
      if (!localIdleDeadline) {
        if (!aIsPeek) {
          EnsureIsPaused(aProofOfUnlock);
          ClearIdleToken();
        }
        return TimeStamp();
      }

      TimeStamp idleDeadline =
          mHasPendingEventsPromisedIdleEvent || shuttingDown
              ? localIdleDeadline
              : GetIdleToken(localIdleDeadline, aProofOfUnlock);
      if (!idleDeadline) {
        if (!aIsPeek) {
          EnsureIsPaused(aProofOfUnlock);

          // Don't call ClearIdleToken() here, since we may have a pending
          // request already.
          //
          // RequestIdleToken can do all sorts of IPC stuff that might
          // take mutexes.  This is one reason why we need the
          // MutexAutoUnlock reference!
          RequestIdleToken(localIdleDeadline);
        }
        return TimeStamp();
      }

      if (!aIsPeek) {
        EnsureIsActive();
      }
      return idleDeadline;
    }

    TimeStamp IdlePeriodState::GetLocalIdleDeadline(
        bool& aShuttingDown, const MutexAutoUnlock& aProofOfUnlock) {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");
      // If we are shutting down, we won't honor the idle period, and we will
      // always process idle runnables.  This will ensure that the idle queue
      // gets exhausted at shutdown time to prevent intermittently leaking
      // some runnables inside that queue and even worse potentially leaving
      // some important cleanup work unfinished.
      if (gXPCOMThreadsShutDown ||
          nsThreadManager::get().GetCurrentThread()->ShuttingDown()) {
        aShuttingDown = true;
        return TimeStamp::Now();
      }

      aShuttingDown = false;
      TimeStamp idleDeadline;
      // This GetIdlePeriodHint() call is the reason we need a MutexAutoUnlock here.
      mIdlePeriod->GetIdlePeriodHint(&idleDeadline);

      // If HasPendingEvents() has been called and it has returned true because of
      // pending idle events, there is a risk that we may decide here that we aren't
      // idle and return null, in which case HasPendingEvents() has effectively
      // lied.  Since we can't go back and fix the past, we have to adjust what we
      // do here and forcefully pick the idle queue task here.  Note that this means
      // that we are choosing to run a task from the idle queue when we would
      // normally decide that we aren't in an idle period, but this can only happen
      // if we fall out of the idle period in between the call to HasPendingEvents()
      // and here, which should hopefully be quite rare.  We are effectively
      // choosing to prioritize the sanity of our API semantics over the optimal
      // scheduling.
      if (!mHasPendingEventsPromisedIdleEvent &&
          (!idleDeadline || idleDeadline < TimeStamp::Now())) {
        return TimeStamp();
      }
      if (mHasPendingEventsPromisedIdleEvent && !idleDeadline) {
        // If HasPendingEvents() has been called and it has returned true, but we're
        // no longer in the idle period, we must return a valid timestamp to pretend
        // that we are still in the idle period.
        return TimeStamp::Now();
      }
      return idleDeadline;
    }

    TimeStamp IdlePeriodState::GetIdleToken(TimeStamp aLocalIdlePeriodHint,
                                            const MutexAutoUnlock& aProofOfUnlock) {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");

      if (!ShouldGetIdleToken()) {
        return aLocalIdlePeriodHint;
      }

      if (mIdleToken) {
        TimeStamp now = TimeStamp::Now();
        if (mIdleToken < now) {
          ClearIdleToken();
          return mIdleToken;
        }
        return mIdleToken < aLocalIdlePeriodHint ? mIdleToken
                                                 : aLocalIdlePeriodHint;
      }
      return TimeStamp();
    }

    void IdlePeriodState::RequestIdleToken(TimeStamp aLocalIdlePeriodHint) {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");
      MOZ_ASSERT(!mActive);

      if (!mIdleSchedulerInitialized) {
        mIdleSchedulerInitialized = true;
        if (ShouldGetIdleToken()) {
          // For now cross-process idle scheduler is supported only on the main
          // threads of the child processes.
          mIdleScheduler = ipc::IdleSchedulerChild::GetMainThreadIdleScheduler();
          if (mIdleScheduler) {
            mIdleScheduler->Init(this);
          }
        }
      }

      if (mIdleScheduler && !mIdleRequestId) {
        TimeStamp now = TimeStamp::Now();
        if (aLocalIdlePeriodHint <= now) {
          return;
        }

        mIdleRequestId = ++sIdleRequestCounter;
        mIdleScheduler->SendRequestIdleTime(mIdleRequestId,
                                            aLocalIdlePeriodHint - now);
      }
    }

    void IdlePeriodState::SetIdleToken(uint64_t aId, TimeDuration aDuration) {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");

      // We check the request ID.  It's possible that the server may be granting a
      // an ealier request that the client has since cancelled and re-requested.
      if (mIdleRequestId == aId) {
        mIdleToken = TimeStamp::Now() + aDuration;
      }
    }

    void IdlePeriodState::SetActive() {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");
      MOZ_ASSERT(!mActive);
      if (mIdleScheduler) {
        mIdleScheduler->SetActive();
      }
      mActive = true;
    }

    void IdlePeriodState::SetPaused(const MutexAutoUnlock& aProofOfUnlock) {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");
      MOZ_ASSERT(mActive);
      if (mIdleScheduler && mIdleScheduler->SetPaused()) {
        // We may have gotten a free cpu core for running idle tasks.
        // We don't try to catch the case when there are prioritized processes
        // running.

        // This SendSchedule call is why we need the MutexAutoUnlock here, because
        // IPC can do weird things with mutexes.
        mIdleScheduler->SendSchedule();
      }
      mActive = false;
    }

    void IdlePeriodState::ClearIdleToken() {
      MOZ_ASSERT(NS_IsMainThread(),
                 "Why are we touching idle state off the main thread?");

      if (mIdleRequestId) {
        if (mIdleScheduler) {
          // This SendIdleTimeUsed call is why we need to not be holding
          // any locks here, because IPC can do weird things with mutexes.
          // Ideally we'd have a MutexAutoUnlock& reference here, but some
          // callers end up here while just not holding any locks at all.
          mIdleScheduler->SendIdleTimeUsed(mIdleRequestId);
        }
        mIdleRequestId = 0;
        mIdleToken = TimeStamp();
      }
    }

    bool IdlePeriodState::ShouldGetIdleToken() {
      return StaticPrefs::idle_period_cross_process_scheduling() &&
             XRE_IsContentProcess();
    }
    }  // namespace mozilla