AllocationPolicy.cpp (8398B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim:set ts=2 sw=2 sts=2 et cindent: */ 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 "AllocationPolicy.h" 8 9 #include "ImageContainer.h" 10 #include "MediaInfo.h" 11 #include "PDMFactory.h" 12 #include "mozilla/ClearOnShutdown.h" 13 #include "mozilla/SchedulerGroup.h" 14 #ifdef MOZ_WIDGET_ANDROID 15 # include "mozilla/jni/Utils.h" 16 #endif 17 18 namespace mozilla { 19 20 using TrackType = TrackInfo::TrackType; 21 22 class AllocPolicyImpl::AutoDeallocToken : public Token { 23 public: 24 explicit AutoDeallocToken(const RefPtr<AllocPolicyImpl>& aPolicy) 25 : mPolicy(aPolicy) {} 26 27 private: 28 ~AutoDeallocToken() { mPolicy->Dealloc(); } 29 30 RefPtr<AllocPolicyImpl> mPolicy; 31 }; 32 33 AllocPolicyImpl::AllocPolicyImpl(int aDecoderLimit) 34 : mMaxDecoderLimit(aDecoderLimit), 35 mMonitor("AllocPolicyImpl"), 36 mDecoderLimit(aDecoderLimit) {} 37 AllocPolicyImpl::~AllocPolicyImpl() { RejectAll(); } 38 39 auto AllocPolicyImpl::Alloc() -> RefPtr<Promise> { 40 ReentrantMonitorAutoEnter mon(mMonitor); 41 // No decoder limit set. 42 if (mDecoderLimit < 0) { 43 return Promise::CreateAndResolve(new Token(), __func__); 44 } 45 46 RefPtr<PromisePrivate> p = new PromisePrivate(__func__); 47 mPromises.push(p); 48 ResolvePromise(mon); 49 return p; 50 } 51 52 void AllocPolicyImpl::Dealloc() { 53 ReentrantMonitorAutoEnter mon(mMonitor); 54 ++mDecoderLimit; 55 ResolvePromise(mon); 56 } 57 58 void AllocPolicyImpl::ResolvePromise(ReentrantMonitorAutoEnter& aProofOfLock) { 59 MOZ_ASSERT(mDecoderLimit >= 0); 60 61 if (mDecoderLimit > 0 && !mPromises.empty()) { 62 --mDecoderLimit; 63 RefPtr<PromisePrivate> p = std::move(mPromises.front()); 64 mPromises.pop(); 65 p->Resolve(new AutoDeallocToken(this), __func__); 66 } 67 } 68 69 void AllocPolicyImpl::RejectAll() { 70 ReentrantMonitorAutoEnter mon(mMonitor); 71 while (!mPromises.empty()) { 72 RefPtr<PromisePrivate> p = std::move(mPromises.front()); 73 mPromises.pop(); 74 p->Reject(true, __func__); 75 } 76 } 77 78 static int32_t MediaDecoderLimitDefault() { return -1; } 79 80 StaticMutex GlobalAllocPolicy::sMutex; 81 82 NotNull<AllocPolicy*> GlobalAllocPolicy::Instance(TrackType aTrack) { 83 StaticMutexAutoLock lock(sMutex); 84 if (aTrack == TrackType::kAudioTrack) { 85 static RefPtr<AllocPolicyImpl> sAudioPolicy = []() { 86 SchedulerGroup::Dispatch(NS_NewRunnableFunction( 87 "GlobalAllocPolicy::GlobalAllocPolicy:Audio", []() { 88 ClearOnShutdown(&sAudioPolicy, ShutdownPhase::XPCOMShutdownThreads); 89 })); 90 return new AllocPolicyImpl(MediaDecoderLimitDefault()); 91 }(); 92 return WrapNotNull(sAudioPolicy.get()); 93 } 94 static RefPtr<AllocPolicyImpl> sVideoPolicy = []() { 95 SchedulerGroup::Dispatch(NS_NewRunnableFunction( 96 "GlobalAllocPolicy::GlobalAllocPolicy:Audio", []() { 97 ClearOnShutdown(&sVideoPolicy, ShutdownPhase::XPCOMShutdownThreads); 98 })); 99 return new AllocPolicyImpl(MediaDecoderLimitDefault()); 100 }(); 101 return WrapNotNull(sVideoPolicy.get()); 102 } 103 104 class SingleAllocPolicy::AutoDeallocCombinedToken : public Token { 105 public: 106 AutoDeallocCombinedToken(already_AddRefed<Token> aSingleAllocPolicyToken, 107 already_AddRefed<Token> aGlobalAllocPolicyToken) 108 : mSingleToken(aSingleAllocPolicyToken), 109 mGlobalToken(aGlobalAllocPolicyToken) {} 110 111 private: 112 // Release tokens allocated from GlobalAllocPolicy and LocalAllocPolicy 113 // and process next token request if any. 114 ~AutoDeallocCombinedToken() = default; 115 const RefPtr<Token> mSingleToken; 116 const RefPtr<Token> mGlobalToken; 117 }; 118 119 auto SingleAllocPolicy::Alloc() -> RefPtr<Promise> { 120 MOZ_DIAGNOSTIC_ASSERT(MaxDecoderLimit() == 1, 121 "We can only handle at most one token out at a time."); 122 RefPtr<SingleAllocPolicy> self = this; 123 return AllocPolicyImpl::Alloc()->Then( 124 mOwnerThread, __func__, 125 [self](RefPtr<Token> aToken) { 126 RefPtr<Token> localToken = std::move(aToken); 127 RefPtr<Promise> p = self->mPendingPromise.Ensure(__func__); 128 GlobalAllocPolicy::Instance(self->mTrack) 129 ->Alloc() 130 ->Then( 131 self->mOwnerThread, __func__, 132 [self, localToken = std::move(localToken)]( 133 RefPtr<Token> aToken) mutable { 134 self->mTokenRequest.Complete(); 135 RefPtr<Token> combinedToken = new AutoDeallocCombinedToken( 136 localToken.forget(), aToken.forget()); 137 self->mPendingPromise.Resolve(combinedToken, __func__); 138 }, 139 [self]() { 140 self->mTokenRequest.Complete(); 141 self->mPendingPromise.Reject(true, __func__); 142 }) 143 ->Track(self->mTokenRequest); 144 return p; 145 }, 146 []() { return Promise::CreateAndReject(true, __func__); }); 147 } 148 149 SingleAllocPolicy::~SingleAllocPolicy() { 150 mPendingPromise.RejectIfExists(true, __func__); 151 mTokenRequest.DisconnectIfExists(); 152 } 153 154 void SingleAllocPolicy::Cancel() { 155 MOZ_ASSERT(mOwnerThread->IsCurrentThreadIn()); 156 mPendingPromise.RejectIfExists(true, __func__); 157 mTokenRequest.DisconnectIfExists(); 158 RejectAll(); 159 } 160 161 AllocationWrapper::AllocationWrapper( 162 already_AddRefed<MediaDataDecoder> aDecoder, already_AddRefed<Token> aToken) 163 : mDecoder(aDecoder), mToken(aToken) { 164 DecoderDoctorLogger::LogConstructionAndBase( 165 "AllocationWrapper", this, static_cast<const MediaDataDecoder*>(this)); 166 DecoderDoctorLogger::LinkParentAndChild("AllocationWrapper", this, "decoder", 167 mDecoder.get()); 168 } 169 170 AllocationWrapper::~AllocationWrapper() { 171 DecoderDoctorLogger::LogDestruction("AllocationWrapper", this); 172 } 173 174 RefPtr<ShutdownPromise> AllocationWrapper::Shutdown() { 175 RefPtr<MediaDataDecoder> decoder = std::move(mDecoder); 176 RefPtr<Token> token = std::move(mToken); 177 return decoder->Shutdown()->Then( 178 GetCurrentSerialEventTarget(), __func__, 179 [token]() { return ShutdownPromise::CreateAndResolve(true, __func__); }); 180 } 181 /* static */ RefPtr<AllocationWrapper::AllocateDecoderPromise> 182 AllocationWrapper::CreateDecoder(const CreateDecoderParams& aParams, 183 AllocPolicy* aPolicy) { 184 RefPtr<AllocateDecoderPromise> p = 185 (aPolicy ? aPolicy : GlobalAllocPolicy::Instance(aParams.mType)) 186 ->Alloc() 187 ->Then( 188 GetCurrentSerialEventTarget(), __func__, 189 [params = 190 CreateDecoderParamsForAsync(aParams)](RefPtr<Token> aToken) { 191 // result may not always be updated by 192 // PDMFactory::CreateDecoder either when the creation 193 // succeeded or failed, as such it must be initialized to a 194 // fatal error by default. 195 MediaResult result = 196 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 197 nsPrintfCString("error creating %s decoder", 198 TrackTypeToStr(params.mType))); 199 RefPtr<PDMFactory> pdm = new PDMFactory(); 200 RefPtr<PlatformDecoderModule::CreateDecoderPromise> p = 201 pdm->CreateDecoder(params)->Then( 202 GetCurrentSerialEventTarget(), __func__, 203 [aToken](RefPtr<MediaDataDecoder>&& aDecoder) mutable { 204 RefPtr<AllocationWrapper> wrapper = 205 new AllocationWrapper(aDecoder.forget(), 206 aToken.forget()); 207 return AllocateDecoderPromise::CreateAndResolve( 208 wrapper, __func__); 209 }, 210 [](const MediaResult& aError) { 211 return AllocateDecoderPromise::CreateAndReject( 212 aError, __func__); 213 }); 214 return p; 215 }, 216 []() { 217 return AllocateDecoderPromise::CreateAndReject( 218 MediaResult(NS_ERROR_DOM_MEDIA_FATAL_ERR, 219 "Allocation policy expired"), 220 __func__); 221 }); 222 return p; 223 } 224 225 } // namespace mozilla