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 TimeBudgetTimeBudget25 explicit TimeBudget(mozilla::TimeDuration duration) : budget(duration) {} TimeBudgetTimeBudget26 explicit TimeBudget(int64_t milliseconds) 27 : budget(mozilla::TimeDuration::FromMilliseconds(milliseconds)) {} 28 29 void setDeadlineFromNow(); 30 }; 31 32 struct JS_PUBLIC_API WorkBudget { 33 const int64_t budget; 34 WorkBudgetWorkBudget35 explicit WorkBudget(int64_t work) : budget(work) {} 36 }; 37 38 struct UnlimitedBudget {}; 39 40 /* 41 * This class describes a limit to the amount of work to be performed in a GC 42 * slice, so that we can return to the mutator without pausing for too long. The 43 * budget may be based on a deadline time or an amount of work to be performed, 44 * or may be unlimited. 45 * 46 * To reduce the number of gettimeofday calls, we only check the time every 1000 47 * operations. 48 */ 49 class JS_PUBLIC_API SliceBudget { 50 public: 51 using InterruptRequestFlag = mozilla::Atomic<bool>; 52 53 // Whether this slice is running in (predicted to be) idle time. 54 // Only used for recording in the profile. 55 bool idle = false; 56 57 // Whether this slice was given an extended budget, larger than 58 // the predicted idle time. 59 bool extended = false; 60 61 private: 62 static const intptr_t UnlimitedCounter = INTPTR_MAX; 63 64 // Most calls to isOverBudget will only check the counter value. Every N 65 // steps, do a more "expensive" check -- look at the current time and/or 66 // check the atomic interrupt flag. 67 static constexpr intptr_t StepsPerExpensiveCheck = 1000; 68 69 // Configuration 70 71 mozilla::Variant<TimeBudget, WorkBudget, UnlimitedBudget> budget; 72 73 // External flag to request the current slice to be interrupted 74 // (and return isOverBudget() early.) Applies only to time-based budgets. 75 InterruptRequestFlag* interruptRequested = nullptr; 76 77 // How many steps to count before checking the time and possibly the interrupt 78 // flag. 79 int64_t counter = StepsPerExpensiveCheck; 80 81 // This SliceBudget is considered interrupted from the time isOverBudget() 82 // finds the interrupt flag set. 83 bool interrupted = false; 84 SliceBudget(InterruptRequestFlag * irqPtr)85 explicit SliceBudget(InterruptRequestFlag* irqPtr) 86 : budget(UnlimitedBudget()), 87 interruptRequested(irqPtr), 88 counter(irqPtr ? StepsPerExpensiveCheck : UnlimitedCounter) {} 89 90 bool checkOverBudget(); 91 92 public: 93 // Use to create an unlimited budget. unlimited()94 static SliceBudget unlimited() { return SliceBudget(nullptr); } 95 96 // Instantiate as SliceBudget(TimeBudget(n)). 97 explicit SliceBudget(TimeBudget time, 98 InterruptRequestFlag* interrupt = nullptr); 99 100 explicit SliceBudget(mozilla::TimeDuration duration, 101 InterruptRequestFlag* interrupt = nullptr) 102 : SliceBudget(TimeBudget(duration.ToMilliseconds()), interrupt) {} 103 104 // Instantiate as SliceBudget(WorkBudget(n)). 105 explicit SliceBudget(WorkBudget work); 106 107 // Register having performed the given number of steps (counted against a 108 // work budget, or progress towards the next time or callback check). 109 void step(uint64_t steps = 1) { 110 MOZ_ASSERT(steps > 0); 111 counter -= steps; 112 } 113 114 // Do enough steps to force an "expensive" (time and/or callback) check on 115 // the next call to isOverBudget. Useful when switching between major phases 116 // of an operation like a cycle collection. stepAndForceCheck()117 void stepAndForceCheck() { 118 if (!isUnlimited()) { 119 counter = 0; 120 } 121 } 122 isOverBudget()123 bool isOverBudget() { return counter <= 0 && checkOverBudget(); } 124 isWorkBudget()125 bool isWorkBudget() const { return budget.is<WorkBudget>(); } isTimeBudget()126 bool isTimeBudget() const { return budget.is<TimeBudget>(); } isUnlimited()127 bool isUnlimited() const { return budget.is<UnlimitedBudget>(); } 128 timeBudget()129 int64_t timeBudget() const { 130 return budget.as<TimeBudget>().budget.ToMilliseconds(); 131 } workBudget()132 int64_t workBudget() const { return budget.as<WorkBudget>().budget; } 133 deadline()134 mozilla::TimeStamp deadline() const { 135 return budget.as<TimeBudget>().deadline; 136 } 137 138 int describe(char* buffer, size_t maxlen) const; 139 }; 140 141 } // namespace js 142 143 #endif /* js_SliceBudget_h */ 144