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