tor-browser

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

SliceBudget.h (5473B)


      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 js_SliceBudget_h
      8 #define js_SliceBudget_h
      9 
     10 #include "mozilla/Assertions.h"
     11 #include "mozilla/Atomics.h"
     12 #include "mozilla/TimeStamp.h"
     13 #include "mozilla/Variant.h"
     14 
     15 #include <stdint.h>
     16 
     17 #include "jstypes.h"
     18 
     19 namespace JS {
     20 
     21 struct JS_PUBLIC_API TimeBudget {
     22  const mozilla::TimeDuration budget;
     23  mozilla::TimeStamp deadline;  // Calculated when SliceBudget is constructed.
     24 
     25  explicit TimeBudget(mozilla::TimeDuration duration) : budget(duration) {}
     26  explicit TimeBudget(int64_t milliseconds)
     27      : budget(mozilla::TimeDuration::FromMilliseconds(milliseconds)) {}
     28 
     29  void setDeadlineFromNow();
     30  double progress(mozilla::TimeStamp t) const {
     31    return (t - (deadline - budget)) / budget;
     32  }
     33 };
     34 
     35 struct JS_PUBLIC_API WorkBudget {
     36  const int64_t budget;
     37 
     38  explicit WorkBudget(int64_t work) : budget(work) {}
     39  double progress(int64_t work) const { return double(work) / double(budget); }
     40 };
     41 
     42 struct UnlimitedBudget {};
     43 
     44 /*
     45 * This class describes a limit to the amount of work to be performed in a GC
     46 * slice, so that we can return to the mutator without pausing for too long. The
     47 * budget may be based on a deadline time or an amount of work to be performed,
     48 * or may be unlimited.
     49 *
     50 * To reduce the number of gettimeofday calls, we only check the time every 1000
     51 * operations.
     52 */
     53 class JS_PUBLIC_API SliceBudget {
     54 public:
     55  using InterruptRequestFlag = mozilla::Atomic<bool, mozilla::Relaxed>;
     56 
     57 private:
     58  static constexpr int64_t UnlimitedCounter = INT64_MAX;
     59 
     60  // Most calls to isOverBudget will only check the counter value. Every N
     61  // steps, do a more "expensive" check -- look at the current time and/or
     62  // check the atomic interrupt flag.
     63  static constexpr int64_t StepsPerExpensiveCheck = 1000;
     64 
     65  // How many steps to count before checking the time and possibly the interrupt
     66  // flag.
     67  int64_t counter = StepsPerExpensiveCheck;
     68 
     69  // External flag to request the current slice to be interrupted
     70  // (and return isOverBudget() early.) Applies only to time-based budgets.
     71  InterruptRequestFlag* interruptRequested = nullptr;
     72 
     73  // Configuration
     74  mozilla::Variant<TimeBudget, WorkBudget, UnlimitedBudget> budget;
     75 
     76  // This SliceBudget is considered interrupted from the time isOverBudget()
     77  // finds the interrupt flag set.
     78  bool interrupted = false;
     79 
     80 public:
     81  // Whether this slice is running in (predicted to be) idle time.
     82  // Only used for recording in the profile.
     83  bool idle = false;
     84 
     85  // Whether this slice was given an extended budget, larger than
     86  // the predicted idle time.
     87  bool extended = false;
     88 
     89  // Temporarily allow going over budget. (isOverBudget() will return false,
     90  // though step() will still have an effect and will matter once this is set
     91  // back to false.)
     92  bool keepGoing = false;
     93 
     94 private:
     95  explicit SliceBudget(InterruptRequestFlag* irqPtr)
     96      : counter(irqPtr ? StepsPerExpensiveCheck : UnlimitedCounter),
     97        interruptRequested(irqPtr),
     98        budget(UnlimitedBudget()) {}
     99 
    100  bool checkOverBudget();
    101 
    102 public:
    103  // Use to create an unlimited budget.
    104  static SliceBudget unlimited() { return SliceBudget(nullptr); }
    105 
    106  // Instantiate as SliceBudget(TimeBudget(n)).
    107  explicit SliceBudget(TimeBudget time,
    108                       InterruptRequestFlag* interrupt = nullptr);
    109 
    110  explicit SliceBudget(mozilla::TimeDuration duration,
    111                       InterruptRequestFlag* interrupt = nullptr)
    112      : SliceBudget(TimeBudget(duration.ToMilliseconds()), interrupt) {}
    113 
    114  // Instantiate as SliceBudget(WorkBudget(n)).
    115  explicit SliceBudget(WorkBudget work);
    116 
    117  // Register having performed the given number of steps (counted against a
    118  // work budget, or progress towards the next time or callback check).
    119  void step(uint64_t steps = 1) {
    120    MOZ_ASSERT(steps > 0);
    121    counter -= steps;
    122  }
    123 
    124  // Force an "expensive" (time) check on the next call to isOverBudget. Useful
    125  // when switching between major phases of an operation like a cycle
    126  // collection.
    127  void forceCheck() {
    128    if (isTimeBudget()) {
    129      counter = 0;
    130    }
    131  }
    132 
    133  bool isOverBudget() {
    134    return counter <= 0 && !keepGoing && checkOverBudget();
    135  }
    136 
    137  // Return the fraction of progress towards the deadline. Note that this can
    138  // return values outside of the range [0,1].
    139  double progress() const {
    140    if (isUnlimited()) {
    141      return 0.0;
    142    }
    143    if (isTimeBudget()) {
    144      return budget.as<TimeBudget>().progress(mozilla::TimeStamp::Now());
    145    }
    146    return budget.as<WorkBudget>().progress(workBudget() - counter);
    147  }
    148 
    149  bool isWorkBudget() const { return budget.is<WorkBudget>(); }
    150  bool isTimeBudget() const { return budget.is<TimeBudget>(); }
    151  bool isUnlimited() const { return budget.is<UnlimitedBudget>(); }
    152 
    153  mozilla::TimeDuration timeBudgetDuration() const {
    154    return budget.as<TimeBudget>().budget;
    155  }
    156  int64_t timeBudget() const { return timeBudgetDuration().ToMilliseconds(); }
    157  int64_t workBudget() const { return budget.as<WorkBudget>().budget; }
    158 
    159  mozilla::TimeStamp deadline() const {
    160    return budget.as<TimeBudget>().deadline;
    161  }
    162 
    163  int describe(char* buffer, size_t maxlen) const;
    164 };
    165 
    166 }  // namespace JS
    167 
    168 #endif /* js_SliceBudget_h */