GMPPlatform.cpp (9200B)
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6 #include "GMPPlatform.h" 7 8 #include "GMPChild.h" 9 #include "GMPStorageChild.h" 10 #include "GMPTimerChild.h" 11 #include "base/task.h" 12 #include "base/thread.h" 13 #include "base/time.h" 14 #include "mozilla/Monitor.h" 15 #include "mozilla/Mutex.h" 16 #include "mozilla/ReentrantMonitor.h" 17 #include "mozilla/StaticMonitor.h" 18 #include "nsTArray.h" 19 #include "nsThreadUtils.h" 20 21 #ifdef XP_WIN 22 # include "mozilla/UntrustedModulesProcessor.h" 23 #endif 24 25 #include <ctime> 26 27 namespace mozilla::gmp { 28 29 static MessageLoop* sMainLoop = nullptr; 30 static GMPChild* sChild = nullptr; 31 32 static StaticMonitor sMainLoopMonitor; 33 static nsTArray<RefPtr<Runnable>>* sMainLoopPendingEvents 34 MOZ_GUARDED_BY(sMainLoopMonitor) = nullptr; 35 static bool sMainLoopHasPendingProcess MOZ_GUARDED_BY(sMainLoopMonitor) = false; 36 37 static bool IsOnChildMainThread() { 38 return sMainLoop && sMainLoop == MessageLoop::current(); 39 } 40 41 // We just need a refcounted wrapper for GMPTask objects. 42 class GMPRunnable final : public Runnable { 43 public: 44 explicit GMPRunnable(GMPTask* aTask) 45 : Runnable("mozilla::gmp::GMPRunnable"), mTask(aTask) { 46 MOZ_ASSERT(mTask); 47 } 48 49 NS_IMETHOD Run() override { 50 mTask->Run(); 51 mTask->Destroy(); 52 mTask = nullptr; 53 return NS_OK; 54 } 55 56 private: 57 GMPTask* mTask; 58 }; 59 60 class GMPSyncRunnable final : public Runnable { 61 public: 62 GMPSyncRunnable(GMPTask* aTask, MessageLoop* aMessageLoop) 63 : Runnable("mozilla::gmp::GMPSyncRunnable"), 64 mDone(false), 65 mTask(aTask), 66 mMonitor("GMPSyncRunnable") { 67 MOZ_ASSERT(mTask); 68 } 69 70 void WaitUntilDone() { 71 // We assert here for two reasons. 72 // 1) Nobody should be blocking the main thread. 73 // 2) This prevents deadlocks when doing sync calls to main which if the 74 // main thread tries to do a sync call back to the calling thread. 75 MOZ_ASSERT(!IsOnChildMainThread()); 76 77 MonitorAutoLock lock(mMonitor); 78 while (!mDone) { 79 lock.Wait(); 80 } 81 } 82 83 NS_IMETHOD Run() override { 84 mTask->Run(); 85 mTask->Destroy(); 86 mTask = nullptr; 87 MonitorAutoLock lock(mMonitor); 88 mDone = true; 89 lock.Notify(); 90 return NS_OK; 91 } 92 93 private: 94 bool mDone MOZ_GUARDED_BY(mMonitor); 95 GMPTask* mTask; 96 Monitor mMonitor; 97 }; 98 99 class GMPThreadImpl final : public GMPThread { 100 public: 101 GMPThreadImpl(); 102 virtual ~GMPThreadImpl(); 103 104 // GMPThread 105 void Post(GMPTask* aTask) override; 106 void Join() override; 107 108 private: 109 Mutex mMutex MOZ_UNANNOTATED; 110 base::Thread mThread MOZ_GUARDED_BY(mMutex); 111 }; 112 113 GMPErr CreateThread(GMPThread** aThread) { 114 if (!aThread) { 115 return GMPGenericErr; 116 } 117 118 *aThread = new GMPThreadImpl(); 119 120 return GMPNoErr; 121 } 122 123 bool SpinPendingGmpEventsUntil(const SpinPendingPredicate& aPred, 124 uint32_t aTimeoutMs) { 125 MOZ_ASSERT(IsOnChildMainThread()); 126 127 auto timeout = TimeDuration::FromMilliseconds(aTimeoutMs); 128 129 while (!aPred()) { 130 nsTArray<RefPtr<Runnable>> pendingEvents; 131 { 132 StaticMonitorAutoLock lock(sMainLoopMonitor); 133 while (sMainLoopPendingEvents->IsEmpty()) { 134 if (lock.Wait(timeout) == CVStatus::Timeout) { 135 return false; 136 } 137 } 138 pendingEvents = std::move(*sMainLoopPendingEvents); 139 } 140 141 for (auto& event : pendingEvents) { 142 event->Run(); 143 } 144 } 145 146 return true; 147 } 148 149 static void ProcessPendingGmpEvents() { 150 MOZ_ASSERT(IsOnChildMainThread()); 151 152 nsTArray<RefPtr<Runnable>> pendingEvents; 153 { 154 StaticMonitorAutoLock lock(sMainLoopMonitor); 155 pendingEvents = std::move(*sMainLoopPendingEvents); 156 sMainLoopHasPendingProcess = false; 157 } 158 159 for (auto& event : pendingEvents) { 160 event->Run(); 161 } 162 } 163 164 static void QueueForMainThread(RefPtr<Runnable>&& aRunnable) { 165 StaticMonitorAutoLock lock(sMainLoopMonitor); 166 sMainLoopPendingEvents->AppendElement(std::move(aRunnable)); 167 if (!sMainLoopHasPendingProcess) { 168 sMainLoop->PostTask(NewRunnableFunction( 169 "mozilla::gmp::ProcessPendingGmpEvents", &ProcessPendingGmpEvents)); 170 sMainLoopHasPendingProcess = true; 171 } 172 lock.Notify(); 173 } 174 175 GMPErr RunOnMainThread(GMPTask* aTask) { 176 if (!aTask || !sMainLoop) { 177 return GMPGenericErr; 178 } 179 180 RefPtr<GMPRunnable> r = new GMPRunnable(aTask); 181 QueueForMainThread(std::move(r)); 182 return GMPNoErr; 183 } 184 185 GMPErr SyncRunOnMainThread(GMPTask* aTask) { 186 if (!aTask || !sMainLoop || IsOnChildMainThread()) { 187 return GMPGenericErr; 188 } 189 190 RefPtr<GMPSyncRunnable> r = new GMPSyncRunnable(aTask, sMainLoop); 191 QueueForMainThread(RefPtr{r}); 192 r->WaitUntilDone(); 193 194 return GMPNoErr; 195 } 196 197 class MOZ_CAPABILITY("mutex") GMPMutexImpl final : public GMPMutex { 198 public: 199 GMPMutexImpl(); 200 virtual ~GMPMutexImpl(); 201 202 // GMPMutex 203 void Acquire() override MOZ_CAPABILITY_ACQUIRE(); 204 void Release() override MOZ_CAPABILITY_RELEASE(); 205 void Destroy() override; 206 207 private: 208 ReentrantMonitor mMonitor MOZ_UNANNOTATED; 209 }; 210 211 GMPErr CreateMutex(GMPMutex** aMutex) { 212 if (!aMutex) { 213 return GMPGenericErr; 214 } 215 216 *aMutex = new GMPMutexImpl(); 217 218 return GMPNoErr; 219 } 220 221 GMPErr CreateRecord(const char* aRecordName, uint32_t aRecordNameSize, 222 GMPRecord** aOutRecord, GMPRecordClient* aClient) { 223 if (aRecordNameSize > GMP_MAX_RECORD_NAME_SIZE || aRecordNameSize == 0) { 224 NS_WARNING("GMP tried to CreateRecord with too long or 0 record name"); 225 return GMPGenericErr; 226 } 227 GMPStorageChild* storage = sChild->GetGMPStorage(); 228 if (!storage) { 229 return GMPGenericErr; 230 } 231 MOZ_ASSERT(storage); 232 return storage->CreateRecord(nsDependentCString(aRecordName, aRecordNameSize), 233 aOutRecord, aClient); 234 } 235 236 GMPErr SetTimerOnMainThread(GMPTask* aTask, int64_t aTimeoutMS) { 237 if (!aTask || !sMainLoop || !IsOnChildMainThread()) { 238 return GMPGenericErr; 239 } 240 GMPTimerChild* timers = sChild->GetGMPTimers(); 241 NS_ENSURE_TRUE(timers, GMPGenericErr); 242 return timers->SetTimer(aTask, aTimeoutMS); 243 } 244 245 GMPErr GetClock(GMPTimestamp* aOutTime) { 246 if (!aOutTime) { 247 return GMPGenericErr; 248 } 249 *aOutTime = base::Time::Now().ToDoubleT() * 1000.0; 250 return GMPNoErr; 251 } 252 253 void InitPlatformAPI(GMPPlatformAPI& aPlatformAPI, GMPChild* aChild) { 254 if (!sMainLoop) { 255 sMainLoop = MessageLoop::current(); 256 } 257 if (!sChild) { 258 sChild = aChild; 259 } 260 261 { 262 StaticMonitorAutoLock lock(sMainLoopMonitor); 263 if (!sMainLoopPendingEvents) { 264 sMainLoopPendingEvents = new nsTArray<RefPtr<Runnable>>(); 265 } 266 } 267 268 aPlatformAPI.version = 0; 269 aPlatformAPI.createthread = &CreateThread; 270 aPlatformAPI.runonmainthread = &RunOnMainThread; 271 aPlatformAPI.syncrunonmainthread = &SyncRunOnMainThread; 272 aPlatformAPI.createmutex = &CreateMutex; 273 aPlatformAPI.createrecord = &CreateRecord; 274 aPlatformAPI.settimer = &SetTimerOnMainThread; 275 aPlatformAPI.getcurrenttime = &GetClock; 276 } 277 278 void ShutdownPlatformAPI() { 279 StaticMonitorAutoLock lock(sMainLoopMonitor); 280 if (sMainLoopPendingEvents) { 281 delete sMainLoopPendingEvents; 282 sMainLoopPendingEvents = nullptr; 283 } 284 } 285 286 void SendFOGData(ipc::ByteBuf&& buf) { 287 if (sChild) { 288 sChild->SendFOGData(std::move(buf)); 289 } 290 } 291 292 #ifdef XP_WIN 293 RefPtr<PGMPChild::GetModulesTrustPromise> SendGetModulesTrust( 294 ModulePaths&& aModules, bool aRunAtNormalPriority) { 295 if (!sChild) { 296 return PGMPChild::GetModulesTrustPromise::CreateAndReject( 297 ipc::ResponseRejectReason::SendError, __func__); 298 } 299 return sChild->SendGetModulesTrust(std::move(aModules), aRunAtNormalPriority); 300 } 301 #endif 302 303 GMPThreadImpl::GMPThreadImpl() : mMutex("GMPThreadImpl"), mThread("GMPThread") { 304 MOZ_COUNT_CTOR(GMPThread); 305 } 306 307 GMPThreadImpl::~GMPThreadImpl() { MOZ_COUNT_DTOR(GMPThread); } 308 309 void GMPThreadImpl::Post(GMPTask* aTask) { 310 MutexAutoLock lock(mMutex); 311 312 if (!mThread.IsRunning()) { 313 bool started = mThread.Start(); 314 if (!started) { 315 NS_WARNING("Unable to start GMPThread!"); 316 return; 317 } 318 } 319 320 RefPtr<GMPRunnable> r = new GMPRunnable(aTask); 321 mThread.message_loop()->PostTask( 322 NewRunnableMethod("gmp::GMPRunnable::Run", r.get(), &GMPRunnable::Run)); 323 } 324 325 void GMPThreadImpl::Join() { 326 { 327 MutexAutoLock lock(mMutex); 328 if (mThread.IsRunning()) { 329 mThread.Stop(); 330 } 331 } 332 delete this; 333 } 334 335 GMPMutexImpl::GMPMutexImpl() : mMonitor("gmp-mutex") { 336 MOZ_COUNT_CTOR(GMPMutexImpl); 337 } 338 339 GMPMutexImpl::~GMPMutexImpl() { MOZ_COUNT_DTOR(GMPMutexImpl); } 340 341 void GMPMutexImpl::Destroy() { delete this; } 342 343 MOZ_PUSH_IGNORE_THREAD_SAFETY 344 void GMPMutexImpl::Acquire() { mMonitor.Enter(); } 345 346 void GMPMutexImpl::Release() { mMonitor.Exit(); } 347 MOZ_POP_THREAD_SAFETY 348 349 GMPTask* NewGMPTask(std::function<void()>&& aFunction) { 350 class Task : public GMPTask { 351 public: 352 explicit Task(std::function<void()>&& aFunction) 353 : mFunction(std::move(aFunction)) {} 354 void Destroy() override { delete this; } 355 ~Task() override = default; 356 void Run() override { mFunction(); } 357 358 private: 359 std::function<void()> mFunction; 360 }; 361 return new Task(std::move(aFunction)); 362 } 363 364 } // namespace mozilla::gmp