1/* 2 * Copyright (C) 2009 Google Inc. All rights reserved. 3 * Copyright (C) 2013 Apple Inc. All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are 7 * met: 8 * 9 * * Redistributions of source code must retain the above copyright 10 * notice, this list of conditions and the following disclaimer. 11 * * Redistributions in binary form must reproduce the above 12 * copyright notice, this list of conditions and the following disclaimer 13 * in the documentation and/or other materials provided with the 14 * distribution. 15 * * Neither the name of Google Inc. nor the names of its 16 * contributors may be used to endorse or promote products derived from 17 * this software without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 20 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 21 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 22 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 23 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 24 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 25 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 26 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 27 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 28 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32#include "config.h" 33#include "SQLTransactionCoordinator.h" 34 35#if ENABLE(SQL_DATABASE) 36 37#include "DatabaseBackend.h" 38#include "SQLTransactionBackend.h" 39#include "SecurityOrigin.h" 40#include <wtf/Deque.h> 41#include <wtf/HashMap.h> 42#include <wtf/HashSet.h> 43#include <wtf/RefPtr.h> 44 45namespace WebCore { 46 47static String getDatabaseIdentifier(SQLTransactionBackend* transaction) 48{ 49 DatabaseBackend* database = transaction->database(); 50 ASSERT(database); 51 return database->securityOrigin()->databaseIdentifier(); 52} 53 54SQLTransactionCoordinator::SQLTransactionCoordinator() 55 : m_isShuttingDown(false) 56{ 57} 58 59void SQLTransactionCoordinator::processPendingTransactions(CoordinationInfo& info) 60{ 61 if (info.activeWriteTransaction || info.pendingTransactions.isEmpty()) 62 return; 63 64 RefPtr<SQLTransactionBackend> firstPendingTransaction = info.pendingTransactions.first(); 65 if (firstPendingTransaction->isReadOnly()) { 66 do { 67 firstPendingTransaction = info.pendingTransactions.takeFirst(); 68 info.activeReadTransactions.add(firstPendingTransaction); 69 firstPendingTransaction->lockAcquired(); 70 } while (!info.pendingTransactions.isEmpty() && info.pendingTransactions.first()->isReadOnly()); 71 } else if (info.activeReadTransactions.isEmpty()) { 72 info.pendingTransactions.removeFirst(); 73 info.activeWriteTransaction = firstPendingTransaction; 74 firstPendingTransaction->lockAcquired(); 75 } 76} 77 78void SQLTransactionCoordinator::acquireLock(SQLTransactionBackend* transaction) 79{ 80 ASSERT(!m_isShuttingDown); 81 82 String dbIdentifier = getDatabaseIdentifier(transaction); 83 84 CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier); 85 if (coordinationInfoIterator == m_coordinationInfoMap.end()) { 86 // No pending transactions for this DB 87 coordinationInfoIterator = m_coordinationInfoMap.add(dbIdentifier, CoordinationInfo()).iterator; 88 } 89 90 CoordinationInfo& info = coordinationInfoIterator->value; 91 info.pendingTransactions.append(transaction); 92 processPendingTransactions(info); 93} 94 95void SQLTransactionCoordinator::releaseLock(SQLTransactionBackend* transaction) 96{ 97 if (m_isShuttingDown) 98 return; 99 100 String dbIdentifier = getDatabaseIdentifier(transaction); 101 102 CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.find(dbIdentifier); 103 ASSERT(coordinationInfoIterator != m_coordinationInfoMap.end()); 104 CoordinationInfo& info = coordinationInfoIterator->value; 105 106 if (transaction->isReadOnly()) { 107 ASSERT(info.activeReadTransactions.contains(transaction)); 108 info.activeReadTransactions.remove(transaction); 109 } else { 110 ASSERT(info.activeWriteTransaction == transaction); 111 info.activeWriteTransaction = 0; 112 } 113 114 processPendingTransactions(info); 115} 116 117void SQLTransactionCoordinator::shutdown() 118{ 119 // Prevent releaseLock() from accessing / changing the coordinationInfo 120 // while we're shutting down. 121 m_isShuttingDown = true; 122 123 // Notify all transactions in progress that the database thread is shutting down 124 for (CoordinationInfoMap::iterator coordinationInfoIterator = m_coordinationInfoMap.begin(); 125 coordinationInfoIterator != m_coordinationInfoMap.end(); ++coordinationInfoIterator) { 126 CoordinationInfo& info = coordinationInfoIterator->value; 127 128 // Clean up transactions that have reached "lockAcquired": 129 // Transaction phase 4 cleanup. See comment on "What happens if a 130 // transaction is interrupted?" at the top of SQLTransactionBackend.cpp. 131 if (info.activeWriteTransaction) 132 info.activeWriteTransaction->notifyDatabaseThreadIsShuttingDown(); 133 for (HashSet<RefPtr<SQLTransactionBackend> >::iterator activeReadTransactionsIterator = 134 info.activeReadTransactions.begin(); 135 activeReadTransactionsIterator != info.activeReadTransactions.end(); 136 ++activeReadTransactionsIterator) { 137 (*activeReadTransactionsIterator)->notifyDatabaseThreadIsShuttingDown(); 138 } 139 140 // Clean up transactions that have NOT reached "lockAcquired": 141 // Transaction phase 3 cleanup. See comment on "What happens if a 142 // transaction is interrupted?" at the top of SQLTransactionBackend.cpp. 143 while (!info.pendingTransactions.isEmpty()) { 144 RefPtr<SQLTransactionBackend> transaction = info.pendingTransactions.first(); 145 transaction->notifyDatabaseThreadIsShuttingDown(); 146 } 147 } 148 149 // Clean up all pending transactions for all databases 150 m_coordinationInfoMap.clear(); 151} 152 153} // namespace WebCore 154 155#endif // ENABLE(SQL_DATABASE) 156