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 : /* ***** BEGIN LICENSE BLOCK *****
4 : * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 : *
6 : * The contents of this file are subject to the Mozilla Public License Version
7 : * 1.1 (the "License"); you may not use this file except in compliance with
8 : * the License. You may obtain a copy of the License at
9 : * http://www.mozilla.org/MPL/
10 : *
11 : * Software distributed under the License is distributed on an "AS IS" basis,
12 : * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 : * for the specific language governing rights and limitations under the
14 : * License.
15 : *
16 : * The Original Code is Indexed Database.
17 : *
18 : * The Initial Developer of the Original Code is
19 : * The Mozilla Foundation.
20 : * Portions created by the Initial Developer are Copyright (C) 2010
21 : * the Initial Developer. All Rights Reserved.
22 : *
23 : * Contributor(s):
24 : * Ben Turner <bent.mozilla@gmail.com>
25 : *
26 : * Alternatively, the contents of this file may be used under the terms of
27 : * either the GNU General Public License Version 2 or later (the "GPL"), or
28 : * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 : * in which case the provisions of the GPL or the LGPL are applicable instead
30 : * of those above. If you wish to allow use of your version of this file only
31 : * under the terms of either the GPL or the LGPL, and not to allow others to
32 : * use your version of this file under the terms of the MPL, indicate your
33 : * decision by deleting the provisions above and replace them with the notice
34 : * and other provisions required by the GPL or the LGPL. If you do not delete
35 : * the provisions above, a recipient may use your version of this file under
36 : * the terms of any one of the MPL, the GPL or the LGPL.
37 : *
38 : * ***** END LICENSE BLOCK ***** */
39 :
40 : #include "TransactionThreadPool.h"
41 :
42 : #include "nsIObserverService.h"
43 : #include "nsIThreadPool.h"
44 :
45 : #include "nsComponentManagerUtils.h"
46 : #include "nsThreadUtils.h"
47 : #include "nsServiceManagerUtils.h"
48 : #include "nsXPCOMCIDInternal.h"
49 :
50 : using mozilla::MonitorAutoLock;
51 :
52 : USING_INDEXEDDB_NAMESPACE
53 :
54 : namespace {
55 :
56 : const PRUint32 kThreadLimit = 20;
57 : const PRUint32 kIdleThreadLimit = 5;
58 : const PRUint32 kIdleThreadTimeoutMs = 30000;
59 :
60 : TransactionThreadPool* gInstance = nsnull;
61 : bool gShutdown = false;
62 :
63 : inline
64 : nsresult
65 2967 : CheckOverlapAndMergeObjectStores(nsTArray<nsString>& aLockedStores,
66 : const nsTArray<nsString>& aObjectStores,
67 : bool aShouldMerge,
68 : bool* aStoresOverlap)
69 : {
70 2967 : PRUint32 length = aObjectStores.Length();
71 :
72 2967 : bool overlap = false;
73 :
74 6934 : for (PRUint32 index = 0; index < length; index++) {
75 3967 : const nsString& storeName = aObjectStores[index];
76 3967 : if (aLockedStores.Contains(storeName)) {
77 2017 : overlap = true;
78 : }
79 1950 : else if (aShouldMerge && !aLockedStores.AppendElement(storeName)) {
80 0 : NS_WARNING("Out of memory!");
81 0 : return NS_ERROR_OUT_OF_MEMORY;
82 : }
83 : }
84 :
85 2967 : *aStoresOverlap = overlap;
86 2967 : return NS_OK;
87 : }
88 :
89 : } // anonymous namespace
90 :
91 : BEGIN_INDEXEDDB_NAMESPACE
92 :
93 : class FinishTransactionRunnable : public nsIRunnable
94 540 : {
95 : public:
96 : NS_DECL_ISUPPORTS
97 : NS_DECL_NSIRUNNABLE
98 :
99 : inline FinishTransactionRunnable(IDBTransaction* aTransaction,
100 : nsCOMPtr<nsIRunnable>& aFinishRunnable);
101 :
102 : private:
103 : IDBTransaction* mTransaction;
104 : nsCOMPtr<nsIRunnable> mFinishRunnable;
105 : };
106 :
107 : END_INDEXEDDB_NAMESPACE
108 :
109 50 : TransactionThreadPool::TransactionThreadPool()
110 : {
111 50 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
112 50 : NS_ASSERTION(!gInstance, "More than one instance!");
113 50 : }
114 :
115 100 : TransactionThreadPool::~TransactionThreadPool()
116 : {
117 50 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
118 50 : NS_ASSERTION(gInstance == this, "Different instances!");
119 50 : gInstance = nsnull;
120 50 : }
121 :
122 : // static
123 : TransactionThreadPool*
124 5682 : TransactionThreadPool::GetOrCreate()
125 : {
126 5682 : if (!gInstance && !gShutdown) {
127 50 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
128 100 : nsAutoPtr<TransactionThreadPool> pool(new TransactionThreadPool());
129 :
130 50 : nsresult rv = pool->Init();
131 50 : NS_ENSURE_SUCCESS(rv, nsnull);
132 :
133 100 : gInstance = pool.forget();
134 : }
135 5682 : return gInstance;
136 : }
137 :
138 : // static
139 : TransactionThreadPool*
140 0 : TransactionThreadPool::Get()
141 : {
142 0 : return gInstance;
143 : }
144 :
145 : // static
146 : void
147 54 : TransactionThreadPool::Shutdown()
148 : {
149 54 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
150 :
151 54 : gShutdown = true;
152 :
153 54 : if (gInstance) {
154 50 : if (NS_FAILED(gInstance->Cleanup())) {
155 0 : NS_WARNING("Failed to shutdown thread pool!");
156 : }
157 50 : delete gInstance;
158 50 : gInstance = nsnull;
159 : }
160 54 : }
161 :
162 : nsresult
163 50 : TransactionThreadPool::Init()
164 : {
165 50 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
166 :
167 50 : if (!mTransactionsInProgress.Init()) {
168 0 : NS_WARNING("Failed to init hash!");
169 0 : return NS_ERROR_FAILURE;
170 : }
171 :
172 : nsresult rv;
173 50 : mThreadPool = do_CreateInstance(NS_THREADPOOL_CONTRACTID, &rv);
174 50 : NS_ENSURE_SUCCESS(rv, rv);
175 :
176 50 : rv = mThreadPool->SetThreadLimit(kThreadLimit);
177 50 : NS_ENSURE_SUCCESS(rv, rv);
178 :
179 50 : rv = mThreadPool->SetIdleThreadLimit(kIdleThreadLimit);
180 50 : NS_ENSURE_SUCCESS(rv, rv);
181 :
182 50 : rv = mThreadPool->SetIdleThreadTimeout(kIdleThreadTimeoutMs);
183 50 : NS_ENSURE_SUCCESS(rv, rv);
184 :
185 50 : return NS_OK;
186 : }
187 :
188 : nsresult
189 50 : TransactionThreadPool::Cleanup()
190 : {
191 50 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
192 :
193 50 : nsresult rv = mThreadPool->Shutdown();
194 50 : NS_ENSURE_SUCCESS(rv, rv);
195 :
196 : // Make sure the pool is still accessible while any callbacks generated from
197 : // the other threads are processed.
198 50 : rv = NS_ProcessPendingEvents(nsnull);
199 50 : NS_ENSURE_SUCCESS(rv, rv);
200 :
201 50 : if (!mCompleteCallbacks.IsEmpty()) {
202 : // Run all callbacks manually now.
203 0 : for (PRUint32 index = 0; index < mCompleteCallbacks.Length(); index++) {
204 0 : mCompleteCallbacks[index].mCallback->Run();
205 : }
206 0 : mCompleteCallbacks.Clear();
207 :
208 : // And make sure they get processed.
209 0 : rv = NS_ProcessPendingEvents(nsnull);
210 0 : NS_ENSURE_SUCCESS(rv, rv);
211 : }
212 :
213 50 : return NS_OK;
214 : }
215 :
216 : void
217 540 : TransactionThreadPool::FinishTransaction(IDBTransaction* aTransaction)
218 : {
219 540 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
220 540 : NS_ASSERTION(aTransaction, "Null pointer!");
221 :
222 : // AddRef here because removing from the hash will call Release.
223 1080 : nsRefPtr<IDBTransaction> transaction(aTransaction);
224 :
225 540 : nsIAtom* databaseId = aTransaction->mDatabase->Id();
226 :
227 : DatabaseTransactionInfo* dbTransactionInfo;
228 540 : if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) {
229 0 : NS_ERROR("We don't know anyting about this database?!");
230 : return;
231 : }
232 :
233 : nsTArray<TransactionInfo>& transactionsInProgress =
234 540 : dbTransactionInfo->transactions;
235 :
236 540 : PRUint32 transactionCount = transactionsInProgress.Length();
237 :
238 : #ifdef DEBUG
239 540 : if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) {
240 71 : NS_ASSERTION(transactionCount == 1,
241 : "More transactions running than should be!");
242 : }
243 : #endif
244 :
245 540 : if (transactionCount == 1) {
246 : #ifdef DEBUG
247 : {
248 404 : TransactionInfo& info = transactionsInProgress[0];
249 404 : NS_ASSERTION(info.transaction == aTransaction, "Transaction mismatch!");
250 : }
251 : #endif
252 404 : mTransactionsInProgress.Remove(databaseId);
253 :
254 : // See if we need to fire any complete callbacks.
255 404 : for (PRUint32 index = 0; index < mCompleteCallbacks.Length(); index++) {
256 0 : MaybeFireCallback(index);
257 : }
258 : }
259 : else {
260 : // We need to rebuild the locked object store list.
261 272 : nsTArray<nsString> storesWriting, storesReading;
262 :
263 701 : for (PRUint32 index = 0, count = transactionCount; index < count; index++) {
264 565 : IDBTransaction* transaction = transactionsInProgress[index].transaction;
265 565 : if (transaction == aTransaction) {
266 136 : NS_ASSERTION(count == transactionCount, "More than one match?!");
267 :
268 136 : transactionsInProgress.RemoveElementAt(index);
269 136 : index--;
270 136 : count--;
271 :
272 136 : continue;
273 : }
274 :
275 429 : const nsTArray<nsString>& objectStores = transaction->mObjectStoreNames;
276 :
277 : bool dummy;
278 429 : if (transaction->mMode == IDBTransaction::READ_WRITE) {
279 3 : if (NS_FAILED(CheckOverlapAndMergeObjectStores(storesWriting,
280 : objectStores,
281 : true, &dummy))) {
282 0 : NS_WARNING("Out of memory!");
283 : }
284 : }
285 426 : else if (transaction->mMode == IDBTransaction::READ_ONLY) {
286 426 : if (NS_FAILED(CheckOverlapAndMergeObjectStores(storesReading,
287 : objectStores,
288 : true, &dummy))) {
289 0 : NS_WARNING("Out of memory!");
290 : }
291 : }
292 : else {
293 0 : NS_NOTREACHED("Unknown mode!");
294 : }
295 : }
296 :
297 136 : NS_ASSERTION(transactionsInProgress.Length() == transactionCount - 1,
298 : "Didn't find the transaction we were looking for!");
299 :
300 136 : dbTransactionInfo->storesWriting.SwapElements(storesWriting);
301 136 : dbTransactionInfo->storesReading.SwapElements(storesReading);
302 : }
303 :
304 : // Try to dispatch all the queued transactions again.
305 1080 : nsTArray<QueuedDispatchInfo> queuedDispatch;
306 540 : queuedDispatch.SwapElements(mDelayedDispatchQueue);
307 :
308 540 : transactionCount = queuedDispatch.Length();
309 1673 : for (PRUint32 index = 0; index < transactionCount; index++) {
310 1133 : if (NS_FAILED(Dispatch(queuedDispatch[index]))) {
311 0 : NS_WARNING("Dispatch failed!");
312 : }
313 : }
314 : }
315 :
316 : nsresult
317 6812 : TransactionThreadPool::TransactionCanRun(IDBTransaction* aTransaction,
318 : bool* aCanRun,
319 : TransactionQueue** aExistingQueue)
320 : {
321 6812 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
322 6812 : NS_ASSERTION(aTransaction, "Null pointer!");
323 6812 : NS_ASSERTION(aCanRun, "Null pointer!");
324 6812 : NS_ASSERTION(aExistingQueue, "Null pointer!");
325 :
326 6812 : nsIAtom* databaseId = aTransaction->mDatabase->Id();
327 6812 : const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames;
328 6812 : const PRUint16 mode = aTransaction->mMode;
329 :
330 : // See if we can run this transaction now.
331 : DatabaseTransactionInfo* dbTransactionInfo;
332 6812 : if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) {
333 : // First transaction for this database, fine to run.
334 404 : *aCanRun = true;
335 404 : *aExistingQueue = nsnull;
336 404 : return NS_OK;
337 : }
338 :
339 : nsTArray<TransactionInfo>& transactionsInProgress =
340 6408 : dbTransactionInfo->transactions;
341 :
342 6408 : PRUint32 transactionCount = transactionsInProgress.Length();
343 6408 : NS_ASSERTION(transactionCount, "Should never be 0!");
344 :
345 8711 : for (PRUint32 index = 0; index < transactionCount; index++) {
346 : // See if this transaction is in out list of current transactions.
347 7442 : const TransactionInfo& info = transactionsInProgress[index];
348 7442 : if (info.transaction == aTransaction) {
349 5139 : *aCanRun = true;
350 5139 : *aExistingQueue = info.queue;
351 5139 : return NS_OK;
352 : }
353 : }
354 :
355 1269 : NS_ASSERTION(mode != IDBTransaction::VERSION_CHANGE, "How did we get here?");
356 :
357 : bool writeOverlap;
358 : nsresult rv =
359 : CheckOverlapAndMergeObjectStores(dbTransactionInfo->storesWriting,
360 : objectStoreNames,
361 : mode == IDBTransaction::READ_WRITE,
362 1269 : &writeOverlap);
363 1269 : NS_ENSURE_SUCCESS(rv, rv);
364 :
365 : bool readOverlap;
366 : rv = CheckOverlapAndMergeObjectStores(dbTransactionInfo->storesReading,
367 : objectStoreNames,
368 : mode == IDBTransaction::READ_ONLY,
369 1269 : &readOverlap);
370 1269 : NS_ENSURE_SUCCESS(rv, rv);
371 :
372 1269 : if (writeOverlap ||
373 : (readOverlap && mode == IDBTransaction::READ_WRITE)) {
374 1133 : *aCanRun = false;
375 1133 : *aExistingQueue = nsnull;
376 1133 : return NS_OK;
377 : }
378 :
379 136 : *aCanRun = true;
380 136 : *aExistingQueue = nsnull;
381 136 : return NS_OK;
382 : }
383 :
384 : nsresult
385 6812 : TransactionThreadPool::Dispatch(IDBTransaction* aTransaction,
386 : nsIRunnable* aRunnable,
387 : bool aFinish,
388 : nsIRunnable* aFinishRunnable)
389 : {
390 6812 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
391 6812 : NS_ASSERTION(aTransaction, "Null pointer!");
392 6812 : NS_ASSERTION(aRunnable, "Null pointer!");
393 :
394 6812 : if (aTransaction->mDatabase->IsInvalidated() && !aFinish) {
395 0 : return NS_ERROR_NOT_AVAILABLE;
396 : }
397 :
398 : bool canRun;
399 : TransactionQueue* existingQueue;
400 6812 : nsresult rv = TransactionCanRun(aTransaction, &canRun, &existingQueue);
401 6812 : NS_ENSURE_SUCCESS(rv, rv);
402 :
403 6812 : if (!canRun) {
404 1133 : QueuedDispatchInfo* info = mDelayedDispatchQueue.AppendElement();
405 1133 : NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
406 :
407 1133 : info->transaction = aTransaction;
408 1133 : info->runnable = aRunnable;
409 1133 : info->finish = aFinish;
410 1133 : info->finishRunnable = aFinishRunnable;
411 :
412 1133 : return NS_OK;
413 : }
414 :
415 5679 : if (existingQueue) {
416 5139 : existingQueue->Dispatch(aRunnable);
417 5139 : if (aFinish) {
418 540 : existingQueue->Finish(aFinishRunnable);
419 : }
420 5139 : return NS_OK;
421 : }
422 :
423 540 : nsIAtom* databaseId = aTransaction->mDatabase->Id();
424 :
425 : #ifdef DEBUG
426 540 : if (aTransaction->mMode == IDBTransaction::VERSION_CHANGE) {
427 71 : NS_ASSERTION(!mTransactionsInProgress.Get(databaseId, nsnull),
428 : "Shouldn't have anything in progress!");
429 : }
430 : #endif
431 :
432 : DatabaseTransactionInfo* dbTransactionInfo;
433 1080 : nsAutoPtr<DatabaseTransactionInfo> autoDBTransactionInfo;
434 :
435 540 : if (!mTransactionsInProgress.Get(databaseId, &dbTransactionInfo)) {
436 : // Make a new struct for this transaction.
437 404 : autoDBTransactionInfo = new DatabaseTransactionInfo();
438 404 : dbTransactionInfo = autoDBTransactionInfo;
439 : }
440 :
441 540 : const nsTArray<nsString>& objectStoreNames = aTransaction->mObjectStoreNames;
442 :
443 : nsTArray<nsString>& storesInUse =
444 : aTransaction->mMode == IDBTransaction::READ_WRITE ?
445 : dbTransactionInfo->storesWriting :
446 540 : dbTransactionInfo->storesReading;
447 :
448 540 : if (!storesInUse.AppendElements(objectStoreNames)) {
449 0 : NS_WARNING("Out of memory!");
450 0 : return NS_ERROR_OUT_OF_MEMORY;
451 : }
452 :
453 : nsTArray<TransactionInfo>& transactionInfoArray =
454 540 : dbTransactionInfo->transactions;
455 :
456 540 : TransactionInfo* transactionInfo = transactionInfoArray.AppendElement();
457 540 : NS_ENSURE_TRUE(transactionInfo, NS_ERROR_OUT_OF_MEMORY);
458 :
459 540 : transactionInfo->transaction = aTransaction;
460 540 : transactionInfo->queue = new TransactionQueue(aTransaction, aRunnable);
461 540 : if (aFinish) {
462 0 : transactionInfo->queue->Finish(aFinishRunnable);
463 : }
464 :
465 540 : if (!transactionInfo->objectStoreNames.AppendElements(objectStoreNames)) {
466 0 : NS_WARNING("Out of memory!");
467 0 : return NS_ERROR_OUT_OF_MEMORY;
468 : }
469 :
470 540 : if (autoDBTransactionInfo) {
471 404 : if (!mTransactionsInProgress.Put(databaseId, autoDBTransactionInfo)) {
472 0 : NS_WARNING("Failed to put!");
473 0 : return NS_ERROR_OUT_OF_MEMORY;
474 : }
475 404 : autoDBTransactionInfo.forget();
476 : }
477 :
478 540 : return mThreadPool->Dispatch(transactionInfo->queue, NS_DISPATCH_NORMAL);
479 : }
480 :
481 : bool
482 3 : TransactionThreadPool::WaitForAllDatabasesToComplete(
483 : nsTArray<nsRefPtr<IDBDatabase> >& aDatabases,
484 : nsIRunnable* aCallback)
485 : {
486 3 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
487 3 : NS_ASSERTION(!aDatabases.IsEmpty(), "No databases to wait on!");
488 3 : NS_ASSERTION(aCallback, "Null pointer!");
489 :
490 3 : DatabasesCompleteCallback* callback = mCompleteCallbacks.AppendElement();
491 3 : if (!callback) {
492 0 : NS_WARNING("Out of memory!");
493 0 : return false;
494 : }
495 :
496 3 : callback->mCallback = aCallback;
497 3 : if (!callback->mDatabases.SwapElements(aDatabases)) {
498 0 : NS_ERROR("This should never fail!");
499 : }
500 :
501 3 : MaybeFireCallback(mCompleteCallbacks.Length() - 1);
502 3 : return true;
503 : }
504 :
505 : void
506 0 : TransactionThreadPool::AbortTransactionsForDatabase(IDBDatabase* aDatabase)
507 : {
508 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
509 0 : NS_ASSERTION(aDatabase, "Null pointer!");
510 :
511 : // Get list of transactions for this database id
512 : DatabaseTransactionInfo* dbTransactionInfo;
513 0 : if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) {
514 : // If there are no running transactions, there can't be any pending ones
515 0 : return;
516 : }
517 :
518 0 : nsAutoTArray<nsRefPtr<IDBTransaction>, 50> transactions;
519 :
520 : // Collect any running transactions
521 : nsTArray<TransactionInfo>& transactionsInProgress =
522 0 : dbTransactionInfo->transactions;
523 :
524 0 : PRUint32 transactionCount = transactionsInProgress.Length();
525 0 : NS_ASSERTION(transactionCount, "Should never be 0!");
526 :
527 0 : for (PRUint32 index = 0; index < transactionCount; index++) {
528 : // See if any transaction belongs to this IDBDatabase instance
529 0 : IDBTransaction* transaction = transactionsInProgress[index].transaction;
530 0 : if (transaction->Database() == aDatabase) {
531 0 : transactions.AppendElement(transaction);
532 : }
533 : }
534 :
535 : // Collect any pending transactions.
536 0 : for (PRUint32 index = 0; index < mDelayedDispatchQueue.Length(); index++) {
537 : // See if any transaction belongs to this IDBDatabase instance
538 0 : IDBTransaction* transaction = mDelayedDispatchQueue[index].transaction;
539 0 : if (transaction->Database() == aDatabase) {
540 0 : transactions.AppendElement(transaction);
541 : }
542 : }
543 :
544 : // Abort transactions. Do this after collecting the transactions in case
545 : // calling Abort() modifies the data structures we're iterating above.
546 0 : for (PRUint32 index = 0; index < transactions.Length(); index++) {
547 : // This can fail, for example if the transaction is in the process of
548 : // being comitted. That is expected and fine, so we ignore any returned
549 : // errors.
550 0 : transactions[index]->Abort();
551 : }
552 : }
553 :
554 : bool
555 0 : TransactionThreadPool::HasTransactionsForDatabase(IDBDatabase* aDatabase)
556 : {
557 0 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
558 0 : NS_ASSERTION(aDatabase, "Null pointer!");
559 :
560 : // Get list of transactions for this database id
561 : DatabaseTransactionInfo* dbTransactionInfo;
562 0 : if (!mTransactionsInProgress.Get(aDatabase->Id(), &dbTransactionInfo)) {
563 0 : return false;
564 : }
565 :
566 : nsTArray<TransactionInfo>& transactionsInProgress =
567 0 : dbTransactionInfo->transactions;
568 :
569 0 : PRUint32 transactionCount = transactionsInProgress.Length();
570 0 : NS_ASSERTION(transactionCount, "Should never be 0!");
571 :
572 0 : for (PRUint32 index = 0; index < transactionCount; index++) {
573 : // See if any transaction belongs to this IDBDatabase instance
574 0 : if (transactionsInProgress[index].transaction->Database() == aDatabase) {
575 0 : return true;
576 : }
577 : }
578 :
579 0 : return false;
580 : }
581 :
582 : void
583 3 : TransactionThreadPool::MaybeFireCallback(PRUint32 aCallbackIndex)
584 : {
585 3 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
586 :
587 3 : DatabasesCompleteCallback& callback = mCompleteCallbacks[aCallbackIndex];
588 :
589 3 : bool freeToRun = true;
590 6 : for (PRUint32 index = 0; index < callback.mDatabases.Length(); index++) {
591 3 : if (mTransactionsInProgress.Get(callback.mDatabases[index]->Id(), nsnull)) {
592 0 : freeToRun = false;
593 0 : break;
594 : }
595 : }
596 :
597 3 : if (freeToRun) {
598 3 : callback.mCallback->Run();
599 3 : mCompleteCallbacks.RemoveElementAt(aCallbackIndex);
600 : }
601 3 : }
602 :
603 540 : TransactionThreadPool::
604 : TransactionQueue::TransactionQueue(IDBTransaction* aTransaction,
605 : nsIRunnable* aRunnable)
606 : : mMonitor("TransactionQueue::mMonitor"),
607 : mTransaction(aTransaction),
608 540 : mShouldFinish(false)
609 : {
610 540 : NS_ASSERTION(aTransaction, "Null pointer!");
611 540 : NS_ASSERTION(aRunnable, "Null pointer!");
612 540 : mQueue.AppendElement(aRunnable);
613 540 : }
614 :
615 : void
616 5139 : TransactionThreadPool::TransactionQueue::Dispatch(nsIRunnable* aRunnable)
617 : {
618 10278 : MonitorAutoLock lock(mMonitor);
619 :
620 5139 : NS_ASSERTION(!mShouldFinish, "Dispatch called after Finish!");
621 :
622 5139 : mQueue.AppendElement(aRunnable);
623 :
624 5139 : mMonitor.Notify();
625 5139 : }
626 :
627 : void
628 540 : TransactionThreadPool::TransactionQueue::Finish(nsIRunnable* aFinishRunnable)
629 : {
630 1080 : MonitorAutoLock lock(mMonitor);
631 :
632 540 : NS_ASSERTION(!mShouldFinish, "Finish called more than once!");
633 :
634 540 : mShouldFinish = true;
635 540 : mFinishRunnable = aFinishRunnable;
636 :
637 540 : mMonitor.Notify();
638 540 : }
639 :
640 4320 : NS_IMPL_THREADSAFE_ISUPPORTS1(TransactionThreadPool::TransactionQueue,
641 : nsIRunnable)
642 :
643 : NS_IMETHODIMP
644 540 : TransactionThreadPool::TransactionQueue::Run()
645 : {
646 1080 : nsAutoTArray<nsCOMPtr<nsIRunnable>, 10> queue;
647 1080 : nsCOMPtr<nsIRunnable> finishRunnable;
648 540 : bool shouldFinish = false;
649 :
650 5320 : while(!shouldFinish) {
651 4240 : NS_ASSERTION(queue.IsEmpty(), "Should have cleared this!");
652 :
653 : {
654 8480 : MonitorAutoLock lock(mMonitor);
655 11838 : while (!mShouldFinish && mQueue.IsEmpty()) {
656 3358 : if (NS_FAILED(mMonitor.Wait())) {
657 0 : NS_ERROR("Failed to wait!");
658 : }
659 : }
660 :
661 4240 : mQueue.SwapElements(queue);
662 4240 : if (mShouldFinish) {
663 540 : mFinishRunnable.swap(finishRunnable);
664 540 : shouldFinish = true;
665 : }
666 : }
667 :
668 4240 : PRUint32 count = queue.Length();
669 9919 : for (PRUint32 index = 0; index < count; index++) {
670 5679 : nsCOMPtr<nsIRunnable>& runnable = queue[index];
671 5679 : runnable->Run();
672 5679 : runnable = nsnull;
673 : }
674 :
675 4240 : if (count) {
676 4225 : queue.Clear();
677 : }
678 : }
679 :
680 : nsCOMPtr<nsIRunnable> finishTransactionRunnable =
681 1080 : new FinishTransactionRunnable(mTransaction, finishRunnable);
682 540 : if (NS_FAILED(NS_DispatchToMainThread(finishTransactionRunnable,
683 : NS_DISPATCH_NORMAL))) {
684 0 : NS_WARNING("Failed to dispatch finishTransactionRunnable!");
685 : }
686 :
687 540 : return NS_OK;
688 : }
689 :
690 540 : FinishTransactionRunnable::FinishTransactionRunnable(
691 : IDBTransaction* aTransaction,
692 : nsCOMPtr<nsIRunnable>& aFinishRunnable)
693 540 : : mTransaction(aTransaction)
694 : {
695 540 : NS_ASSERTION(!NS_IsMainThread(), "Wrong thread!");
696 540 : NS_ASSERTION(aTransaction, "Null pointer!");
697 540 : mFinishRunnable.swap(aFinishRunnable);
698 540 : }
699 :
700 5940 : NS_IMPL_THREADSAFE_ISUPPORTS1(FinishTransactionRunnable, nsIRunnable)
701 :
702 : NS_IMETHODIMP
703 540 : FinishTransactionRunnable::Run()
704 : {
705 540 : NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
706 540 : if (!gInstance) {
707 0 : NS_ERROR("Running after shutdown!");
708 0 : return NS_ERROR_FAILURE;
709 : }
710 :
711 540 : gInstance->FinishTransaction(mTransaction);
712 :
713 540 : if (mFinishRunnable) {
714 540 : mFinishRunnable->Run();
715 540 : mFinishRunnable = nsnull;
716 : }
717 :
718 540 : return NS_OK;
719 : }
|