https://bugs.webkit.org/show_bug.cgi?id=110600.
Reviewed by Geoffrey Garen.
Source/WebCore:
1. Introduced the OriginLock for synchronizing write access to the
database origin directory. This allows us to more accurately
compute the disk usage.
The OriginLock uses a mutex to provide mutual exclusion between
threads and a file lock for mutual exclusion between processes.
The file lock part is conditional on USE(FILE_LOCK).
The mutex mutual exclusion also serves to ensure that only 1 thread
can write to a sqlite database at one time.
2. Change the SQLTransactionCoordinator to only allow one write
transaction to an origin instead of one write transaction per
database. This is needed in order to accurately compute the
disk usage. It is also necessary so that the OriginLock does not
deadlock itself (as would be the case if concurrent write transactions
to different databases in the same origin are allowed).
3. Fix DatabaseTracker::getMaxSizeForDatabase() to check for when
disk usage may exceed the quota, and ensure that we will return
an appropriate max database size.
Disk usage can exceed the usage if it is already near the quota limit
but have not exceeded it yet. If a new database is opened in that
origin, it may bump the usage above the quota, but should not
continually repeat this. Subsequent attempts to open a database
will find that the quota is already exhausted and fail.
There is still a race condition pertaining to the tracker database
getting out of sync that may still enable runaway growth in the
database sizes. That issue only manifest in a multi-process
environment, and will be fixed in another changeset.
4. Fixed a bug in SQLStatement to check if the errorCallback exists
before invoking it.
No new layout tests. A quota-test.html was attached to bugzilla for manual
testing of multi-tab concurrent consumption of storage resource, and also
to test handling situations when the user deletes the database files while
the script is still using the database.
* CMakeLists.txt:
* GNUmakefile.list.am:
* Modules/webdatabase/DatabaseTracker.cpp:
(WebCore::DatabaseTracker::getMaxSizeForDatabase):
(WebCore::DatabaseTracker::originLockFor):
(WebCore::DatabaseTracker::deleteOriginLockFor):
(WebCore::DatabaseTracker::deleteOrigin):
* Modules/webdatabase/DatabaseTracker.h:
* Modules/webdatabase/OriginLock.cpp: Added.
(WebCore::OriginLock::lockFileNameForPath):
(WebCore::OriginLock::OriginLock):
(WebCore::OriginLock::~OriginLock):
(WebCore::OriginLock::lock):
(WebCore::OriginLock::unlock):
* Modules/webdatabase/OriginLock.h: Added.
* Modules/webdatabase/SQLStatement.cpp:
(WebCore::SQLStatement::performCallback):
* Modules/webdatabase/SQLTransactionBackend.cpp:
(WebCore::SQLTransactionBackend::doCleanup):
(WebCore::SQLTransactionBackend::computeNextStateAndCleanupIfNeeded):
(WebCore::SQLTransactionBackend::openTransactionAndPreflight):
(WebCore::SQLTransactionBackend::postflightAndCommit):
(WebCore::SQLTransactionBackend::cleanupAfterTransactionErrorCallback):
(WebCore::SQLTransactionBackend::acquireOriginLock):
(WebCore::SQLTransactionBackend::releaseOriginLockIfNeeded):
* Modules/webdatabase/SQLTransactionBackend.h:
(SQLTransactionBackend):
* Modules/webdatabase/SQLTransactionCoordinator.cpp:
(WebCore::getDatabaseIdentifier):
* Target.pri:
* WebCore.gypi:
* WebCore.vcproj/WebCore.vcproj:
* WebCore.vcxproj/WebCore.vcxproj:
* WebCore.vcxproj/WebCore.vcxproj.filters:
* WebCore.xcodeproj/project.pbxproj:
* config.h:
* platform/FileSystem.h:
* platform/posix/FileSystemPOSIX.cpp:
(WebCore::lockFile):
(WebCore::unlockFile):
LayoutTests:
* storage/websql/multiple-databases-garbage-collection.js:
- This test runs 2 transactions on 2 databases (1 each). The 2 databases
are named "persistent" and "forgotten". The test executes the
transaction on "persistent" first, but expects the transaction on
"forgotten" to finish first. This is because "forgotten"'s transaction
is a smaller one. The new changes to SQLTransactionCoordinator now
ensures that a write transaction must completes before another is
started for databases in the same origin. Hence, the previously expected
result will no longer be true.
Regardless, the purpose of the test is not to test the order of
completion but that resources are reclaimed. So, I'm changing the test
to start the "forgotten" transaction first followed by the "persistent"
transaction. This ensures that the test will yield consistent results
even when run on ports that may allow more than one write transaction
to run at the same time.
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@144760
268f45cc-cd09-0410-ab3c-
d52691b4dbfc
+2013-03-05 Mark Lam <mark.lam@apple.com>
+
+ Improve robustness of WebSQL quota management.
+ https://bugs.webkit.org/show_bug.cgi?id=110600.
+
+ Reviewed by Geoffrey Garen.
+
+ * storage/websql/multiple-databases-garbage-collection.js:
+ - This test runs 2 transactions on 2 databases (1 each). The 2 databases
+ are named "persistent" and "forgotten". The test executes the
+ transaction on "persistent" first, but expects the transaction on
+ "forgotten" to finish first. This is because "forgotten"'s transaction
+ is a smaller one. The new changes to SQLTransactionCoordinator now
+ ensures that a write transaction must completes before another is
+ started for databases in the same origin. Hence, the previously expected
+ result will no longer be true.
+
+ Regardless, the purpose of the test is not to test the order of
+ completion but that resources are reclaimed. So, I'm changing the test
+ to start the "forgotten" transaction first followed by the "persistent"
+ transaction. This ensures that the test will yield consistent results
+ even when run on ports that may allow more than one write transaction
+ to run at the same time.
+
2013-03-05 Andrey Kosyakov <caseq@chromium.org>
Web Inspector: remove length parameter from Parse HTML timeline event
persistentDB = openDatabaseWithSuffix("MultipleDatabasesTest1", "1.0", "Test one out of a set of databases being destroyed (1)", 32768);
forgottenDB = openDatabaseWithSuffix("MultipleDatabasesTest2", "1.0", "Test one out of a set of databases being destroyed (2)", 32768);
- persistentDB.transaction(function(tx) {
- tx.executeSql("CREATE TABLE IF NOT EXISTS DataTest (randomData)", [], function(tx, result) {
- for (var i = 0; i < 25; ++i)
- tx.executeSql("INSERT INTO DataTest (randomData) VALUES (1)", []);
- });
- }, function(err) {
- log("Persistent Database Transaction Errored - " + err);
- checkCompletion();
- }, function() {
- log("Persistent Database Transaction Complete");
- checkCompletion();
- });
-
forgottenDB.transaction(function(tx) {
tx.executeSql("CREATE TABLE IF NOT EXISTS EmptyTable (unimportantData)", []);
}, function(err) {
GC();
checkCompletion();
});
+
+ persistentDB.transaction(function(tx) {
+ tx.executeSql("CREATE TABLE IF NOT EXISTS DataTest (randomData)", [], function(tx, result) {
+ for (var i = 0; i < 25; ++i)
+ tx.executeSql("INSERT INTO DataTest (randomData) VALUES (1)", []);
+ });
+ }, function(err) {
+ log("Persistent Database Transaction Errored - " + err);
+ checkCompletion();
+ }, function() {
+ log("Persistent Database Transaction Complete");
+ checkCompletion();
+ });
}
Modules/webdatabase/DatabaseTask.cpp
Modules/webdatabase/DatabaseThread.cpp
Modules/webdatabase/DatabaseTracker.cpp
+ Modules/webdatabase/OriginLock.cpp
Modules/webdatabase/SQLException.cpp
Modules/webdatabase/SQLResultSet.cpp
Modules/webdatabase/SQLResultSetRowList.cpp
+2013-03-05 Mark Lam <mark.lam@apple.com>
+
+ Improve robustness of WebSQL quota management.
+ https://bugs.webkit.org/show_bug.cgi?id=110600.
+
+ Reviewed by Geoffrey Garen.
+
+ 1. Introduced the OriginLock for synchronizing write access to the
+ database origin directory. This allows us to more accurately
+ compute the disk usage.
+
+ The OriginLock uses a mutex to provide mutual exclusion between
+ threads and a file lock for mutual exclusion between processes.
+ The file lock part is conditional on USE(FILE_LOCK).
+
+ The mutex mutual exclusion also serves to ensure that only 1 thread
+ can write to a sqlite database at one time.
+
+ 2. Change the SQLTransactionCoordinator to only allow one write
+ transaction to an origin instead of one write transaction per
+ database. This is needed in order to accurately compute the
+ disk usage. It is also necessary so that the OriginLock does not
+ deadlock itself (as would be the case if concurrent write transactions
+ to different databases in the same origin are allowed).
+
+ 3. Fix DatabaseTracker::getMaxSizeForDatabase() to check for when
+ disk usage may exceed the quota, and ensure that we will return
+ an appropriate max database size.
+
+ Disk usage can exceed the usage if it is already near the quota limit
+ but have not exceeded it yet. If a new database is opened in that
+ origin, it may bump the usage above the quota, but should not
+ continually repeat this. Subsequent attempts to open a database
+ will find that the quota is already exhausted and fail.
+
+ There is still a race condition pertaining to the tracker database
+ getting out of sync that may still enable runaway growth in the
+ database sizes. That issue only manifest in a multi-process
+ environment, and will be fixed in another changeset.
+
+ 4. Fixed a bug in SQLStatement to check if the errorCallback exists
+ before invoking it.
+
+ No new layout tests. A quota-test.html was attached to bugzilla for manual
+ testing of multi-tab concurrent consumption of storage resource, and also
+ to test handling situations when the user deletes the database files while
+ the script is still using the database.
+
+ * CMakeLists.txt:
+ * GNUmakefile.list.am:
+ * Modules/webdatabase/DatabaseTracker.cpp:
+ (WebCore::DatabaseTracker::getMaxSizeForDatabase):
+ (WebCore::DatabaseTracker::originLockFor):
+ (WebCore::DatabaseTracker::deleteOriginLockFor):
+ (WebCore::DatabaseTracker::deleteOrigin):
+ * Modules/webdatabase/DatabaseTracker.h:
+ * Modules/webdatabase/OriginLock.cpp: Added.
+ (WebCore::OriginLock::lockFileNameForPath):
+ (WebCore::OriginLock::OriginLock):
+ (WebCore::OriginLock::~OriginLock):
+ (WebCore::OriginLock::lock):
+ (WebCore::OriginLock::unlock):
+ * Modules/webdatabase/OriginLock.h: Added.
+ * Modules/webdatabase/SQLStatement.cpp:
+ (WebCore::SQLStatement::performCallback):
+ * Modules/webdatabase/SQLTransactionBackend.cpp:
+ (WebCore::SQLTransactionBackend::doCleanup):
+ (WebCore::SQLTransactionBackend::computeNextStateAndCleanupIfNeeded):
+ (WebCore::SQLTransactionBackend::openTransactionAndPreflight):
+ (WebCore::SQLTransactionBackend::postflightAndCommit):
+ (WebCore::SQLTransactionBackend::cleanupAfterTransactionErrorCallback):
+ (WebCore::SQLTransactionBackend::acquireOriginLock):
+ (WebCore::SQLTransactionBackend::releaseOriginLockIfNeeded):
+ * Modules/webdatabase/SQLTransactionBackend.h:
+ (SQLTransactionBackend):
+ * Modules/webdatabase/SQLTransactionCoordinator.cpp:
+ (WebCore::getDatabaseIdentifier):
+ * Target.pri:
+ * WebCore.gypi:
+ * WebCore.vcproj/WebCore.vcproj:
+ * WebCore.vcxproj/WebCore.vcxproj:
+ * WebCore.vcxproj/WebCore.vcxproj.filters:
+ * WebCore.xcodeproj/project.pbxproj:
+ * config.h:
+ * platform/FileSystem.h:
+ * platform/posix/FileSystemPOSIX.cpp:
+ (WebCore::lockFile):
+ (WebCore::unlockFile):
+
2013-03-05 Ilya Tikhonovsky <loislo@chromium.org>
Web Inspector: move PopoverContentHelper from TimelinePresentationModel.js to Popover.js.
Source/WebCore/Modules/webdatabase/DatabaseThread.h \
Source/WebCore/Modules/webdatabase/DatabaseTracker.cpp \
Source/WebCore/Modules/webdatabase/DatabaseTracker.h \
+ Source/WebCore/Modules/webdatabase/OriginLock.cpp \
+ Source/WebCore/Modules/webdatabase/OriginLock.h \
Source/WebCore/Modules/webdatabase/SQLCallbackWrapper.h \
Source/WebCore/Modules/webdatabase/SQLError.h \
Source/WebCore/Modules/webdatabase/SQLException.cpp \
#include "DatabaseThread.h"
#include "FileSystem.h"
#include "Logging.h"
+#include "OriginLock.h"
#include "Page.h"
#include "SecurityOrigin.h"
#include "SecurityOriginHash.h"
unsigned long long databaseFileSize = SQLiteFileSystem::getDatabaseFileSize(database->fileName());
ASSERT(databaseFileSize <= diskUsage);
+ if (diskUsage > quota)
+ return databaseFileSize;
+
// A previous error may have allowed the origin to exceed its quota, or may
// have allowed this database to exceed our cached estimate of the origin
// disk usage. Don't multiply that error through integer underflow, or the
databases->add(*it);
}
+PassRefPtr<OriginLock> DatabaseTracker::originLockFor(SecurityOrigin* origin)
+{
+ MutexLocker lockDatabase(m_databaseGuard);
+ String databaseIdentifier = origin->databaseIdentifier();
+
+ // The originLockMap is accessed from multiple DatabaseThreads since
+ // different script contexts can be writing to different databases from
+ // the same origin. Hence, the databaseIdentifier key needs to be an
+ // isolated copy. An isolated copy gives us a value whose refCounting is
+ // thread-safe, since our copy is guarded by the m_databaseGuard mutex.
+ databaseIdentifier = databaseIdentifier.isolatedCopy();
+
+ OriginLockMap::AddResult addResult =
+ m_originLockMap.add(databaseIdentifier, RefPtr<OriginLock>());
+ if (!addResult.isNewEntry)
+ return addResult.iterator->value;
+
+ String path = originPath(origin);
+ RefPtr<OriginLock> lock = adoptRef(new OriginLock(path));
+ ASSERT(lock);
+ addResult.iterator->value = lock;
+
+ return lock.release();
+}
+
+void DatabaseTracker::deleteOriginLockFor(SecurityOrigin* origin)
+{
+ ASSERT(!m_databaseGuard.tryLock());
+
+ // There is not always an instance of an OriginLock associated with an origin.
+ // For example, if the OriginLock lock file was created by a previous run of
+ // the browser which has now terminated, and the current browser process
+ // has not executed any database transactions from this origin that would
+ // have created the OriginLock instance in memory. In this case, we will have
+ // a lock file but not an OriginLock instance in memory.
+
+ // This function is only called if we are already deleting all the database
+ // files in this origin. We'll give the OriginLock one chance to do an
+ // orderly clean up first when we remove its ref from the m_originLockMap.
+ // This may or may not be possible depending on whether other threads are
+ // also using the OriginLock at the same time. After that, we will go ahead
+ // and delete the lock file.
+
+ m_originLockMap.remove(origin->databaseIdentifier());
+ OriginLock::deleteLockFile(originPath(origin));
+}
+
unsigned long long DatabaseTracker::usageForOrigin(SecurityOrigin* origin)
{
String originPath = this->originPath(origin);
{
MutexLocker lockDatabase(m_databaseGuard);
+ deleteOriginLockFor(origin);
doneDeletingOrigin(origin);
SQLiteStatement statement(m_database, "DELETE FROM Databases WHERE origin=?");
class DatabaseBackendBase;
class DatabaseBackendContext;
+class OriginLock;
class SecurityOrigin;
#if !PLATFORM(CHROMIUM)
unsigned long long usageForOrigin(SecurityOrigin*);
unsigned long long quotaForOrigin(SecurityOrigin*);
void setQuota(SecurityOrigin*, unsigned long long);
+ PassRefPtr<OriginLock> originLockFor(SecurityOrigin*);
void deleteAllDatabases();
bool deleteOrigin(SecurityOrigin*);
bool deleteDatabaseFile(SecurityOrigin*, const String& name);
+ void deleteOriginLockFor(SecurityOrigin*);
+
typedef HashSet<DatabaseBackendBase*> DatabaseSet;
typedef HashMap<String, DatabaseSet*> DatabaseNameMap;
typedef HashMap<RefPtr<SecurityOrigin>, DatabaseNameMap*> DatabaseOriginMap;
typedef HashMap<RefPtr<SecurityOrigin>, unsigned long long> QuotaMap;
mutable OwnPtr<QuotaMap> m_quotaMap;
+ typedef HashMap<String, RefPtr<OriginLock> > OriginLockMap;
+ OriginLockMap m_originLockMap;
+
String m_databaseDirectoryPath;
DatabaseManagerClient* m_client;
--- /dev/null
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+#include "OriginLock.h"
+
+#if ENABLE(SQL_DATABASE)
+
+#include "FileSystem.h"
+#include <wtf/PassOwnPtr.h>
+
+namespace WebCore {
+
+String OriginLock::lockFileNameForPath(String originPath)
+{
+ return pathByAppendingComponent(originPath, String(".lock"));
+}
+
+OriginLock::OriginLock(String originPath)
+ : m_lockFileName(lockFileNameForPath(originPath).isolatedCopy())
+#if USE(FILE_LOCK)
+ , m_lockHandle(invalidPlatformFileHandle)
+#endif
+{
+}
+
+OriginLock::~OriginLock()
+{
+}
+
+void OriginLock::lock()
+{
+ m_mutex.lock();
+
+#if USE(FILE_LOCK)
+ m_lockHandle = openFile(m_lockFileName, OpenForWrite);
+ if (m_lockHandle == invalidPlatformFileHandle) {
+ // The only way we can get here is if the directory containing the lock
+ // has been deleted or we were given a path to a non-existant directory.
+ // In that case, there's nothing we can do but cleanup and return.
+ m_mutex.unlock();
+ return;
+ }
+
+ lockFile(m_lockHandle, LockExclusive);
+#endif
+}
+
+void OriginLock::unlock()
+{
+#if USE(FILE_LOCK)
+ // If the file descriptor was uninitialized, then that means the directory
+ // containing the lock has been deleted before we opened the lock file, or
+ // we were given a path to a non-existant directory. Which, in turn, means
+ // that there's nothing to unlock.
+ if (m_lockHandle == invalidPlatformFileHandle)
+ return;
+
+ unlockFile(m_lockHandle);
+
+ closeFile(m_lockHandle);
+ m_lockHandle = invalidPlatformFileHandle;
+#endif
+
+ m_mutex.unlock();
+}
+
+void OriginLock::deleteLockFile(String originPath)
+{
+ UNUSED_PARAM(originPath);
+#if USE(FILE_LOCK)
+ String lockFileName = OriginLock::lockFileNameForPath(originPath);
+ deleteFile(lockFileName);
+#endif
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(SQL_DATABASE)
--- /dev/null
+/*
+ * Copyright (C) 2013 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef OriginLock_h
+#define OriginLock_h
+
+#if ENABLE(SQL_DATABASE)
+
+#include "FileSystem.h"
+#include <wtf/ThreadSafeRefCounted.h>
+#include <wtf/text/WTFString.h>
+
+namespace WebCore {
+
+class OriginLock : public ThreadSafeRefCounted<OriginLock> {
+ WTF_MAKE_NONCOPYABLE(OriginLock); WTF_MAKE_FAST_ALLOCATED;
+public:
+ OriginLock(String originPath);
+ ~OriginLock();
+
+ void lock();
+ void unlock();
+
+ static void deleteLockFile(String originPath);
+
+private:
+ static String lockFileNameForPath(String originPath);
+
+ String m_lockFileName;
+ Mutex m_mutex;
+#if USE(FILE_LOCK)
+ PlatformFileHandle m_lockHandle;
+#endif
+};
+
+} // namespace WebCore
+
+#endif // ENABLE(SQL_DATABASE)
+
+#endif // OriginLock_h
// Call the appropriate statement callback and track if it resulted in an error,
// because then we need to jump to the transaction error callback.
if (error) {
- ASSERT(errorCallback);
- callbackError = errorCallback->handleEvent(transaction, error.get());
+ if (errorCallback)
+ callbackError = errorCallback->handleEvent(transaction, error.get());
} else if (callback) {
RefPtr<SQLResultSet> resultSet = m_backend->sqlResultSet();
callbackError = !callback->handleEvent(transaction, resultSet.get());
#include "DatabaseBackend.h"
#include "DatabaseBackendContext.h"
#include "DatabaseThread.h"
+#include "DatabaseTracker.h"
#include "ExceptionCode.h"
#include "Logging.h"
+#include "OriginLock.h"
#include "SQLError.h"
#include "SQLStatementBackend.h"
#include "SQLTransactionClient.h"
ASSERT(currentThread() == database()->databaseContext()->databaseThread()->getThreadID());
+ releaseOriginLockIfNeeded();
+
MutexLocker locker(m_statementMutex);
m_statementQueue.clear();
return;
}
+ // If we get here, then we should be shutting down. Do clean up if needed:
if (m_nextState == SQLTransactionState::End)
return;
m_nextState = SQLTransactionState::End;
}
// Set the maximum usage for this transaction if this transactions is not read-only
- if (!m_readOnly)
+ if (!m_readOnly) {
+ acquireOriginLock();
m_database->sqliteDatabase().setMaximumSize(m_database->maximumSize());
+ }
ASSERT(!m_sqliteTransaction);
m_sqliteTransaction = adoptPtr(new SQLiteTransaction(m_database->sqliteDatabase(), m_readOnly));
m_sqliteTransaction->commit();
m_database->enableAuthorizer();
+ releaseOriginLockIfNeeded();
+
// If the commit failed, the transaction will still be marked as "in progress"
if (m_sqliteTransaction->inProgress()) {
if (m_wrapper)
}
m_database->enableAuthorizer();
+ releaseOriginLockIfNeeded();
+
ASSERT(!m_database->sqliteDatabase().transactionInProgress());
return SQLTransactionState::CleanupAndTerminate;
return SQLTransactionState::Idle;
}
+void SQLTransactionBackend::acquireOriginLock()
+{
+#if !PLATFORM(CHROMIUM)
+ ASSERT(!m_originLock);
+ m_originLock = DatabaseTracker::tracker().originLockFor(m_database->securityOrigin());
+ m_originLock->lock();
+#endif
+}
+
+void SQLTransactionBackend::releaseOriginLockIfNeeded()
+{
+#if !PLATFORM(CHROMIUM)
+ if (m_originLock) {
+ m_originLock->unlock();
+ m_originLock.clear();
+ }
+#endif
+}
+
} // namespace WebCore
#endif // ENABLE(SQL_DATABASE)
class AbstractSQLTransaction;
class DatabaseBackend;
+class OriginLock;
class SQLError;
class SQLiteTransaction;
class SQLStatementBackend;
void getNextStatement();
+ void acquireOriginLock();
+ void releaseOriginLockIfNeeded();
+
RefPtr<AbstractSQLTransaction> m_frontend; // Has a reference cycle, and will break in doCleanup().
RefPtr<SQLStatementBackend> m_currentStatementBackend;
Deque<RefPtr<SQLStatementBackend> > m_statementQueue;
OwnPtr<SQLiteTransaction> m_sqliteTransaction;
+#if !PLATFORM(CHROMIUM)
+ RefPtr<OriginLock> m_originLock;
+#endif
};
} // namespace WebCore
#include "DatabaseBackend.h"
#include "SQLTransactionBackend.h"
+#include "SecurityOrigin.h"
#include <wtf/Deque.h>
#include <wtf/HashMap.h>
#include <wtf/HashSet.h>
{
DatabaseBackend* database = transaction->database();
ASSERT(database);
- return database->stringIdentifier();
+ return database->securityOrigin()->databaseIdentifier();
}
SQLTransactionCoordinator::SQLTransactionCoordinator()
Modules/webdatabase/DatabaseTask.h \
Modules/webdatabase/DatabaseThread.h \
Modules/webdatabase/DatabaseTracker.h \
+ Modules/webdatabase/OriginLock.h \
Modules/webdatabase/SQLCallbackWrapper.h \
Modules/webdatabase/SQLResultSet.h \
Modules/webdatabase/SQLResultSetRowList.h \
Modules/webdatabase/DatabaseTask.cpp \
Modules/webdatabase/DatabaseThread.cpp \
Modules/webdatabase/DatabaseTracker.cpp \
+ Modules/webdatabase/OriginLock.cpp \
Modules/webdatabase/SQLException.cpp \
Modules/webdatabase/SQLResultSet.cpp \
Modules/webdatabase/SQLResultSetRowList.cpp \
['exclude', 'Modules/indexeddb/IDBFactoryBackendInterface\\.cpp$'],
['exclude', 'Modules/webdatabase/DatabaseManagerClient\\.h$'],
['exclude', 'Modules/webdatabase/DatabaseTracker\\.cpp$'],
+ ['exclude', 'Modules/webdatabase/OriginLock\\.cpp$'],
['exclude', 'Modules/webdatabase/SQLTransactionClient\\.cpp$'],
['exclude', 'inspector/InspectorFrontendClientLocal\\.cpp$'],
['exclude', 'inspector/JavaScript[^/]*\\.cpp$'],
'Modules/webdatabase/DatabaseThread.cpp',
'Modules/webdatabase/DatabaseThread.h',
'Modules/webdatabase/DatabaseTracker.cpp',
+ 'Modules/webdatabase/DatabaseTracker.h',
'Modules/webdatabase/DOMWindowWebDatabase.cpp',
'Modules/webdatabase/DOMWindowWebDatabase.h',
+ 'Modules/webdatabase/OriginLock.cpp',
+ 'Modules/webdatabase/OriginLock.h',
'Modules/webdatabase/SQLCallbackWrapper.h',
'Modules/webdatabase/SQLException.cpp',
'Modules/webdatabase/SQLException.h',
>
</File>
<File
+ RelativePath="..\Modules\webdatabase\OriginLock.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\Modules\webdatabase\OriginLock.h"
+ >
+ </File>
+ <File
RelativePath="..\Modules\webdatabase\SQLCallbackWrapper.h"
>
</File>
<ClCompile Include="..\Modules\webdatabase\DatabaseThread.cpp" />
<ClCompile Include="..\Modules\webdatabase\DatabaseTracker.cpp" />
<ClCompile Include="..\Modules\webdatabase\DOMWindowWebDatabase.cpp" />
+ <ClCompile Include="..\Modules\webdatabase\OriginLock.cpp" />
<ClCompile Include="..\Modules\webdatabase\SQLException.cpp" />
<ClCompile Include="..\Modules\webdatabase\SQLResultSet.cpp" />
<ClCompile Include="..\Modules\webdatabase\SQLResultSetRowList.cpp" />
<ClInclude Include="..\Modules\webdatabase\DatabaseThread.h" />
<ClInclude Include="..\Modules\webdatabase\DatabaseTracker.h" />
<ClInclude Include="..\Modules\webdatabase\DOMWindowWebDatabase.h" />
+ <ClInclude Include="..\Modules\webdatabase\OriginLock.h" />
<ClInclude Include="..\Modules\webdatabase\SQLCallbackWrapper.h" />
<ClInclude Include="..\Modules\webdatabase\SQLError.h" />
<ClInclude Include="..\Modules\webdatabase\SQLException.h" />
<ClCompile Include="..\Modules\webdatabase\DOMWindowWebDatabase.cpp">
<Filter>Modules\webdatabase</Filter>
</ClCompile>
+ <ClCompile Include="..\Modules\webdatabase\OriginLock.cpp">
+ <Filter>Modules\webdatabase</Filter>
+ </ClCompile>
<ClCompile Include="..\Modules\webdatabase\SQLException.cpp">
<Filter>Modules\webdatabase</Filter>
</ClCompile>
<ClInclude Include="..\Modules\webdatabase\DOMWindowWebDatabase.h">
<Filter>Modules\webdatabase</Filter>
</ClInclude>
+ <ClInclude Include="..\Modules\webdatabase\OriginLock.h">
+ <Filter>Modules\webdatabase</Filter>
+ </ClInclude>
<ClInclude Include="..\Modules\webdatabase\SQLCallbackWrapper.h">
<Filter>Modules\webdatabase</Filter>
</ClInclude>
FE80DA720E9C472F000D6F75 /* JSPositionError.h in Headers */ = {isa = PBXBuildFile; fileRef = FE80DA6E0E9C472F000D6F75 /* JSPositionError.h */; };
FE8A674716CDD19E00930BF8 /* SQLStatementBackend.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE8A674516CDD19E00930BF8 /* SQLStatementBackend.cpp */; };
FE8A674816CDD19E00930BF8 /* SQLStatementBackend.h in Headers */ = {isa = PBXBuildFile; fileRef = FE8A674616CDD19E00930BF8 /* SQLStatementBackend.h */; };
+ FE9E89FB16E2DC0500A908F8 /* OriginLock.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FE9E89F916E2DC0400A908F8 /* OriginLock.cpp */; };
+ FE9E89FC16E2DC0500A908F8 /* OriginLock.h in Headers */ = {isa = PBXBuildFile; fileRef = FE9E89FA16E2DC0400A908F8 /* OriginLock.h */; settings = {ATTRIBUTES = (Private, ); }; };
FEAD7D8716C339EE00D4670B /* SQLTransactionBackendSync.cpp in Sources */ = {isa = PBXBuildFile; fileRef = FEAD7D8516C339EE00D4670B /* SQLTransactionBackendSync.cpp */; };
FEAD7D8816C339EE00D4670B /* SQLTransactionBackendSync.h in Headers */ = {isa = PBXBuildFile; fileRef = FEAD7D8616C339EE00D4670B /* SQLTransactionBackendSync.h */; };
FEAF6654167970320062D0C5 /* DatabaseServer.h in Headers */ = {isa = PBXBuildFile; fileRef = FEAF6653167970070062D0C5 /* DatabaseServer.h */; settings = {ATTRIBUTES = (Private, ); }; };
FE80DA6E0E9C472F000D6F75 /* JSPositionError.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSPositionError.h; sourceTree = "<group>"; };
FE8A674516CDD19E00930BF8 /* SQLStatementBackend.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SQLStatementBackend.cpp; path = Modules/webdatabase/SQLStatementBackend.cpp; sourceTree = "<group>"; };
FE8A674616CDD19E00930BF8 /* SQLStatementBackend.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SQLStatementBackend.h; path = Modules/webdatabase/SQLStatementBackend.h; sourceTree = "<group>"; };
+ FE9E89F916E2DC0400A908F8 /* OriginLock.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = OriginLock.cpp; path = Modules/webdatabase/OriginLock.cpp; sourceTree = "<group>"; };
+ FE9E89FA16E2DC0400A908F8 /* OriginLock.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = OriginLock.h; path = Modules/webdatabase/OriginLock.h; sourceTree = "<group>"; };
FEAD7D8516C339EE00D4670B /* SQLTransactionBackendSync.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = SQLTransactionBackendSync.cpp; path = Modules/webdatabase/SQLTransactionBackendSync.cpp; sourceTree = "<group>"; };
FEAD7D8616C339EE00D4670B /* SQLTransactionBackendSync.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = SQLTransactionBackendSync.h; path = Modules/webdatabase/SQLTransactionBackendSync.h; sourceTree = "<group>"; };
FEAF6653167970070062D0C5 /* DatabaseServer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DatabaseServer.h; path = Modules/webdatabase/DatabaseServer.h; sourceTree = "<group>"; };
A8CCBB46151F831600AB7CE9 /* DOMWindowWebDatabase.cpp */,
A8CCBB47151F831600AB7CE9 /* DOMWindowWebDatabase.h */,
A8CCBB4E151F835A00AB7CE9 /* DOMWindowWebDatabase.idl */,
+ FE9E89F916E2DC0400A908F8 /* OriginLock.cpp */,
+ FE9E89FA16E2DC0400A908F8 /* OriginLock.h */,
97BC69F91505F081001B74AC /* SQLCallbackWrapper.h */,
97BC69FA1505F081001B74AC /* SQLError.h */,
97BC69FB1505F081001B74AC /* SQLError.idl */,
buildActionMask = 2147483647;
files = (
FE115FAB167988CD00249134 /* AbstractDatabaseServer.h in Headers */,
+ FE9E89FC16E2DC0500A908F8 /* OriginLock.h in Headers */,
FE4AADEE16D2C37400026FFC /* AbstractSQLStatement.h in Headers */,
FE4AADEF16D2C37400026FFC /* AbstractSQLStatementBackend.h in Headers */,
41E1B1D10FF5986900576B3B /* AbstractWorker.h in Headers */,
FDB8D1E516B0CC9700340F10 /* ExclusionShapeInfo.cpp in Sources */,
FD748ABF15BF74ED0059CF0D /* ExclusionShapeInsideInfo.cpp in Sources */,
9A9CEF8D163B3EA100DE7EFE /* ExclusionShapeOutsideInfo.cpp in Sources */,
+ FE9E89FB16E2DC0500A908F8 /* OriginLock.cpp in Sources */,
6E67D2A61280E8A4008758F7 /* Extensions3DOpenGL.cpp in Sources */,
44DAB5B115A623580097C1E4 /* Extensions3DOpenGLCommon.cpp in Sources */,
7728694E14F8882500F484DC /* EXTTextureFilterAnisotropic.cpp in Sources */,
/*
- * Copyright (C) 2004, 2005, 2006 Apple Inc.
+ * Copyright (C) 2004, 2005, 2006, 2013 Apple Inc.
* Copyright (C) 2009 Google Inc. All rights reserved.
*
* This library is free software; you can redistribute it and/or
#include <wtf/Platform.h>
+#if PLATFORM(MAC) || PLATFORM(IOS)
+#define WTF_USE_FILE_LOCK 1
+#endif
+
#if PLATFORM(WIN) && !OS(WINCE)
#include <WebCore/WebCoreHeaderDetection.h>
#endif
SeekFromEnd
};
+enum FileLockMode {
+ LockShared = 1,
+ LockExclusive = 2,
+ LockNonBlocking = 4
+};
+
#if OS(WINDOWS)
static const char PlatformFilePathSeparator = '\\';
#else
int writeToFile(PlatformFileHandle, const char* data, int length);
// Returns number of bytes actually written if successful, -1 otherwise.
int readFromFile(PlatformFileHandle, char* data, int length);
+#if USE(FILE_LOCK)
+bool lockFile(PlatformFileHandle, FileLockMode);
+bool unlockFile(PlatformFileHandle);
+#endif
// Functions for working with loadable modules.
bool unloadModule(PlatformModule);
return -1;
}
+#if USE(FILE_LOCK)
+bool lockFile(PlatformFileHandle handle, FileLockMode lockMode)
+{
+ COMPILE_ASSERT(LOCK_SH == LockShared, LockSharedEncodingIsAsExpected);
+ COMPILE_ASSERT(LOCK_EX == LockExclusive, LockExclusiveEncodingIsAsExpected);
+ COMPILE_ASSERT(LOCK_NB == LockNonBlocking, LockNonBlockingEncodingIsAsExpected);
+ int result = flock(handle, lockMode);
+ return (result != -1);
+}
+
+bool unlockFile(PlatformFileHandle handle)
+{
+ int result = flock(handle, LOCK_UN);
+ return (result != -1);
+}
+#endif
+
bool deleteEmptyDirectory(const String& path)
{
CString fsRep = fileSystemRepresentation(path);