tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

ProgressLogger.h (21660B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      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 ProgressLogger_h
      8 #define ProgressLogger_h
      9 
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/ProportionValue.h"
     12 #include "mozilla/RefCounted.h"
     13 #include "mozilla/RefPtr.h"
     14 
     15 #include <atomic>
     16 
     17 // Uncomment to printf ProcessLogger updates.
     18 // #define DEBUG_PROCESSLOGGER
     19 
     20 #ifdef DEBUG_PROCESSLOGGER
     21 #  include "mozilla/BaseProfilerUtils.h"
     22 #  include <cstdio>
     23 #endif  // DEBUG_PROCESSLOGGER
     24 
     25 namespace mozilla {
     26 
     27 // A `ProgressLogger` is used to update a referenced atomic `ProportionValue`,
     28 // and can recursively create a sub-logger corresponding to a subset of their
     29 // own range, but that sub-logger's updates are done in its local 0%-100% range.
     30 // The typical usage is for multi-level tasks, where each level can estimate its
     31 // own work and the work delegated to a next-level function, without knowing how
     32 // this local work relates to the higher-level total work. See
     33 // `CreateSubLoggerFromTo` for details.
     34 // Note that this implementation is single-threaded, it does not support logging
     35 // progress from multiple threads at the same time.
     36 class ProgressLogger {
     37 public:
     38  // An RefPtr'd object of this class is used as the target of all
     39  // ProgressLogger updates, and it may be shared to make these updates visible
     40  // from other code in any thread.
     41  class SharedProgress : public external::AtomicRefCounted<SharedProgress> {
     42   public:
     43    MOZ_DECLARE_REFCOUNTED_TYPENAME(SharedProgress)
     44 
     45    SharedProgress() = default;
     46 
     47    SharedProgress(const SharedProgress&) = delete;
     48    SharedProgress& operator=(const SharedProgress&) = delete;
     49 
     50    // This constant is used to indicate that an update may change the progress
     51    // value, but should not modify the previously-recorded location.
     52    static constexpr const char* NO_LOCATION_UPDATE = nullptr;
     53 
     54    // Set the current progress and location, but the previous location is not
     55    // overwritten if the new one is null or empty.
     56    // The location and then the progress are atomically "released", so that all
     57    // preceding writes on this thread will be visible to other threads reading
     58    // these values; most importantly when reaching 100% progress, the reader
     59    // can be confident that the location is final and the operation being
     60    // watched has completed.
     61    void SetProgress(
     62        ProportionValue aProgress,
     63        const char* aLocationOrNullEmptyToIgnore = NO_LOCATION_UPDATE) {
     64      if (aLocationOrNullEmptyToIgnore &&
     65          *aLocationOrNullEmptyToIgnore != '\0') {
     66        mLastLocation.store(aLocationOrNullEmptyToIgnore,
     67                            std::memory_order_release);
     68      }
     69      mProgress.store(aProgress, std::memory_order_release);
     70    }
     71 
     72    // Read the current progress value. Atomically "acquired", so that writes
     73    // from the thread that stored this value are all visible to the reader
     74    // here; most importantly when reaching 100%, we can be confident that the
     75    // location is final and the operation being watched has completed.
     76    [[nodiscard]] ProportionValue Progress() const {
     77      return mProgress.load(std::memory_order_acquire);
     78    }
     79 
     80    // Read the current progress value. Atomically "acquired".
     81    [[nodiscard]] const char* LastLocation() const {
     82      return mLastLocation.load(std::memory_order_acquire);
     83    }
     84 
     85   private:
     86    friend mozilla::detail::RefCounted<SharedProgress,
     87                                       mozilla::detail::AtomicRefCount>;
     88    ~SharedProgress() = default;
     89 
     90    // Progress and last-known location.
     91    // Beware that these two values are not strongly tied: Reading one then the
     92    // other may give mismatched information; but it should be fine for
     93    // informational usage.
     94    // They are stored using atomic acquire-release ordering, to guarantee that
     95    // when read, all writes preceding these values are visible.
     96    std::atomic<ProportionValue> mProgress = ProportionValue{0.0};
     97    std::atomic<const char*> mLastLocation = nullptr;
     98  };
     99 
    100  static constexpr const char* NO_LOCATION_UPDATE =
    101      SharedProgress::NO_LOCATION_UPDATE;
    102 
    103  ProgressLogger() = default;
    104 
    105  // Construct a top-level logger, starting at 0% and expected to end at 100%.
    106  explicit ProgressLogger(
    107      RefPtr<SharedProgress> aGlobalProgressOrNull,
    108      const char* aLocationOrNullEmptyToIgnoreAtStart = NO_LOCATION_UPDATE,
    109      const char* aLocationOrNullEmptyToIgnoreAtEnd = NO_LOCATION_UPDATE)
    110      : ProgressLogger{std::move(aGlobalProgressOrNull),
    111                       /* Start */ ProportionValue{0.0},
    112                       /* Multiplier */ ProportionValue{1.0},
    113                       aLocationOrNullEmptyToIgnoreAtStart,
    114                       aLocationOrNullEmptyToIgnoreAtEnd} {}
    115 
    116  // Don't make copies, it would be confusing!
    117  // TODO: Copies could one day be allowed to track multi-threaded work, but it
    118  // is outside the scope of this implementation; Please update if needed.
    119  ProgressLogger(const ProgressLogger&) = delete;
    120  ProgressLogger& operator&(const ProgressLogger&) = delete;
    121 
    122  // Move-construct is allowed, to return from CreateSubLoggerFromTo, and
    123  // forward straight into a function. Note that moved-from ProgressLoggers must
    124  // not be used anymore! Use `CreateSubLoggerFromTo` to pass a sub-logger to
    125  // functions.
    126  ProgressLogger(ProgressLogger&& aOther)
    127      : mGlobalProgressOrNull(std::move(aOther.mGlobalProgressOrNull)),
    128        mLocalStartInGlobalSpace(aOther.mLocalStartInGlobalSpace),
    129        mLocalToGlobalMultiplier(aOther.mLocalToGlobalMultiplier),
    130        mLocationAtDestruction(aOther.mLocationAtDestruction) {
    131    aOther.MarkMovedFrom();
    132 #ifdef DEBUG_PROCESSLOGGER
    133    if (mGlobalProgressOrNull) {
    134      printf("[%d] Moved (staying globally at %.2f in [%.2f, %.2f])\n",
    135             int(baseprofiler::profiler_current_process_id().ToNumber()),
    136             GetGlobalProgress().ToDouble() * 100.0,
    137             mLocalStartInGlobalSpace.ToDouble() * 100.0,
    138             (mLocalStartInGlobalSpace + mLocalToGlobalMultiplier).ToDouble() *
    139                 100.0);
    140    }
    141 #endif  // DEBUG_PROCESSLOGGER
    142  }
    143 
    144  // Move-assign. This may be useful when starting with a default (empty) logger
    145  // and later assigning it a progress value to start updating.
    146  ProgressLogger& operator=(ProgressLogger&& aOther) {
    147    mGlobalProgressOrNull = std::move(aOther.mGlobalProgressOrNull);
    148    mLocalStartInGlobalSpace = aOther.mLocalStartInGlobalSpace;
    149    mLocalToGlobalMultiplier = aOther.mLocalToGlobalMultiplier;
    150    mLocationAtDestruction = aOther.mLocationAtDestruction;
    151    aOther.MarkMovedFrom();
    152 #ifdef DEBUG_PROCESSLOGGER
    153    if (mGlobalProgressOrNull) {
    154      printf("[%d] Re-assigned (globally at %.2f in [%.2f, %.2f])\n",
    155             int(baseprofiler::profiler_current_process_id().ToNumber()),
    156             GetGlobalProgress().ToDouble() * 100.0,
    157             mLocalStartInGlobalSpace.ToDouble() * 100.0,
    158             (mLocalStartInGlobalSpace + mLocalToGlobalMultiplier).ToDouble() *
    159                 100.0);
    160    }
    161 #endif  // DEBUG_PROCESSLOGGER
    162    return *this;
    163  }
    164 
    165  // Destruction sets the local update value to 100% unless empty or moved-from.
    166  ~ProgressLogger() {
    167    if (!IsMovedFrom()) {
    168 #ifdef DEBUG_PROCESSLOGGER
    169      if (mGlobalProgressOrNull) {
    170        printf("[%d] Destruction:\n",
    171               int(baseprofiler::profiler_current_process_id().ToNumber()));
    172      }
    173 #endif  // DEBUG_PROCESSLOGGER
    174      SetLocalProgress(ProportionValue{1.0}, mLocationAtDestruction);
    175    }
    176  }
    177 
    178  // Retrieve the current progress in the global space. May be invalid.
    179  [[nodiscard]] ProportionValue GetGlobalProgress() const {
    180    return mGlobalProgressOrNull ? mGlobalProgressOrNull->Progress()
    181                                 : ProportionValue::MakeInvalid();
    182  }
    183 
    184  // Retrieve the last known global location. May be null.
    185  [[nodiscard]] const char* GetLastGlobalLocation() const {
    186    return mGlobalProgressOrNull ? mGlobalProgressOrNull->LastLocation()
    187                                 : nullptr;
    188  }
    189 
    190  // Set the current progress in the local space.
    191  void SetLocalProgress(ProportionValue aLocalProgress,
    192                        const char* aLocationOrNullEmptyToIgnore) {
    193    MOZ_ASSERT(!IsMovedFrom());
    194    if (mGlobalProgressOrNull && !mLocalToGlobalMultiplier.IsExactlyZero()) {
    195      mGlobalProgressOrNull->SetProgress(LocalToGlobal(aLocalProgress),
    196                                         aLocationOrNullEmptyToIgnore);
    197 #ifdef DEBUG_PROCESSLOGGER
    198      printf("[%d] - local %.0f%% ~ global %.2f%% \"%s\"\n",
    199             int(baseprofiler::profiler_current_process_id().ToNumber()),
    200             aLocalProgress.ToDouble() * 100.0,
    201             LocalToGlobal(aLocalProgress).ToDouble() * 100.0,
    202             aLocationOrNullEmptyToIgnore ? aLocationOrNullEmptyToIgnore
    203                                          : "<null>");
    204 #endif  // DEBUG_PROCESSLOGGER
    205    }
    206  }
    207 
    208  // Create a sub-logger that will record progress in the given local range.
    209  // E.g.: `f(pl.CreateSubLoggerFromTo(0.2, "f...", 0.4, "f done"));` expects
    210  // that `f` will produce work in the local range 0.2 (when starting) to 0.4
    211  // (when returning); `f` itself will update this provided logger from 0.0
    212  // to 1.0 (local to that `f` function), which will effectively be converted to
    213  // 0.2-0.4 (local to the calling function).
    214  // This can cascade multiple levels, each deeper level affecting a smaller and
    215  // smaller range in the global output.
    216  [[nodiscard]] ProgressLogger CreateSubLoggerFromTo(
    217      ProportionValue aSubStartInLocalSpace,
    218      const char* aLocationOrNullEmptyToIgnoreAtStart,
    219      ProportionValue aSubEndInLocalSpace,
    220      const char* aLocationOrNullEmptyToIgnoreAtEnd = NO_LOCATION_UPDATE) {
    221    MOZ_ASSERT(!IsMovedFrom());
    222    if (!mGlobalProgressOrNull) {
    223      return ProgressLogger{};
    224    }
    225    const ProportionValue subStartInGlobalSpace =
    226        LocalToGlobal(aSubStartInLocalSpace);
    227    const ProportionValue subEndInGlobalSpace =
    228        LocalToGlobal(aSubEndInLocalSpace);
    229    if (subStartInGlobalSpace.IsInvalid() || subEndInGlobalSpace.IsInvalid()) {
    230      return ProgressLogger{mGlobalProgressOrNull,
    231                            /* Start */ ProportionValue::MakeInvalid(),
    232                            /* Multiplier */ ProportionValue{0.0},
    233                            aLocationOrNullEmptyToIgnoreAtStart,
    234                            aLocationOrNullEmptyToIgnoreAtEnd};
    235    }
    236 #ifdef DEBUG_PROCESSLOGGER
    237    if (mGlobalProgressOrNull) {
    238      printf("[%d] * Sub: local [%.0f%%, %.0f%%] ~ global [%.2f%%, %.2f%%]\n",
    239             int(baseprofiler::profiler_current_process_id().ToNumber()),
    240             aSubStartInLocalSpace.ToDouble() * 100.0,
    241             aSubEndInLocalSpace.ToDouble() * 100.0,
    242             subStartInGlobalSpace.ToDouble() * 100.0,
    243             subEndInGlobalSpace.ToDouble() * 100.0);
    244    }
    245 #endif  // DEBUG_PROCESSLOGGER
    246    return ProgressLogger{
    247        mGlobalProgressOrNull,
    248        /* Start */ subStartInGlobalSpace,
    249        /* Multipler */ subEndInGlobalSpace - subStartInGlobalSpace,
    250        aLocationOrNullEmptyToIgnoreAtStart, aLocationOrNullEmptyToIgnoreAtEnd};
    251  }
    252 
    253  // Helper with no start location.
    254  [[nodiscard]] ProgressLogger CreateSubLoggerFromTo(
    255      ProportionValue aSubStartInLocalSpace,
    256      ProportionValue aSubEndInLocalSpace,
    257      const char* aLocationOrNullEmptyToIgnoreAtEnd = NO_LOCATION_UPDATE) {
    258    return CreateSubLoggerFromTo(aSubStartInLocalSpace, NO_LOCATION_UPDATE,
    259                                 aSubEndInLocalSpace,
    260                                 aLocationOrNullEmptyToIgnoreAtEnd);
    261  }
    262 
    263  // Helper using the current progress as start.
    264  [[nodiscard]] ProgressLogger CreateSubLoggerTo(
    265      const char* aLocationOrNullEmptyToIgnoreAtStart,
    266      ProportionValue aSubEndInLocalSpace,
    267      const char* aLocationOrNullEmptyToIgnoreAtEnd = NO_LOCATION_UPDATE) {
    268    MOZ_ASSERT(!IsMovedFrom());
    269    if (!mGlobalProgressOrNull) {
    270      return ProgressLogger{};
    271    }
    272    const ProportionValue subStartInGlobalSpace = GetGlobalProgress();
    273    const ProportionValue subEndInGlobalSpace =
    274        LocalToGlobal(aSubEndInLocalSpace);
    275    if (subStartInGlobalSpace.IsInvalid() || subEndInGlobalSpace.IsInvalid()) {
    276      return ProgressLogger{mGlobalProgressOrNull,
    277                            /* Start */ ProportionValue::MakeInvalid(),
    278                            /* Multiplier */ ProportionValue{0.0},
    279                            aLocationOrNullEmptyToIgnoreAtStart,
    280                            aLocationOrNullEmptyToIgnoreAtEnd};
    281    }
    282 #ifdef DEBUG_PROCESSLOGGER
    283    if (mGlobalProgressOrNull) {
    284      printf("[%d] * Sub: local [(here), %.0f%%] ~ global [%.2f%%, %.2f%%]\n",
    285             int(baseprofiler::profiler_current_process_id().ToNumber()),
    286             aSubEndInLocalSpace.ToDouble() * 100.0,
    287             subStartInGlobalSpace.ToDouble() * 100.0,
    288             subEndInGlobalSpace.ToDouble() * 100.0);
    289    }
    290 #endif  // DEBUG_PROCESSLOGGER
    291    return ProgressLogger{
    292        mGlobalProgressOrNull,
    293        /* Start */ subStartInGlobalSpace,
    294        /* Multiplier */ subEndInGlobalSpace - subStartInGlobalSpace,
    295        aLocationOrNullEmptyToIgnoreAtStart, aLocationOrNullEmptyToIgnoreAtEnd};
    296  }
    297 
    298  // Helper using the current progress as start, no start location.
    299  [[nodiscard]] ProgressLogger CreateSubLoggerTo(
    300      ProportionValue aSubEndInLocalSpace,
    301      const char* aLocationOrNullEmptyToIgnoreAtEnd = NO_LOCATION_UPDATE) {
    302    return CreateSubLoggerTo(NO_LOCATION_UPDATE, aSubEndInLocalSpace,
    303                             aLocationOrNullEmptyToIgnoreAtEnd);
    304  }
    305 
    306  class IndexAndProgressLoggerRange;
    307 
    308  [[nodiscard]] inline IndexAndProgressLoggerRange CreateLoopSubLoggersFromTo(
    309      ProportionValue aLoopStartInLocalSpace,
    310      ProportionValue aLoopEndInLocalSpace, uint32_t aLoopCount,
    311      const char* aLocationOrNullEmptyToIgnoreAtEdges =
    312          ProgressLogger::NO_LOCATION_UPDATE);
    313  [[nodiscard]] inline IndexAndProgressLoggerRange CreateLoopSubLoggersTo(
    314      ProportionValue aLoopEndInLocalSpace, uint32_t aLoopCount,
    315      const char* aLocationOrNullEmptyToIgnoreAtEdges =
    316          ProgressLogger::NO_LOCATION_UPDATE);
    317 
    318 private:
    319  // All constructions start at the local 0%.
    320  ProgressLogger(RefPtr<SharedProgress> aGlobalProgressOrNull,
    321                 ProportionValue aLocalStartInGlobalSpace,
    322                 ProportionValue aLocalToGlobalMultiplier,
    323                 const char* aLocationOrNullEmptyToIgnoreAtConstruction,
    324                 const char* aLocationOrNullEmptyToIgnoreAtDestruction)
    325      : mGlobalProgressOrNull(std::move(aGlobalProgressOrNull)),
    326        mLocalStartInGlobalSpace(aLocalStartInGlobalSpace),
    327        mLocalToGlobalMultiplier(aLocalToGlobalMultiplier),
    328        mLocationAtDestruction(aLocationOrNullEmptyToIgnoreAtDestruction) {
    329    MOZ_ASSERT(!IsMovedFrom(), "Don't construct a moved-from object!");
    330    SetLocalProgress(ProportionValue{0.0},
    331                     aLocationOrNullEmptyToIgnoreAtConstruction);
    332  }
    333 
    334  void MarkMovedFrom() {
    335    mLocalToGlobalMultiplier = ProportionValue::MakeInvalid();
    336  }
    337  [[nodiscard]] bool IsMovedFrom() const {
    338    return mLocalToGlobalMultiplier.IsInvalid();
    339  }
    340 
    341  [[nodiscard]] ProportionValue LocalToGlobal(
    342      ProportionValue aLocalProgress) const {
    343    return aLocalProgress * mLocalToGlobalMultiplier + mLocalStartInGlobalSpace;
    344  }
    345 
    346  // Global progress value to update from local changes.
    347  RefPtr<SharedProgress> mGlobalProgressOrNull;
    348 
    349  // How much to multiply and add to a local [0, 100%] value, to get the
    350  // corresponding value in the global space.
    351  // If mLocalToGlobalMultiplier is invalid, this ProgressLogger is moved-from,
    352  // functions should not be used, and destructor won't update progress.
    353  ProportionValue mLocalStartInGlobalSpace;
    354  ProportionValue mLocalToGlobalMultiplier;
    355 
    356  const char* mLocationAtDestruction = nullptr;
    357 };
    358 
    359 // Helper class for range-for loop, e.g., with `aProgressLogger`:
    360 //   for (auto [index, loopProgressLogger] :
    361 //        IndexAndProgressLoggerRange{aProgressLogger, 30_pc, 50_pc, 10,
    362 //                                    "looping..."}) {
    363 //     // This will loop 10 times.
    364 //     // `index` is the loop index, from 0 to 9.
    365 //     // The overall loop will start at 30% and end at 50% of aProgressLogger.
    366 //     // `loopProgressLogger` is the progress logger for each iteration,
    367 //     // covering 1/10th of the range, therefore: [30%,32%], then [32%,34%],
    368 //     // etc. until [48%,50%].
    369 //     // Progress is automatically updated before/after each loop.
    370 //   }
    371 // Note that this implementation is single-threaded, it does not support logging
    372 // progress from parallel loops.
    373 class ProgressLogger::IndexAndProgressLoggerRange {
    374 public:
    375  struct IndexAndProgressLogger {
    376    uint32_t index;
    377    ProgressLogger progressLogger;
    378  };
    379 
    380  class IndexAndProgressLoggerEndIterator {
    381   public:
    382    explicit IndexAndProgressLoggerEndIterator(uint32_t aIndex)
    383        : mIndex(aIndex) {}
    384 
    385    [[nodiscard]] uint32_t Index() const { return mIndex; }
    386 
    387   private:
    388    uint32_t mIndex;
    389  };
    390 
    391  class IndexAndProgressLoggerIterator {
    392   public:
    393    IndexAndProgressLoggerIterator(
    394        RefPtr<ProgressLogger::SharedProgress> aGlobalProgressOrNull,
    395        ProportionValue aLoopStartInGlobalSpace,
    396        ProportionValue aLoopIncrementInGlobalSpace,
    397        const char* aLocationOrNullEmptyToIgnoreAtEdges)
    398        : mGlobalProgressOrNull(aGlobalProgressOrNull),
    399          mLoopStartInGlobalSpace(aLoopStartInGlobalSpace),
    400          mLoopIncrementInGlobalSpace(aLoopIncrementInGlobalSpace),
    401          mIndex(0u),
    402          mLocationOrNullEmptyToIgnoreAtEdges(
    403              aLocationOrNullEmptyToIgnoreAtEdges) {
    404      if (mGlobalProgressOrNull) {
    405        mGlobalProgressOrNull->SetProgress(mLoopStartInGlobalSpace,
    406                                           mLocationOrNullEmptyToIgnoreAtEdges);
    407      }
    408    }
    409 
    410    [[nodiscard]] IndexAndProgressLogger operator*() {
    411      return IndexAndProgressLogger{
    412          mIndex,
    413          mGlobalProgressOrNull
    414              ? ProgressLogger{mGlobalProgressOrNull, mLoopStartInGlobalSpace,
    415                               mLoopIncrementInGlobalSpace,
    416                               ProgressLogger::NO_LOCATION_UPDATE,
    417                               ProgressLogger::NO_LOCATION_UPDATE}
    418              : ProgressLogger{}};
    419    }
    420 
    421    [[nodiscard]] bool operator!=(
    422        const IndexAndProgressLoggerEndIterator& aEnd) const {
    423      return mIndex != aEnd.Index();
    424    }
    425 
    426    IndexAndProgressLoggerIterator& operator++() {
    427      ++mIndex;
    428      mLoopStartInGlobalSpace =
    429          mLoopStartInGlobalSpace + mLoopIncrementInGlobalSpace;
    430      if (mGlobalProgressOrNull) {
    431        mGlobalProgressOrNull->SetProgress(mLoopStartInGlobalSpace,
    432                                           mLocationOrNullEmptyToIgnoreAtEdges);
    433      }
    434      return *this;
    435    }
    436 
    437   private:
    438    RefPtr<ProgressLogger::SharedProgress> mGlobalProgressOrNull;
    439    ProportionValue mLoopStartInGlobalSpace;
    440    ProportionValue mLoopIncrementInGlobalSpace;
    441    uint32_t mIndex;
    442    const char* mLocationOrNullEmptyToIgnoreAtEdges;
    443  };
    444 
    445  [[nodiscard]] IndexAndProgressLoggerIterator begin() {
    446    return IndexAndProgressLoggerIterator{
    447        mGlobalProgressOrNull, mLoopStartInGlobalSpace,
    448        mLoopIncrementInGlobalSpace, mLocationOrNullEmptyToIgnoreAtEdges};
    449  }
    450 
    451  [[nodiscard]] IndexAndProgressLoggerEndIterator end() {
    452    return IndexAndProgressLoggerEndIterator{mLoopCount};
    453  }
    454 
    455 private:
    456  friend class ProgressLogger;
    457  IndexAndProgressLoggerRange(ProgressLogger& aProgressLogger,
    458                              ProportionValue aLoopStartInGlobalSpace,
    459                              ProportionValue aLoopEndInGlobalSpace,
    460                              uint32_t aLoopCount,
    461                              const char* aLocationOrNullEmptyToIgnoreAtEdges =
    462                                  ProgressLogger::NO_LOCATION_UPDATE)
    463      : mGlobalProgressOrNull(aProgressLogger.mGlobalProgressOrNull),
    464        mLoopStartInGlobalSpace(aLoopStartInGlobalSpace),
    465        mLoopIncrementInGlobalSpace(
    466            (aLoopEndInGlobalSpace - aLoopStartInGlobalSpace) / aLoopCount),
    467        mLoopCount(aLoopCount),
    468        mLocationOrNullEmptyToIgnoreAtEdges(
    469            aLocationOrNullEmptyToIgnoreAtEdges) {}
    470 
    471  RefPtr<ProgressLogger::SharedProgress> mGlobalProgressOrNull;
    472  ProportionValue mLoopStartInGlobalSpace;
    473  ProportionValue mLoopIncrementInGlobalSpace;
    474  uint32_t mLoopCount;
    475  const char* mLocationOrNullEmptyToIgnoreAtEdges;
    476 };
    477 
    478 [[nodiscard]] ProgressLogger::IndexAndProgressLoggerRange
    479 ProgressLogger::CreateLoopSubLoggersFromTo(
    480    ProportionValue aLoopStartInLocalSpace,
    481    ProportionValue aLoopEndInLocalSpace, uint32_t aLoopCount,
    482    const char* aLocationOrNullEmptyToIgnoreAtEdges) {
    483  return IndexAndProgressLoggerRange{
    484      *this, LocalToGlobal(aLoopStartInLocalSpace),
    485      LocalToGlobal(aLoopEndInLocalSpace), aLoopCount,
    486      aLocationOrNullEmptyToIgnoreAtEdges};
    487 }
    488 
    489 [[nodiscard]] ProgressLogger::IndexAndProgressLoggerRange
    490 ProgressLogger::CreateLoopSubLoggersTo(
    491    ProportionValue aLoopEndInLocalSpace, uint32_t aLoopCount,
    492    const char* aLocationOrNullEmptyToIgnoreAtEdges) {
    493  return IndexAndProgressLoggerRange{
    494      *this, GetGlobalProgress(), LocalToGlobal(aLoopEndInLocalSpace),
    495      aLoopCount, aLocationOrNullEmptyToIgnoreAtEdges};
    496 }
    497 
    498 }  // namespace mozilla
    499 
    500 #endif  // ProgressLogger_h