1/*
2 * Copyright (C) 2010 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1.  Redistributions of source code must retain the above copyright
9 *     notice, this list of conditions and the following disclaimer.
10 * 2.  Redistributions in binary form must reproduce the above copyright
11 *     notice, this list of conditions and the following disclaimer in the
12 *     documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
23 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "IDBTransactionCoordinator.h"
28
29#if ENABLE(INDEXED_DATABASE)
30
31#include "IDBDatabaseBackendImpl.h"
32#include "IDBTransactionBackendImpl.h"
33
34namespace WebCore {
35
36PassOwnPtr<IDBTransactionCoordinator> IDBTransactionCoordinator::create()
37{
38    return adoptPtr(new IDBTransactionCoordinator());
39}
40
41IDBTransactionCoordinator::IDBTransactionCoordinator()
42{
43}
44
45IDBTransactionCoordinator::~IDBTransactionCoordinator()
46{
47}
48
49void IDBTransactionCoordinator::didCreateTransaction(IDBTransactionBackendImpl* transaction)
50{
51    ASSERT(!m_transactions.contains(transaction));
52    m_transactions.add(transaction, transaction);
53}
54
55void IDBTransactionCoordinator::didStartTransaction(IDBTransactionBackendImpl* transaction)
56{
57    ASSERT(m_transactions.contains(transaction));
58
59    m_queuedTransactions.add(transaction);
60    processStartedTransactions();
61}
62
63void IDBTransactionCoordinator::didFinishTransaction(IDBTransactionBackendImpl* transaction)
64{
65    ASSERT(m_transactions.contains(transaction));
66
67    if (m_queuedTransactions.contains(transaction)) {
68        ASSERT(!m_startedTransactions.contains(transaction));
69        m_queuedTransactions.remove(transaction);
70    } else if (m_startedTransactions.contains(transaction))
71        m_startedTransactions.remove(transaction);
72
73    m_transactions.remove(transaction);
74
75    processStartedTransactions();
76}
77
78#ifndef NDEBUG
79// Verifies internal consistiency while returning whether anything is found.
80bool IDBTransactionCoordinator::isActive(IDBTransactionBackendImpl* transaction)
81{
82    bool found = false;
83    if (m_queuedTransactions.contains(transaction))
84        found = true;
85    if (m_startedTransactions.contains(transaction)) {
86        ASSERT(!found);
87        found = true;
88    }
89    ASSERT(found == m_transactions.contains(transaction));
90    return found;
91}
92#endif
93
94void IDBTransactionCoordinator::processStartedTransactions()
95{
96    if (m_queuedTransactions.isEmpty())
97        return;
98
99    ASSERT(m_startedTransactions.isEmpty() || (*m_startedTransactions.begin())->mode() != IndexedDB::TransactionVersionChange);
100
101    ListHashSet<IDBTransactionBackendImpl*>::const_iterator it = m_queuedTransactions.begin();
102    while (it != m_queuedTransactions.end()) {
103        IDBTransactionBackendImpl* transaction = *it;
104        ++it;
105        if (canRunTransaction(transaction)) {
106            m_queuedTransactions.remove(transaction);
107            m_startedTransactions.add(transaction);
108            transaction->run();
109        }
110    }
111}
112
113static bool doScopesOverlap(const HashSet<int64_t>& scope1, const HashSet<int64_t>& scope2)
114{
115    for (HashSet<int64_t>::const_iterator it = scope1.begin(); it != scope1.end(); ++it) {
116        if (scope2.contains(*it))
117            return true;
118    }
119    return false;
120}
121
122bool IDBTransactionCoordinator::canRunTransaction(IDBTransactionBackendImpl* transaction)
123{
124    ASSERT(m_queuedTransactions.contains(transaction));
125    switch (transaction->mode()) {
126    case IndexedDB::TransactionVersionChange:
127        ASSERT(m_queuedTransactions.size() == 1);
128        ASSERT(m_startedTransactions.isEmpty());
129        return true;
130
131    case IndexedDB::TransactionReadOnly:
132        return true;
133
134    case IndexedDB::TransactionReadWrite:
135        for (HashSet<IDBTransactionBackendImpl*>::const_iterator it = m_startedTransactions.begin(); it != m_startedTransactions.end(); ++it) {
136            if ((*it)->mode() == IndexedDB::TransactionReadWrite && doScopesOverlap(transaction->scope(), (*it)->scope()))
137                return false;
138        }
139        for (ListHashSet<IDBTransactionBackendImpl*>::const_iterator it = m_queuedTransactions.begin(); *it != transaction; ++it) {
140            ASSERT(it != m_queuedTransactions.end());
141            if ((*it)->mode() == IndexedDB::TransactionReadWrite && doScopesOverlap(transaction->scope(), (*it)->scope()))
142                return false;
143        }
144        return true;
145    }
146    ASSERT_NOT_REACHED();
147    return false;
148}
149
150};
151
152#endif // ENABLE(INDEXED_DATABASE)
153