|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ |
|
2 /* vim: set ts=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 "TransactionThreadPool.h" |
|
8 |
|
9 #include "nsIObserverService.h" |
|
10 #include "nsIThreadPool.h" |
|
11 |
|
12 #include "nsComponentManagerUtils.h" |
|
13 #include "nsThreadUtils.h" |
|
14 #include "nsServiceManagerUtils.h" |
|
15 #include "nsXPCOMCIDInternal.h" |
|
16 |
|
17 #include "ProfilerHelpers.h" |
|
18 |
|
19 using mozilla::MonitorAutoLock; |
|
20 |
|
21 USING_INDEXEDDB_NAMESPACE |
|
22 |
|
23 namespace { |
|
24 |
|
25 const uint32_t kThreadLimit = 20; |
|
26 const uint32_t kIdleThreadLimit = 5; |
|
27 const uint32_t kIdleThreadTimeoutMs = 30000; |
|
28 |
|
29 TransactionThreadPool* gThreadPool = nullptr; |
|
30 bool gShutdown = false; |
|
31 |
|
32 #ifdef MOZ_ENABLE_PROFILER_SPS |
|
33 |
|
34 class TransactionThreadPoolListener : public nsIThreadPoolListener |
|
35 { |
|
36 public: |
|
37 NS_DECL_THREADSAFE_ISUPPORTS |
|
38 NS_DECL_NSITHREADPOOLLISTENER |
|
39 |
|
40 private: |
|
41 virtual ~TransactionThreadPoolListener() |
|
42 { } |
|
43 }; |
|
44 |
|
45 #endif // MOZ_ENABLE_PROFILER_SPS |
|
46 |
|
47 } // anonymous namespace |
|
48 |
|
49 BEGIN_INDEXEDDB_NAMESPACE |
|
50 |
|
51 class FinishTransactionRunnable MOZ_FINAL : public nsIRunnable |
|
52 { |
|
53 public: |
|
54 NS_DECL_THREADSAFE_ISUPPORTS |
|
55 NS_DECL_NSIRUNNABLE |
|
56 |
|
57 inline FinishTransactionRunnable(IDBTransaction* aTransaction, |
|
58 nsCOMPtr<nsIRunnable>& aFinishRunnable); |
|
59 |
|
60 private: |
|
61 IDBTransaction* mTransaction; |
|
62 nsCOMPtr<nsIRunnable> mFinishRunnable; |
|
63 }; |
|
64 |
|
65 END_INDEXEDDB_NAMESPACE |
|
66 |
|
67 TransactionThreadPool::TransactionThreadPool() |
|
68 { |
|
69 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
70 NS_ASSERTION(!gThreadPool, "More than one instance!"); |
|
71 } |
|
72 |
|
73 TransactionThreadPool::~TransactionThreadPool() |
|
74 { |
|
75 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
76 NS_ASSERTION(gThreadPool == this, "Different instances!"); |
|
77 gThreadPool = nullptr; |
|
78 } |
|
79 |
|
80 // static |
|
81 TransactionThreadPool* |
|
82 TransactionThreadPool::GetOrCreate() |
|
83 { |
|
84 if (!gThreadPool && !gShutdown) { |
|
85 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
86 nsAutoPtr<TransactionThreadPool> pool(new TransactionThreadPool()); |
|
87 |
|
88 nsresult rv = pool->Init(); |
|
89 NS_ENSURE_SUCCESS(rv, nullptr); |
|
90 |
|
91 gThreadPool = pool.forget(); |
|
92 } |
|
93 return gThreadPool; |
|
94 } |
|
95 |
|
96 // static |
|
97 TransactionThreadPool* |
|
98 TransactionThreadPool::Get() |
|
99 { |
|
100 return gThreadPool; |
|
101 } |
|
102 |
|
103 // static |
|
104 void |
|
105 TransactionThreadPool::Shutdown() |
|
106 { |
|
107 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
108 |
|
109 gShutdown = true; |
|
110 |
|
111 if (gThreadPool) { |
|
112 if (NS_FAILED(gThreadPool->Cleanup())) { |
|
113 NS_WARNING("Failed to shutdown thread pool!"); |
|
114 } |
|
115 delete gThreadPool; |
|
116 gThreadPool = nullptr; |
|
117 } |
|
118 } |
|
119 |
|
120 nsresult |
|
121 TransactionThreadPool::Init() |
|
122 { |
|
123 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
124 |
|
125 nsresult rv; |
|
126 mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv); |
|
127 NS_ENSURE_SUCCESS(rv, rv); |
|
128 |
|
129 rv = mThreadPool->SetName(NS_LITERAL_CSTRING("IndexedDB Trans")); |
|
130 NS_ENSURE_SUCCESS(rv, rv); |
|
131 |
|
132 rv = mThreadPool->SetThreadLimit(kThreadLimit); |
|
133 NS_ENSURE_SUCCESS(rv, rv); |
|
134 |
|
135 rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit); |
|
136 NS_ENSURE_SUCCESS(rv, rv); |
|
137 |
|
138 rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs); |
|
139 NS_ENSURE_SUCCESS(rv, rv); |
|
140 |
|
141 #ifdef MOZ_ENABLE_PROFILER_SPS |
|
142 nsCOMPtr<nsIThreadPoolListener> listener = |
|
143 new TransactionThreadPoolListener(); |
|
144 |
|
145 rv = mThreadPool->SetListener(listener); |
|
146 NS_ENSURE_SUCCESS(rv, rv); |
|
147 #endif |
|
148 |
|
149 return NS_OK; |
|
150 } |
|
151 |
|
152 nsresult |
|
153 TransactionThreadPool::Cleanup() |
|
154 { |
|
155 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
156 |
|
157 PROFILER_MAIN_THREAD_LABEL("IndexedDB", "TransactionThreadPool::Cleanup"); |
|
158 |
|
159 nsresult rv = mThreadPool->Shutdown(); |
|
160 NS_ENSURE_SUCCESS(rv, rv); |
|
161 |
|
162 // Make sure the pool is still accessible while any callbacks generated from |
|
163 // the other threads are processed. |
|
164 rv = NS_ProcessPendingEvents(nullptr); |
|
165 NS_ENSURE_SUCCESS(rv, rv); |
|
166 |
|
167 if (!mCompleteCallbacks.IsEmpty()) { |
|
168 // Run all callbacks manually now. |
|
169 for (uint32_t index = 0; index < mCompleteCallbacks.Length(); index++) { |
|
170 mCompleteCallbacks[index].mCallback->Run(); |
|
171 } |
|
172 mCompleteCallbacks.Clear(); |
|
173 |
|
174 // And make sure they get processed. |
|
175 rv = NS_ProcessPendingEvents(nullptr); |
|
176 NS_ENSURE_SUCCESS(rv, rv); |
|
177 } |
|
178 |
|
179 return NS_OK; |
|
180 } |
|
181 |
|
182 // static |
|
183 PLDHashOperator |
|
184 TransactionThreadPool::MaybeUnblockTransaction(nsPtrHashKey<TransactionInfo>* aKey, |
|
185 void* aUserArg) |
|
186 { |
|
187 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
188 |
|
189 TransactionInfo* maybeUnblockedInfo = aKey->GetKey(); |
|
190 TransactionInfo* finishedInfo = static_cast<TransactionInfo*>(aUserArg); |
|
191 |
|
192 NS_ASSERTION(maybeUnblockedInfo->blockedOn.Contains(finishedInfo), |
|
193 "Huh?"); |
|
194 maybeUnblockedInfo->blockedOn.RemoveEntry(finishedInfo); |
|
195 if (!maybeUnblockedInfo->blockedOn.Count()) { |
|
196 // Let this transaction run. |
|
197 maybeUnblockedInfo->queue->Unblock(); |
|
198 } |
|
199 |
|
200 return PL_DHASH_NEXT; |
|
201 } |
|
202 |
|
203 void |
|
204 TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction) |
|
205 { |
|
206 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
207 NS_ASSERTION(aTransaction, "Null pointer!"); |
|
208 |
|
209 PROFILER_MAIN_THREAD_LABEL("IndexedDB", |
|
210 "TransactionThreadPool::FinishTransaction"); |
|
211 |
|
212 // AddRef here because removing from the hash will call Release. |
|
213 nsRefPtr<IDBTransaction> transaction(aTransaction); |
|
214 |
|
215 const nsACString& databaseId = aTransaction->mDatabase->Id(); |
|
216 |
|
217 DatabaseTransactionInfo* dbTransactionInfo; |
|
218 if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) { |
|
219 NS_ERROR("We don't know anyting about this database?!"); |
|
220 return; |
|
221 } |
|
222 |
|
223 DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = |
|
224 dbTransactionInfo->transactions; |
|
225 |
|
226 uint32_t transactionCount = transactionsInProgress.Count(); |
|
227 |
|
228 #ifdef DEBUG |
|
229 if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) { |
|
230 NS_ASSERTION(transactionCount == 1, |
|
231 "More transactions running than should be!"); |
|
232 } |
|
233 #endif |
|
234 |
|
235 if (transactionCount == 1) { |
|
236 #ifdef DEBUG |
|
237 { |
|
238 const TransactionInfo* info = transactionsInProgress.Get(aTransaction); |
|
239 NS_ASSERTION(info->transaction == aTransaction, "Transaction mismatch!"); |
|
240 } |
|
241 #endif |
|
242 mTransactionsInProgress.Remove(databaseId); |
|
243 |
|
244 // See if we need to fire any complete callbacks. |
|
245 uint32_t index = 0; |
|
246 while (index < mCompleteCallbacks.Length()) { |
|
247 if (MaybeFireCallback(mCompleteCallbacks[index])) { |
|
248 mCompleteCallbacks.RemoveElementAt(index); |
|
249 } |
|
250 else { |
|
251 index++; |
|
252 } |
|
253 } |
|
254 |
|
255 return; |
|
256 } |
|
257 TransactionInfo* info = transactionsInProgress.Get(aTransaction); |
|
258 NS_ASSERTION(info, "We've never heard of this transaction?!?"); |
|
259 |
|
260 const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames; |
|
261 for (uint32_t index = 0, count = objectStoreNames.Length(); index < count; |
|
262 index++) { |
|
263 TransactionInfoPair* blockInfo = |
|
264 dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]); |
|
265 NS_ASSERTION(blockInfo, "Huh?"); |
|
266 |
|
267 if (aTransaction->mMode == IDBTransaction::READ_WRITE && |
|
268 blockInfo->lastBlockingReads == info) { |
|
269 blockInfo->lastBlockingReads = nullptr; |
|
270 } |
|
271 |
|
272 uint32_t i = blockInfo->lastBlockingWrites.IndexOf(info); |
|
273 if (i != blockInfo->lastBlockingWrites.NoIndex) { |
|
274 blockInfo->lastBlockingWrites.RemoveElementAt(i); |
|
275 } |
|
276 } |
|
277 |
|
278 info->blocking.EnumerateEntries(MaybeUnblockTransaction, info); |
|
279 |
|
280 transactionsInProgress.Remove(aTransaction); |
|
281 } |
|
282 |
|
283 TransactionThreadPool::TransactionQueue& |
|
284 TransactionThreadPool::GetQueueForTransaction(IDBTransaction* aTransaction) |
|
285 { |
|
286 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
287 NS_ASSERTION(aTransaction, "Null pointer!"); |
|
288 |
|
289 const nsACString& databaseId = aTransaction->mDatabase->Id(); |
|
290 |
|
291 const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames; |
|
292 const uint16_t mode = aTransaction->mMode; |
|
293 |
|
294 // See if we can run this transaction now. |
|
295 DatabaseTransactionInfo* dbTransactionInfo; |
|
296 if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) { |
|
297 // First transaction for this database. |
|
298 dbTransactionInfo = new DatabaseTransactionInfo(); |
|
299 mTransactionsInProgress.Put(databaseId, dbTransactionInfo); |
|
300 } |
|
301 |
|
302 DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = |
|
303 dbTransactionInfo->transactions; |
|
304 TransactionInfo* info = transactionsInProgress.Get(aTransaction); |
|
305 if (info) { |
|
306 // We recognize this one. |
|
307 return *info->queue; |
|
308 } |
|
309 |
|
310 TransactionInfo* transactionInfo = new TransactionInfo(aTransaction); |
|
311 |
|
312 dbTransactionInfo->transactions.Put(aTransaction, transactionInfo);; |
|
313 |
|
314 for (uint32_t index = 0, count = objectStoreNames.Length(); index < count; |
|
315 index++) { |
|
316 TransactionInfoPair* blockInfo = |
|
317 dbTransactionInfo->blockingTransactions.Get(objectStoreNames[index]); |
|
318 if (!blockInfo) { |
|
319 blockInfo = new TransactionInfoPair(); |
|
320 blockInfo->lastBlockingReads = nullptr; |
|
321 dbTransactionInfo->blockingTransactions.Put(objectStoreNames[index], |
|
322 blockInfo); |
|
323 } |
|
324 |
|
325 // Mark what we are blocking on. |
|
326 if (blockInfo->lastBlockingReads) { |
|
327 TransactionInfo* blockingInfo = blockInfo->lastBlockingReads; |
|
328 transactionInfo->blockedOn.PutEntry(blockingInfo); |
|
329 blockingInfo->blocking.PutEntry(transactionInfo); |
|
330 } |
|
331 |
|
332 if (mode == IDBTransaction::READ_WRITE && |
|
333 blockInfo->lastBlockingWrites.Length()) { |
|
334 for (uint32_t index = 0, |
|
335 count = blockInfo->lastBlockingWrites.Length(); index < count; |
|
336 index++) { |
|
337 TransactionInfo* blockingInfo = blockInfo->lastBlockingWrites[index]; |
|
338 transactionInfo->blockedOn.PutEntry(blockingInfo); |
|
339 blockingInfo->blocking.PutEntry(transactionInfo); |
|
340 } |
|
341 } |
|
342 |
|
343 if (mode == IDBTransaction::READ_WRITE) { |
|
344 blockInfo->lastBlockingReads = transactionInfo; |
|
345 blockInfo->lastBlockingWrites.Clear(); |
|
346 } |
|
347 else { |
|
348 blockInfo->lastBlockingWrites.AppendElement(transactionInfo); |
|
349 } |
|
350 } |
|
351 |
|
352 if (!transactionInfo->blockedOn.Count()) { |
|
353 transactionInfo->queue->Unblock(); |
|
354 } |
|
355 |
|
356 return *transactionInfo->queue; |
|
357 } |
|
358 |
|
359 nsresult |
|
360 TransactionThreadPool::Dispatch(IDBTransaction* aTransaction, |
|
361 nsIRunnable* aRunnable, |
|
362 bool aFinish, |
|
363 nsIRunnable* aFinishRunnable) |
|
364 { |
|
365 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
366 NS_ASSERTION(aTransaction, "Null pointer!"); |
|
367 NS_ASSERTION(aRunnable, "Null pointer!"); |
|
368 |
|
369 if (aTransaction->mDatabase->IsInvalidated() && !aFinish) { |
|
370 return NS_ERROR_NOT_AVAILABLE; |
|
371 } |
|
372 |
|
373 TransactionQueue& queue = GetQueueForTransaction(aTransaction); |
|
374 |
|
375 queue.Dispatch(aRunnable); |
|
376 if (aFinish) { |
|
377 queue.Finish(aFinishRunnable); |
|
378 } |
|
379 return NS_OK; |
|
380 } |
|
381 |
|
382 void |
|
383 TransactionThreadPool::WaitForDatabasesToComplete( |
|
384 nsTArray<nsRefPtr<IDBDatabase> >& aDatabases, |
|
385 nsIRunnable* aCallback) |
|
386 { |
|
387 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
388 NS_ASSERTION(!aDatabases.IsEmpty(), "No databases to wait on!"); |
|
389 NS_ASSERTION(aCallback, "Null pointer!"); |
|
390 |
|
391 DatabasesCompleteCallback* callback = mCompleteCallbacks.AppendElement(); |
|
392 |
|
393 callback->mCallback = aCallback; |
|
394 callback->mDatabases.SwapElements(aDatabases); |
|
395 |
|
396 if (MaybeFireCallback(*callback)) { |
|
397 mCompleteCallbacks.RemoveElementAt(mCompleteCallbacks.Length() - 1); |
|
398 } |
|
399 } |
|
400 |
|
401 // static |
|
402 PLDHashOperator |
|
403 TransactionThreadPool::CollectTransactions(IDBTransaction* aKey, |
|
404 TransactionInfo* aValue, |
|
405 void* aUserArg) |
|
406 { |
|
407 nsAutoTArray<nsRefPtr<IDBTransaction>, 50>* transactionArray = |
|
408 static_cast<nsAutoTArray<nsRefPtr<IDBTransaction>, 50>*>(aUserArg); |
|
409 transactionArray->AppendElement(aKey); |
|
410 |
|
411 return PL_DHASH_NEXT; |
|
412 } |
|
413 |
|
414 void |
|
415 TransactionThreadPool::AbortTransactionsForDatabase(IDBDatabase* aDatabase) |
|
416 { |
|
417 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
418 NS_ASSERTION(aDatabase, "Null pointer!"); |
|
419 |
|
420 PROFILER_MAIN_THREAD_LABEL("IndexedDB", |
|
421 "TransactionThreadPool::" |
|
422 "AbortTransactionsForDatabase"); |
|
423 |
|
424 // Get list of transactions for this database id |
|
425 DatabaseTransactionInfo* dbTransactionInfo; |
|
426 if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) { |
|
427 // If there are no transactions, we're done. |
|
428 return; |
|
429 } |
|
430 |
|
431 // Collect any running transactions |
|
432 DatabaseTransactionInfo::TransactionHashtable& transactionsInProgress = |
|
433 dbTransactionInfo->transactions; |
|
434 |
|
435 NS_ASSERTION(transactionsInProgress.Count(), "Should never be 0!"); |
|
436 |
|
437 nsAutoTArray<nsRefPtr<IDBTransaction>, 50> transactions; |
|
438 transactionsInProgress.EnumerateRead(CollectTransactions, &transactions); |
|
439 |
|
440 // Abort transactions. Do this after collecting the transactions in case |
|
441 // calling Abort() modifies the data structures we're iterating above. |
|
442 for (uint32_t index = 0; index < transactions.Length(); index++) { |
|
443 if (transactions[index]->Database() != aDatabase) { |
|
444 continue; |
|
445 } |
|
446 |
|
447 // This can fail, for example if the transaction is in the process of |
|
448 // being comitted. That is expected and fine, so we ignore any returned |
|
449 // errors. |
|
450 ErrorResult rv; |
|
451 transactions[index]->Abort(rv); |
|
452 } |
|
453 } |
|
454 |
|
455 struct MOZ_STACK_CLASS TransactionSearchInfo |
|
456 { |
|
457 TransactionSearchInfo(nsIOfflineStorage* aDatabase) |
|
458 : db(aDatabase), found(false) |
|
459 { |
|
460 } |
|
461 |
|
462 nsIOfflineStorage* db; |
|
463 bool found; |
|
464 }; |
|
465 |
|
466 // static |
|
467 PLDHashOperator |
|
468 TransactionThreadPool::FindTransaction(IDBTransaction* aKey, |
|
469 TransactionInfo* aValue, |
|
470 void* aUserArg) |
|
471 { |
|
472 TransactionSearchInfo* info = static_cast<TransactionSearchInfo*>(aUserArg); |
|
473 |
|
474 if (aKey->Database() == info->db) { |
|
475 info->found = true; |
|
476 return PL_DHASH_STOP; |
|
477 } |
|
478 |
|
479 return PL_DHASH_NEXT; |
|
480 } |
|
481 bool |
|
482 TransactionThreadPool::HasTransactionsForDatabase(IDBDatabase* aDatabase) |
|
483 { |
|
484 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
485 NS_ASSERTION(aDatabase, "Null pointer!"); |
|
486 |
|
487 DatabaseTransactionInfo* dbTransactionInfo = nullptr; |
|
488 dbTransactionInfo = mTransactionsInProgress.Get(aDatabase->Id()); |
|
489 if (!dbTransactionInfo) { |
|
490 return false; |
|
491 } |
|
492 |
|
493 TransactionSearchInfo info(aDatabase); |
|
494 dbTransactionInfo->transactions.EnumerateRead(FindTransaction, &info); |
|
495 |
|
496 return info.found; |
|
497 } |
|
498 |
|
499 bool |
|
500 TransactionThreadPool::MaybeFireCallback(DatabasesCompleteCallback aCallback) |
|
501 { |
|
502 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
503 |
|
504 PROFILER_MAIN_THREAD_LABEL("IndexedDB", |
|
505 "TransactionThreadPool::MaybeFireCallback"); |
|
506 |
|
507 for (uint32_t index = 0; index < aCallback.mDatabases.Length(); index++) { |
|
508 IDBDatabase* database = aCallback.mDatabases[index]; |
|
509 if (!database) { |
|
510 MOZ_CRASH(); |
|
511 } |
|
512 |
|
513 if (mTransactionsInProgress.Get(database->Id(), nullptr)) { |
|
514 return false; |
|
515 } |
|
516 } |
|
517 |
|
518 aCallback.mCallback->Run(); |
|
519 return true; |
|
520 } |
|
521 |
|
522 TransactionThreadPool:: |
|
523 TransactionQueue::TransactionQueue(IDBTransaction* aTransaction) |
|
524 : mMonitor("TransactionQueue::mMonitor"), |
|
525 mTransaction(aTransaction), |
|
526 mShouldFinish(false) |
|
527 { |
|
528 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
529 NS_ASSERTION(aTransaction, "Null pointer!"); |
|
530 } |
|
531 |
|
532 void |
|
533 TransactionThreadPool::TransactionQueue::Unblock() |
|
534 { |
|
535 MonitorAutoLock lock(mMonitor); |
|
536 |
|
537 // NB: Finish may be called before Unblock. |
|
538 |
|
539 TransactionThreadPool::Get()->mThreadPool-> |
|
540 Dispatch(this, NS_DISPATCH_NORMAL); |
|
541 } |
|
542 |
|
543 void |
|
544 TransactionThreadPool::TransactionQueue::Dispatch(nsIRunnable* aRunnable) |
|
545 { |
|
546 MonitorAutoLock lock(mMonitor); |
|
547 |
|
548 NS_ASSERTION(!mShouldFinish, "Dispatch called after Finish!"); |
|
549 |
|
550 mQueue.AppendElement(aRunnable); |
|
551 |
|
552 mMonitor.Notify(); |
|
553 } |
|
554 |
|
555 void |
|
556 TransactionThreadPool::TransactionQueue::Finish(nsIRunnable* aFinishRunnable) |
|
557 { |
|
558 MonitorAutoLock lock(mMonitor); |
|
559 |
|
560 NS_ASSERTION(!mShouldFinish, "Finish called more than once!"); |
|
561 |
|
562 mShouldFinish = true; |
|
563 mFinishRunnable = aFinishRunnable; |
|
564 |
|
565 mMonitor.Notify(); |
|
566 } |
|
567 |
|
568 NS_IMPL_ISUPPORTS(TransactionThreadPool::TransactionQueue, nsIRunnable) |
|
569 |
|
570 NS_IMETHODIMP |
|
571 TransactionThreadPool::TransactionQueue::Run() |
|
572 { |
|
573 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); |
|
574 NS_ASSERTION(IndexedDatabaseManager::IsMainProcess(), "Wrong process!"); |
|
575 |
|
576 PROFILER_LABEL("IndexedDB", "TransactionQueue::Run"); |
|
577 |
|
578 IDB_PROFILER_MARK("IndexedDB Transaction %llu: Beginning database work", |
|
579 "IDBTransaction[%llu] DT Start", |
|
580 mTransaction->GetSerialNumber()); |
|
581 |
|
582 nsAutoTArray<nsCOMPtr<nsIRunnable>, 10> queue; |
|
583 nsCOMPtr<nsIRunnable> finishRunnable; |
|
584 bool shouldFinish = false; |
|
585 |
|
586 do { |
|
587 NS_ASSERTION(queue.IsEmpty(), "Should have cleared this!"); |
|
588 |
|
589 { |
|
590 MonitorAutoLock lock(mMonitor); |
|
591 while (!mShouldFinish && mQueue.IsEmpty()) { |
|
592 if (NS_FAILED(mMonitor.Wait())) { |
|
593 NS_ERROR("Failed to wait!"); |
|
594 } |
|
595 } |
|
596 |
|
597 mQueue.SwapElements(queue); |
|
598 if (mShouldFinish) { |
|
599 mFinishRunnable.swap(finishRunnable); |
|
600 shouldFinish = true; |
|
601 } |
|
602 } |
|
603 |
|
604 uint32_t count = queue.Length(); |
|
605 for (uint32_t index = 0; index < count; index++) { |
|
606 nsCOMPtr<nsIRunnable>& runnable = queue[index]; |
|
607 runnable->Run(); |
|
608 runnable = nullptr; |
|
609 } |
|
610 |
|
611 if (count) { |
|
612 queue.Clear(); |
|
613 } |
|
614 } while (!shouldFinish); |
|
615 |
|
616 IDB_PROFILER_MARK("IndexedDB Transaction %llu: Finished database work", |
|
617 "IDBTransaction[%llu] DT Done", |
|
618 mTransaction->GetSerialNumber()); |
|
619 |
|
620 nsCOMPtr<nsIRunnable> finishTransactionRunnable = |
|
621 new FinishTransactionRunnable(mTransaction, finishRunnable); |
|
622 if (NS_FAILED(NS_DispatchToMainThread(finishTransactionRunnable, |
|
623 NS_DISPATCH_NORMAL))) { |
|
624 NS_WARNING("Failed to dispatch finishTransactionRunnable!"); |
|
625 } |
|
626 |
|
627 return NS_OK; |
|
628 } |
|
629 |
|
630 FinishTransactionRunnable::FinishTransactionRunnable( |
|
631 IDBTransaction* aTransaction, |
|
632 nsCOMPtr<nsIRunnable>& aFinishRunnable) |
|
633 : mTransaction(aTransaction) |
|
634 { |
|
635 NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!"); |
|
636 NS_ASSERTION(aTransaction, "Null pointer!"); |
|
637 mFinishRunnable.swap(aFinishRunnable); |
|
638 } |
|
639 |
|
640 NS_IMPL_ISUPPORTS(FinishTransactionRunnable, nsIRunnable) |
|
641 |
|
642 NS_IMETHODIMP |
|
643 FinishTransactionRunnable::Run() |
|
644 { |
|
645 NS_ASSERTION(NS_IsMainThread(), "Wrong thread!"); |
|
646 |
|
647 PROFILER_MAIN_THREAD_LABEL("IndexedDB", "FinishTransactionRunnable::Run"); |
|
648 |
|
649 if (!gThreadPool) { |
|
650 NS_ERROR("Running after shutdown!"); |
|
651 return NS_ERROR_FAILURE; |
|
652 } |
|
653 |
|
654 gThreadPool->FinishTransaction(mTransaction); |
|
655 |
|
656 if (mFinishRunnable) { |
|
657 mFinishRunnable->Run(); |
|
658 mFinishRunnable = nullptr; |
|
659 } |
|
660 |
|
661 return NS_OK; |
|
662 } |
|
663 |
|
664 #ifdef MOZ_ENABLE_PROFILER_SPS |
|
665 |
|
666 NS_IMPL_ISUPPORTS(TransactionThreadPoolListener, nsIThreadPoolListener) |
|
667 |
|
668 NS_IMETHODIMP |
|
669 TransactionThreadPoolListener::OnThreadCreated() |
|
670 { |
|
671 MOZ_ASSERT(!NS_IsMainThread()); |
|
672 char aLocal; |
|
673 profiler_register_thread("IndexedDB Transaction", &aLocal); |
|
674 return NS_OK; |
|
675 } |
|
676 |
|
677 NS_IMETHODIMP |
|
678 TransactionThreadPoolListener::OnThreadShuttingDown() |
|
679 { |
|
680 MOZ_ASSERT(!NS_IsMainThread()); |
|
681 profiler_unregister_thread(); |
|
682 return NS_OK; |
|
683 } |
|
684 |
|
685 #endif // MOZ_ENABLE_PROFILER_SPS |