NS_IMETHODIMP Connection::CreateAggregateFunction(const nsACString &aFunctionName, PRInt32 aNumArguments, mozIStorageAggregateFunction *aFunction) { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; // Check to see if this function name is already defined. SQLiteMutexAutoLock lockedScope(sharedDBMutex); NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE); // Because aggregate functions depend on state across calls, you cannot have // the same instance use the same name. We want to enumerate all functions // and make sure this instance is not already registered. NS_ENSURE_FALSE(findFunctionByInstance(aFunction), NS_ERROR_FAILURE); int srv = ::sqlite3_create_function(mDBConn, nsPromiseFlatCString(aFunctionName).get(), aNumArguments, SQLITE_ANY, aFunction, NULL, aggregateFunctionStepHelper, aggregateFunctionFinalHelper); if (srv != SQLITE_OK) return convertResultCode(srv); FunctionInfo info = { aFunction, Connection::FunctionInfo::AGGREGATE, aNumArguments }; NS_ENSURE_TRUE(mFunctions.Put(aFunctionName, info), NS_ERROR_OUT_OF_MEMORY); return NS_OK; }
NS_IMETHODIMP Connection::CreateFunction(const nsACString &aFunctionName, PRInt32 aNumArguments, mozIStorageFunction *aFunction) { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; // Check to see if this function is already defined. We only check the name // because a function can be defined with the same body but different names. SQLiteMutexAutoLock lockedScope(sharedDBMutex); NS_ENSURE_FALSE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE); int srv = ::sqlite3_create_function(mDBConn, nsPromiseFlatCString(aFunctionName).get(), aNumArguments, SQLITE_ANY, aFunction, basicFunctionHelper, NULL, NULL); if (srv != SQLITE_OK) return convertResultCode(srv); FunctionInfo info = { aFunction, Connection::FunctionInfo::SIMPLE, aNumArguments }; NS_ENSURE_TRUE(mFunctions.Put(aFunctionName, info), NS_ERROR_OUT_OF_MEMORY); return NS_OK; }
NS_IMETHODIMP Connection::BeginTransactionAs(PRInt32 aTransactionType) { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; SQLiteMutexAutoLock lockedScope(sharedDBMutex); if (mTransactionInProgress) return NS_ERROR_FAILURE; nsresult rv; switch(aTransactionType) { case TRANSACTION_DEFERRED: rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN DEFERRED")); break; case TRANSACTION_IMMEDIATE: rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN IMMEDIATE")); break; case TRANSACTION_EXCLUSIVE: rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("BEGIN EXCLUSIVE")); break; default: return NS_ERROR_ILLEGAL_VALUE; } if (NS_SUCCEEDED(rv)) mTransactionInProgress = PR_TRUE; return rv; }
NS_IMETHODIMP Connection::GetTransactionInProgress(PRBool *_inProgress) { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; SQLiteMutexAutoLock lockedScope(sharedDBMutex); *_inProgress = mTransactionInProgress; return NS_OK; }
void MessageHandler::RemoveHandlerFromDevices() { MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); Lock::Locker lockedScope(handlerImpl->pLock); while(!handlerImpl->UseList.IsEmpty()) { MessageHandlerRef* use = handlerImpl->UseList.GetFirst(); use->SetHandler_NTS(0); } }
MessageHandler::~MessageHandler() { MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); { Lock::Locker lockedScope(handlerImpl->pLock); OVR_ASSERT_LOG(handlerImpl->UseList.IsEmpty(), ("~MessageHandler %p - Handler still active; call RemoveHandlerFromDevices", this)); } Destruct<MessageHandlerImpl>(handlerImpl); }
// Warning: To get a Connection's measurements requires holding its lock. // There may be a delay getting the lock if another thread is accessing the // Connection. This isn't very nice if CollectReports is called from the // main thread! But at the time of writing this function is only called when // about:memory is loaded (not, for example, when telemetry pings occur) and // any delays in that case aren't so bad. NS_IMETHOD CollectReports(nsIMemoryMultiReporterCallback *aCb, nsISupports *aClosure) { nsresult rv; size_t totalConnSize = 0; { nsTArray<nsRefPtr<Connection> > connections; mService->getConnections(connections); for (uint32_t i = 0; i < connections.Length(); i++) { nsRefPtr<Connection> &conn = connections[i]; // Someone may have closed the Connection, in which case we skip it. bool isReady; (void)conn->GetConnectionReady(&isReady); if (!isReady) { continue; } nsCString pathHead("explicit/storage/sqlite/"); pathHead.Append(conn->getFilename()); pathHead.AppendLiteral("/"); SQLiteMutexAutoLock lockedScope(conn->sharedDBMutex); rv = reportConn(aCb, aClosure, *conn.get(), pathHead, NS_LITERAL_CSTRING("stmt"), mStmtDesc, SQLITE_DBSTATUS_STMT_USED, &totalConnSize); NS_ENSURE_SUCCESS(rv, rv); rv = reportConn(aCb, aClosure, *conn.get(), pathHead, NS_LITERAL_CSTRING("cache"), mCacheDesc, SQLITE_DBSTATUS_CACHE_USED, &totalConnSize); NS_ENSURE_SUCCESS(rv, rv); rv = reportConn(aCb, aClosure, *conn.get(), pathHead, NS_LITERAL_CSTRING("schema"), mSchemaDesc, SQLITE_DBSTATUS_SCHEMA_USED, &totalConnSize); NS_ENSURE_SUCCESS(rv, rv); } } int64_t other = ::sqlite3_memory_used() - totalConnSize; rv = aCb->Callback(NS_LITERAL_CSTRING(""), NS_LITERAL_CSTRING("explicit/storage/sqlite/other"), nsIMemoryReporter::KIND_HEAP, nsIMemoryReporter::UNITS_BYTES, other, NS_LITERAL_CSTRING("All unclassified sqlite memory."), aClosure); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; }
bool AsyncExecuteStatements::executeAndProcessStatement(sqlite3_stmt *aStatement, bool aLastStatement) { mMutex.AssertNotCurrentThreadOwns(); // Execute our statement bool hasResults; do { hasResults = executeStatement(aStatement); // If we had an error, bail. if (mState == ERROR) return false; // If we have been canceled, there is no point in going on... { MutexAutoLock lockedScope(mMutex); if (mCancelRequested) { mState = CANCELED; return false; } } // Build our result set and notify if we got anything back and have a // callback to notify. if (mCallback && hasResults && NS_FAILED(buildAndNotifyResults(aStatement))) { // We had an error notifying, so we notify on error and stop processing. mState = ERROR; // Notify, and stop processing statements. (void)notifyError(mozIStorageError::ERROR, "An error occurred while notifying about results"); return false; } } while (hasResults); #ifdef DEBUG // Check to make sure that this statement was smart about what it did. checkAndLogStatementPerformance(aStatement); #endif // If we are done, we need to set our state accordingly while we still hold // our mutex. We would have already returned if we were canceled or had // an error at this point. if (aLastStatement) mState = COMPLETED; return true; }
bool AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement) { mMutex.AssertNotCurrentThreadOwns(); Telemetry::AutoTimer<Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_MS> finallySendExecutionDuration(mRequestStartDate); while (true) { // lock the sqlite mutex so sqlite3_errmsg cannot change SQLiteMutexAutoLock lockedScope(mDBMutex); int rc = mConnection->stepStatement(mNativeConnection, aStatement); // Stop if we have no more results. if (rc == SQLITE_DONE) { Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, true); return false; } // If we got results, we can return now. if (rc == SQLITE_ROW) { Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, true); return true; } // Some errors are not fatal, and we can handle them and continue. if (rc == SQLITE_BUSY) { // Don't hold the lock while we call outside our module. SQLiteMutexAutoUnlock unlockedScope(mDBMutex); // Yield, and try again (void)::PR_Sleep(PR_INTERVAL_NO_WAIT); continue; } // Set an error state. mState = ERROR; Telemetry::Accumulate(Telemetry::MOZ_STORAGE_ASYNC_REQUESTS_SUCCESS, false); // Construct the error message before giving up the mutex (which we cannot // hold during the call to notifyError). nsCOMPtr<mozIStorageError> errorObj( new Error(rc, ::sqlite3_errmsg(mNativeConnection)) ); // We cannot hold the DB mutex while calling notifyError. SQLiteMutexAutoUnlock unlockedScope(mDBMutex); (void)notifyError(errorObj); // Finally, indicate that we should stop processing. return false; } }
NS_IMETHODIMP Connection::RemoveProgressHandler(mozIStorageProgressHandler **_oldHandler) { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; // Return previous one SQLiteMutexAutoLock lockedScope(sharedDBMutex); NS_IF_ADDREF(*_oldHandler = mProgressHandler); mProgressHandler = nsnull; ::sqlite3_progress_handler(mDBConn, 0, NULL, NULL); return NS_OK; }
NS_IMETHODIMP Connection::RollbackTransaction() { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; SQLiteMutexAutoLock lockedScope(sharedDBMutex); if (!mTransactionInProgress) return NS_ERROR_UNEXPECTED; nsresult rv = ExecuteSimpleSQL(NS_LITERAL_CSTRING("ROLLBACK TRANSACTION")); if (NS_SUCCEEDED(rv)) mTransactionInProgress = PR_FALSE; return rv; }
NS_IMETHODIMP Connection::Close() { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; { // Make sure we have not executed any asynchronous statements. MutexAutoLock lockedScope(sharedAsyncExecutionMutex); NS_ENSURE_FALSE(mAsyncExecutionThread, NS_ERROR_UNEXPECTED); } nsresult rv = setClosedState(); NS_ENSURE_SUCCESS(rv, rv); return internalClose(); }
nsresult Connection::internalClose() { #ifdef DEBUG // Sanity checks to make sure we are in the proper state before calling this. NS_ASSERTION(mDBConn, "Database connection is already null!"); { // Make sure we have marked our async thread as shutting down. MutexAutoLock lockedScope(sharedAsyncExecutionMutex); NS_ASSERTION(mAsyncExecutionThreadShuttingDown, "Did not call setClosedState!"); } { // Ensure that we are being called on the thread we were opened with. PRBool onOpenedThread = PR_FALSE; (void)threadOpenedOn->IsOnCurrentThread(&onOpenedThread); NS_ASSERTION(onOpenedThread, "Not called on the thread the database was opened on!"); } #endif #ifdef PR_LOGGING nsCAutoString leafName(":memory"); if (mDatabaseFile) (void)mDatabaseFile->GetNativeLeafName(leafName); PR_LOG(gStorageLog, PR_LOG_NOTICE, ("Closing connection to '%s'", leafName.get())); #endif #ifdef DEBUG // Notify about any non-finalized statements. sqlite3_stmt *stmt = NULL; while ((stmt = ::sqlite3_next_stmt(mDBConn, stmt))) { char *msg = ::PR_smprintf("SQL statement '%s' was not finalized", ::sqlite3_sql(stmt)); NS_WARNING(msg); ::PR_smprintf_free(msg); } #endif int srv = ::sqlite3_close(mDBConn); NS_ASSERTION(srv == SQLITE_OK, "sqlite3_close failed. There are probably outstanding statements that are listed above!"); mDBConn = NULL; return convertResultCode(srv); }
NS_IMETHODIMP AsyncExecuteStatements::Run() { // Do not run if we have been canceled. { MutexAutoLock lockedScope(mMutex); if (mCancelRequested) mState = CANCELED; } if (mState == CANCELED) return notifyComplete(); // If there is more than one statement, run it in a transaction. We assume // that we have been given write statements since getting a batch of read // statements doesn't make a whole lot of sense. // Additionally, if we have only one statement and it needs a transaction, we // will wrap it in one. if (mStatements.Length() > 1 || mStatements[0].needsTransaction()) { // We don't error if this failed because it's not terrible if it does. mTransactionManager = new mozStorageTransaction(mConnection, PR_FALSE, mozIStorageConnection::TRANSACTION_IMMEDIATE); } // Execute each statement, giving the callback results if it returns any. for (PRUint32 i = 0; i < mStatements.Length(); i++) { bool finished = (i == (mStatements.Length() - 1)); // If we have parameters to bind, bind them, execute, and process. if (mStatements[i].hasParametersToBeBound()) { if (!bindExecuteAndProcessStatement(mStatements[i], finished)) break; } // Otherwise, just execute and process the statement. else if (!executeAndProcessStatement(mStatements[i], finished)) { break; } } // If we still have results that we haven't notified about, take care of // them now. if (mResultSet) (void)notifyResults(); // Notify about completion return notifyComplete(); }
bool AsyncExecuteStatements::executeStatement(sqlite3_stmt *aStatement) { mMutex.AssertNotCurrentThreadOwns(); while (true) { // lock the sqlite mutex so sqlite3_errmsg cannot change SQLiteMutexAutoLock lockedScope(mDBMutex); int rc = stepStmt(aStatement); // Stop if we have no more results. if (rc == SQLITE_DONE) return false; // If we got results, we can return now. if (rc == SQLITE_ROW) return true; // Some errors are not fatal, and we can handle them and continue. if (rc == SQLITE_BUSY) { // Don't hold the lock while we call outside our module. SQLiteMutexAutoUnlock unlockedScope(mDBMutex); // Yield, and try again (void)::PR_Sleep(PR_INTERVAL_NO_WAIT); continue; } // Set an error state. mState = ERROR; // Construct the error message before giving up the mutex (which we cannot // hold during the call to notifyError). sqlite3 *db = mConnection->GetNativeConnection(); nsCOMPtr<mozIStorageError> errorObj(new Error(rc, ::sqlite3_errmsg(db))); // We cannot hold the DB mutex while calling notifyError. SQLiteMutexAutoUnlock unlockedScope(mDBMutex); (void)notifyError(errorObj); // Finally, indicate that we should stop processing. return false; } }
NS_IMETHODIMP Connection::SetProgressHandler(PRInt32 aGranularity, mozIStorageProgressHandler *aHandler, mozIStorageProgressHandler **_oldHandler) { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; // Return previous one SQLiteMutexAutoLock lockedScope(sharedDBMutex); NS_IF_ADDREF(*_oldHandler = mProgressHandler); if (!aHandler || aGranularity <= 0) { aHandler = nsnull; aGranularity = 0; } mProgressHandler = aHandler; ::sqlite3_progress_handler(mDBConn, aGranularity, sProgressHelper, this); return NS_OK; }
nsIEventTarget * Connection::getAsyncExecutionTarget() { MutexAutoLock lockedScope(sharedAsyncExecutionMutex); // If we are shutting down the asynchronous thread, don't hand out any more // references to the thread. if (mAsyncExecutionThreadShuttingDown) return nsnull; if (!mAsyncExecutionThread) { nsresult rv = ::NS_NewThread(getter_AddRefs(mAsyncExecutionThread)); if (NS_FAILED(rv)) { NS_WARNING("Failed to create async thread."); return nsnull; } } return mAsyncExecutionThread; }
NS_IMETHODIMP AsyncExecuteStatements::Cancel() { #ifdef DEBUG bool onCallingThread = false; (void)mCallingThread->IsOnCurrentThread(&onCallingThread); NS_ASSERTION(onCallingThread, "Not canceling from the calling thread!"); #endif // If we have already canceled, we have an error, but always indicate that // we are trying to cancel. NS_ENSURE_FALSE(mCancelRequested, NS_ERROR_UNEXPECTED); { MutexAutoLock lockedScope(mMutex); // We need to indicate that we want to try and cancel now. mCancelRequested = true; } return NS_OK; }
nsresult Connection::setClosedState() { // Ensure that we are on the correct thread to close the database. PRBool onOpenedThread; nsresult rv = threadOpenedOn->IsOnCurrentThread(&onOpenedThread); NS_ENSURE_SUCCESS(rv, rv); if (!onOpenedThread) { NS_ERROR("Must close the database on the thread that you opened it with!"); return NS_ERROR_UNEXPECTED; } // Flag that we are shutting down the async thread, so that // getAsyncExecutionTarget knows not to expose/create the async thread. { MutexAutoLock lockedScope(sharedAsyncExecutionMutex); NS_ENSURE_FALSE(mAsyncExecutionThreadShuttingDown, NS_ERROR_UNEXPECTED); mAsyncExecutionThreadShuttingDown = true; } return NS_OK; }
NS_IMETHODIMP Connection::RemoveFunction(const nsACString &aFunctionName) { if (!mDBConn) return NS_ERROR_NOT_INITIALIZED; SQLiteMutexAutoLock lockedScope(sharedDBMutex); NS_ENSURE_TRUE(mFunctions.Get(aFunctionName, NULL), NS_ERROR_FAILURE); int srv = ::sqlite3_create_function(mDBConn, nsPromiseFlatCString(aFunctionName).get(), 0, SQLITE_ANY, NULL, NULL, NULL, NULL); if (srv != SQLITE_OK) return convertResultCode(srv); mFunctions.Remove(aFunctionName); return NS_OK; }
NS_IMETHODIMP AsyncExecuteStatements::Run() { MOZ_ASSERT(!mConnection->isClosed()); // Do not run if we have been canceled. { MutexAutoLock lockedScope(mMutex); if (mCancelRequested) mState = CANCELED; } if (mState == CANCELED) return notifyComplete(); if (statementsNeedTransaction() && mConnection->getAutocommit()) { if (NS_SUCCEEDED(mConnection->beginTransactionInternal(mNativeConnection, mozIStorageConnection::TRANSACTION_IMMEDIATE))) { mHasTransaction = true; } #ifdef DEBUG else { NS_WARNING("Unable to create a transaction for async execution."); } #endif } // Execute each statement, giving the callback results if it returns any. for (uint32_t i = 0; i < mStatements.Length(); i++) { bool finished = (i == (mStatements.Length() - 1)); sqlite3_stmt *stmt; { // lock the sqlite mutex so sqlite3_errmsg cannot change SQLiteMutexAutoLock lockedScope(mDBMutex); int rc = mStatements[i].getSqliteStatement(&stmt); if (rc != SQLITE_OK) { // Set our error state. mState = ERROR; // Build the error object; can't call notifyError with the lock held nsCOMPtr<mozIStorageError> errorObj( new Error(rc, ::sqlite3_errmsg(mNativeConnection)) ); { // We cannot hold the DB mutex and call notifyError. SQLiteMutexAutoUnlock unlockedScope(mDBMutex); (void)notifyError(errorObj); } break; } } // If we have parameters to bind, bind them, execute, and process. if (mStatements[i].hasParametersToBeBound()) { if (!bindExecuteAndProcessStatement(mStatements[i], finished)) break; } // Otherwise, just execute and process the statement. else if (!executeAndProcessStatement(stmt, finished)) { break; } } // If we still have results that we haven't notified about, take care of // them now. if (mResultSet) (void)notifyResults(); // Notify about completion return notifyComplete(); }
bool MessageHandler::IsHandlerInstalled() const { const MessageHandlerImpl* handlerImpl = MessageHandlerImpl::FromHandler(this); Lock::Locker lockedScope(handlerImpl->pLock); return handlerImpl->UseList.IsEmpty() != true; }
NS_IMETHODIMP AsyncExecuteStatements::Run() { // Do not run if we have been canceled. { MutexAutoLock lockedScope(mMutex); if (mCancelRequested) mState = CANCELED; } if (mState == CANCELED) return notifyComplete(); if (statementsNeedTransaction()) { mTransactionManager = new mozStorageTransaction(mConnection, false, mozIStorageConnection::TRANSACTION_IMMEDIATE); } // Execute each statement, giving the callback results if it returns any. for (PRUint32 i = 0; i < mStatements.Length(); i++) { bool finished = (i == (mStatements.Length() - 1)); sqlite3_stmt *stmt; { // lock the sqlite mutex so sqlite3_errmsg cannot change SQLiteMutexAutoLock lockedScope(mDBMutex); int rc = mStatements[i].getSqliteStatement(&stmt); if (rc != SQLITE_OK) { // Set our error state. mState = ERROR; // Build the error object; can't call notifyError with the lock held sqlite3 *db = mConnection->GetNativeConnection(); nsCOMPtr<mozIStorageError> errorObj( new Error(rc, ::sqlite3_errmsg(db)) ); { // We cannot hold the DB mutex and call notifyError. SQLiteMutexAutoUnlock unlockedScope(mDBMutex); (void)notifyError(errorObj); } break; } } // If we have parameters to bind, bind them, execute, and process. if (mStatements[i].hasParametersToBeBound()) { if (!bindExecuteAndProcessStatement(mStatements[i], finished)) break; } // Otherwise, just execute and process the statement. else if (!executeAndProcessStatement(stmt, finished)) { break; } } // If we still have results that we haven't notified about, take care of // them now. if (mResultSet) (void)notifyResults(); // Notify about completion return notifyComplete(); }