Improve robustness of WebSQL quota management.
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: http://svn.webkit.org/repository/webkit/trunk@144760 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h
index ce5459f..00c2280 100644
--- a/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h
+++ b/Source/WebCore/Modules/webdatabase/SQLTransactionBackend.h
@@ -42,6 +42,7 @@
class AbstractSQLTransaction;
class DatabaseBackend;
+class OriginLock;
class SQLError;
class SQLiteTransaction;
class SQLStatementBackend;
@@ -108,6 +109,9 @@
void getNextStatement();
+ void acquireOriginLock();
+ void releaseOriginLockIfNeeded();
+
RefPtr<AbstractSQLTransaction> m_frontend; // Has a reference cycle, and will break in doCleanup().
RefPtr<SQLStatementBackend> m_currentStatementBackend;
@@ -128,6 +132,9 @@
Deque<RefPtr<SQLStatementBackend> > m_statementQueue;
OwnPtr<SQLiteTransaction> m_sqliteTransaction;
+#if !PLATFORM(CHROMIUM)
+ RefPtr<OriginLock> m_originLock;
+#endif
};
} // namespace WebCore