IdleSchedulerChild.cpp (4554B)
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 #include "mozilla/ipc/IdleSchedulerChild.h" 8 #include "mozilla/ipc/IdleSchedulerParent.h" 9 #include "mozilla/ipc/PBackgroundChild.h" 10 #include "mozilla/Atomics.h" 11 #include "mozilla/IdlePeriodState.h" 12 #include "BackgroundChild.h" 13 14 namespace mozilla::ipc { 15 16 static IdleSchedulerChild* sMainThreadIdleScheduler = nullptr; 17 static bool sIdleSchedulerDestroyed = false; 18 19 IdleSchedulerChild::~IdleSchedulerChild() { 20 if (sMainThreadIdleScheduler == this) { 21 sMainThreadIdleScheduler = nullptr; 22 sIdleSchedulerDestroyed = true; 23 } 24 MOZ_ASSERT(!mIdlePeriodState); 25 } 26 27 void IdleSchedulerChild::Init(IdlePeriodState* aIdlePeriodState) { 28 mIdlePeriodState = aIdlePeriodState; 29 30 RefPtr<IdleSchedulerChild> scheduler = this; 31 auto resolve = [&](std::tuple<mozilla::Maybe<MutableSharedMemoryHandle>, 32 uint32_t>&& aResult) { 33 if (auto& handle = std::get<0>(aResult)) { 34 mActiveCounter = handle->Map(); 35 mChildId = std::get<1>(aResult); 36 if (mChildId && mIdlePeriodState && mIdlePeriodState->IsActive()) { 37 SetActive(); 38 } 39 } 40 }; 41 42 auto reject = [&](ResponseRejectReason) {}; 43 SendInitForIdleUse(std::move(resolve), std::move(reject)); 44 } 45 46 IPCResult IdleSchedulerChild::RecvIdleTime(uint64_t aId, TimeDuration aBudget) { 47 if (mIdlePeriodState) { 48 mIdlePeriodState->SetIdleToken(aId, aBudget); 49 } 50 return IPC_OK(); 51 } 52 53 void IdleSchedulerChild::SetActive() { 54 if (mChildId && CanSend() && mActiveCounter) { 55 auto counters = mActiveCounter.DataAsSpan<Atomic<int32_t>>(); 56 ++counters[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]; 57 ++counters[mChildId]; 58 } 59 } 60 61 bool IdleSchedulerChild::SetPaused() { 62 if (mChildId && CanSend() && mActiveCounter) { 63 auto counters = mActiveCounter.DataAsSpan<Atomic<int32_t>>(); 64 --counters[mChildId]; 65 // The following expression reduces the global activity count and checks if 66 // it drops below the cpu counter limit. 67 return counters[NS_IDLE_SCHEDULER_INDEX_OF_ACTIVITY_COUNTER]-- == 68 counters[NS_IDLE_SCHEDULER_INDEX_OF_CPU_COUNTER]; 69 } 70 71 return false; 72 } 73 74 RefPtr<IdleSchedulerChild::MayGCPromise> IdleSchedulerChild::MayGCNow() { 75 if (mIsRequestingGC || mIsDoingGC) { 76 return MayGCPromise::CreateAndResolve(false, __func__); 77 } 78 79 mIsRequestingGC = true; 80 return SendRequestGC()->Then( 81 GetMainThreadSerialEventTarget(), __func__, 82 [self = RefPtr(this)](bool aIgnored) { 83 // Only one of these may be true at a time. 84 MOZ_ASSERT(!(self->mIsRequestingGC && self->mIsDoingGC)); 85 86 // The parent process always says yes, sometimes after a delay. 87 if (self->mIsRequestingGC) { 88 self->mIsRequestingGC = false; 89 self->mIsDoingGC = true; 90 return MayGCPromise::CreateAndResolve(true, __func__); 91 } 92 return MayGCPromise::CreateAndResolve(false, __func__); 93 }, 94 [self = RefPtr(this)](ResponseRejectReason reason) { 95 self->mIsRequestingGC = false; 96 return MayGCPromise::CreateAndReject(reason, __func__); 97 }); 98 } 99 100 void IdleSchedulerChild::StartedGC() { 101 // Only one of these may be true at a time. 102 MOZ_ASSERT(!(mIsRequestingGC && mIsDoingGC)); 103 104 // If mRequestingGC was true then when the outstanding GC request returns 105 // it'll see that the GC has already started. 106 mIsRequestingGC = false; 107 108 if (!mIsDoingGC) { 109 if (CanSend()) { 110 SendStartedGC(); 111 } 112 mIsDoingGC = true; 113 } 114 } 115 116 void IdleSchedulerChild::DoneGC() { 117 if (mIsDoingGC) { 118 if (CanSend()) { 119 SendDoneGC(); 120 } 121 mIsDoingGC = false; 122 } 123 } 124 125 IdleSchedulerChild* IdleSchedulerChild::GetMainThreadIdleScheduler() { 126 MOZ_ASSERT(NS_IsMainThread()); 127 128 if (sMainThreadIdleScheduler) { 129 return sMainThreadIdleScheduler; 130 } 131 132 if (sIdleSchedulerDestroyed) { 133 return nullptr; 134 } 135 136 ipc::PBackgroundChild* background = 137 ipc::BackgroundChild::GetOrCreateForCurrentThread(); 138 if (background) { 139 // this is nulled out on our destruction, so we don't need to worry 140 sMainThreadIdleScheduler = new ipc::IdleSchedulerChild(); 141 MOZ_ALWAYS_TRUE( 142 background->SendPIdleSchedulerConstructor(sMainThreadIdleScheduler)); 143 } 144 return sMainThreadIdleScheduler; 145 } 146 147 } // namespace mozilla::ipc