diff options
author | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
---|---|---|
committer | Matt A. Tobin <mattatobin@localhost.localdomain> | 2018-02-02 04:16:08 -0500 |
commit | 5f8de423f190bbb79a62f804151bc24824fa32d8 (patch) | |
tree | 10027f336435511475e392454359edea8e25895d /storage/StorageBaseStatementInternal.cpp | |
parent | 49ee0794b5d912db1f95dce6eb52d781dc210db5 (diff) | |
download | uxp-5f8de423f190bbb79a62f804151bc24824fa32d8.tar.gz |
Add m-esr52 at 52.6.0
Diffstat (limited to 'storage/StorageBaseStatementInternal.cpp')
-rw-r--r-- | storage/StorageBaseStatementInternal.cpp | 221 |
1 files changed, 221 insertions, 0 deletions
diff --git a/storage/StorageBaseStatementInternal.cpp b/storage/StorageBaseStatementInternal.cpp new file mode 100644 index 0000000000..d6545fcb4d --- /dev/null +++ b/storage/StorageBaseStatementInternal.cpp @@ -0,0 +1,221 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: sw=2 ts=2 sts=2 expandtab + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "StorageBaseStatementInternal.h" + +#include "nsProxyRelease.h" + +#include "mozStorageBindingParamsArray.h" +#include "mozStorageStatementData.h" +#include "mozStorageAsyncStatementExecution.h" + +namespace mozilla { +namespace storage { + +//////////////////////////////////////////////////////////////////////////////// +//// Local Classes + +/** + * Used to finalize an asynchronous statement on the background thread. + */ +class AsyncStatementFinalizer : public Runnable +{ +public: + /** + * Constructor for the event. + * + * @param aStatement + * We need the AsyncStatement to be able to get at the sqlite3_stmt; + * we only access/create it on the async thread. + * @param aConnection + * We need the connection to know what thread to release the statement + * on. We release the statement on that thread since releasing the + * statement might end up releasing the connection too. + */ + AsyncStatementFinalizer(StorageBaseStatementInternal *aStatement, + Connection *aConnection) + : mStatement(aStatement) + , mConnection(aConnection) + { + } + + NS_IMETHOD Run() override + { + if (mStatement->mAsyncStatement) { + sqlite3_finalize(mStatement->mAsyncStatement); + mStatement->mAsyncStatement = nullptr; + } + + nsCOMPtr<nsIThread> targetThread(mConnection->threadOpenedOn); + NS_ProxyRelease(targetThread, mStatement.forget()); + return NS_OK; + } +private: + RefPtr<StorageBaseStatementInternal> mStatement; + RefPtr<Connection> mConnection; +}; + +/** + * Finalize a sqlite3_stmt on the background thread for a statement whose + * destructor was invoked and the statement was non-null. + */ +class LastDitchSqliteStatementFinalizer : public Runnable +{ +public: + /** + * Event constructor. + * + * @param aConnection + * Used to keep the connection alive. If we failed to do this, it + * is possible that the statement going out of scope invoking us + * might have the last reference to the connection and so trigger + * an attempt to close the connection which is doomed to fail + * (because the asynchronous execution thread must exist which will + * trigger the failure case). + * @param aStatement + * The sqlite3_stmt to finalize. This object takes ownership / + * responsibility for the instance and all other references to it + * should be forgotten. + */ + LastDitchSqliteStatementFinalizer(RefPtr<Connection> &aConnection, + sqlite3_stmt *aStatement) + : mConnection(aConnection) + , mAsyncStatement(aStatement) + { + NS_PRECONDITION(aConnection, "You must provide a Connection"); + } + + NS_IMETHOD Run() override + { + (void)::sqlite3_finalize(mAsyncStatement); + mAsyncStatement = nullptr; + + nsCOMPtr<nsIThread> target(mConnection->threadOpenedOn); + (void)::NS_ProxyRelease(target, mConnection.forget()); + return NS_OK; + } +private: + RefPtr<Connection> mConnection; + sqlite3_stmt *mAsyncStatement; +}; + +//////////////////////////////////////////////////////////////////////////////// +//// StorageBaseStatementInternal + +StorageBaseStatementInternal::StorageBaseStatementInternal() +: mAsyncStatement(nullptr) +{ +} + +void +StorageBaseStatementInternal::asyncFinalize() +{ + nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget(); + if (target) { + // Attempt to finalize asynchronously + nsCOMPtr<nsIRunnable> event = + new AsyncStatementFinalizer(this, mDBConnection); + + // Dispatch. Note that dispatching can fail, typically if + // we have a race condition with asyncClose(). It's ok, + // let asyncClose() win. + (void)target->Dispatch(event, NS_DISPATCH_NORMAL); + } + // If we cannot get the background thread, + // mozStorageConnection::AsyncClose() has already been called and + // the statement either has been or will be cleaned up by + // internalClose(). +} + +void +StorageBaseStatementInternal::destructorAsyncFinalize() +{ + if (!mAsyncStatement) + return; + + // If we reach this point, our owner has not finalized this + // statement, yet we are being destructed. If possible, we want to + // auto-finalize it early, to release the resources early. + nsIEventTarget *target = mDBConnection->getAsyncExecutionTarget(); + if (target) { + // If we can get the async execution target, we can indeed finalize + // the statement, as the connection is still open. + bool isAsyncThread = false; + (void)target->IsOnCurrentThread(&isAsyncThread); + + nsCOMPtr<nsIRunnable> event = + new LastDitchSqliteStatementFinalizer(mDBConnection, mAsyncStatement); + if (isAsyncThread) { + (void)event->Run(); + } else { + (void)target->Dispatch(event, NS_DISPATCH_NORMAL); + } + } + + // We might not be able to dispatch to the background thread, + // presumably because it is being shutdown. Since said shutdown will + // finalize the statement, we just need to clean-up around here. + mAsyncStatement = nullptr; +} + +NS_IMETHODIMP +StorageBaseStatementInternal::NewBindingParamsArray( + mozIStorageBindingParamsArray **_array +) +{ + nsCOMPtr<mozIStorageBindingParamsArray> array = new BindingParamsArray(this); + NS_ENSURE_TRUE(array, NS_ERROR_OUT_OF_MEMORY); + + array.forget(_array); + return NS_OK; +} + +NS_IMETHODIMP +StorageBaseStatementInternal::ExecuteAsync( + mozIStorageStatementCallback *aCallback, + mozIStoragePendingStatement **_stmt +) +{ + // We used to call Connection::ExecuteAsync but it takes a + // mozIStorageBaseStatement signature because it is also a public API. Since + // our 'this' has no static concept of mozIStorageBaseStatement and Connection + // would just QI it back across to a StorageBaseStatementInternal and the + // actual logic is very simple, we now roll our own. + nsTArray<StatementData> stmts(1); + StatementData data; + nsresult rv = getAsynchronousStatementData(data); + NS_ENSURE_SUCCESS(rv, rv); + NS_ENSURE_TRUE(stmts.AppendElement(data), NS_ERROR_OUT_OF_MEMORY); + + // Dispatch to the background + return AsyncExecuteStatements::execute(stmts, mDBConnection, + mNativeConnection, aCallback, _stmt); +} + +NS_IMETHODIMP +StorageBaseStatementInternal::EscapeStringForLIKE( + const nsAString &aValue, + const char16_t aEscapeChar, + nsAString &_escapedString +) +{ + const char16_t MATCH_ALL('%'); + const char16_t MATCH_ONE('_'); + + _escapedString.Truncate(0); + + for (uint32_t i = 0; i < aValue.Length(); i++) { + if (aValue[i] == aEscapeChar || aValue[i] == MATCH_ALL || + aValue[i] == MATCH_ONE) { + _escapedString += aEscapeChar; + } + _escapedString += aValue[i]; + } + return NS_OK; +} + +} // namespace storage +} // namespace mozilla |