/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * vim: sw=2 ts=2 et lcs=trail\:.,tab\:>~ : * 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/. */
/** * Passes a single SQLite memory statistic to a memory reporter callback. * * @param aHandleReport * The callback. * @param aData * The data for the callback. * @param aConn * The SQLite connection. * @param aPathHead * Head of the path for the memory report. * @param aKind * The memory report statistic kind, one of "stmt", "cache" or * "schema". * @param aDesc * The memory report description. * @param aOption * The SQLite constant for getting the measurement. * @param aTotal * The accumulator for the measurement.
*/ staticvoid ReportConn(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, Connection* aConn, const nsACString& aPathHead, const nsACString& aKind, const nsACString& aDesc, int32_t aOption,
size_t* aTotal) {
nsCString path(aPathHead);
path.Append(aKind);
path.AppendLiteral("-used");
// 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_IMETHODIMP
Service::CollectReports(nsIHandleReportCallback* aHandleReport,
nsISupports* aData, bool aAnonymize) {
size_t totalConnSize = 0;
{
nsTArray<RefPtr<Connection>> connections;
getConnections(connections);
for (uint32_t i = 0; i < connections.Length(); i++) {
RefPtr<Connection>& conn = connections[i];
// Someone may have closed the Connection, in which case we skip it. // Note that we have consumers of the synchronous API that are off the // main-thread, like the DOM Cache and IndexedDB, and as such we must be // sure that we have a connection.
MutexAutoLock lockedAsyncScope(conn->sharedAsyncExecutionMutex); if (!conn->connectionReady()) { continue;
}
nsCString pathHead("explicit/storage/sqlite/"); // This filename isn't privacy-sensitive, and so is never anonymized.
pathHead.Append(conn->getFilename());
pathHead.Append('/');
constexpr auto stmtDesc = "Memory (approximate) used by all prepared statements used by " "connections to this database."_ns;
ReportConn(aHandleReport, aData, conn, pathHead, "stmt"_ns, stmtDesc,
SQLITE_DBSTATUS_STMT_USED, &totalConnSize);
constexpr auto cacheDesc = "Memory (approximate) used by all pager caches used by connections " "to this database."_ns;
ReportConn(aHandleReport, aData, conn, pathHead, "cache"_ns, cacheDesc,
SQLITE_DBSTATUS_CACHE_USED_SHARED, &totalConnSize);
constexpr auto schemaDesc = "Memory (approximate) used to store the schema for all databases " "associated with connections to this database."_ns;
ReportConn(aHandleReport, aData, conn, pathHead, "schema"_ns, schemaDesc,
SQLITE_DBSTATUS_SCHEMA_USED, &totalConnSize);
}
#ifdef MOZ_DMD if (::sqlite3_memory_used() != int64_t(gSqliteMemoryUsed)) {
NS_WARNING( "memory consumption reported by SQLite doesn't match " "our measurements");
} #endif
}
int64_t other = static_cast<int64_t>(::sqlite3_memory_used() - totalConnSize);
MOZ_COLLECT_REPORT("explicit/storage/sqlite/other", KIND_HEAP, UNITS_BYTES,
other, "All unclassified sqlite memory.");
return NS_OK;
}
//////////////////////////////////////////////////////////////////////////////// //// Service
already_AddRefed<Service> Service::getSingleton() { if (gService) { return do_AddRef(gService);
}
// The first reference to the storage service must be obtained on the // main thread.
NS_ENSURE_TRUE(NS_IsMainThread(), nullptr);
RefPtr<Service> service = new Service(); if (NS_SUCCEEDED(service->initialize())) { // Note: This is cleared in the Service destructor.
gService = service.get(); return service.forget();
}
return nullptr;
}
int Service::AutoVFSRegistration::Init(UniquePtr<sqlite3_vfs> aVFS) {
MOZ_ASSERT(!mVFS); if (aVFS) {
mVFS = std::move(aVFS); return sqlite3_vfs_register(mVFS.get(), 0);
}
NS_WARNING("Failed to register VFS"); return SQLITE_OK;
}
Service::AutoVFSRegistration::~AutoVFSRegistration() { if (mVFS) { int rc = sqlite3_vfs_unregister(mVFS.get()); if (rc != SQLITE_OK) {
NS_WARNING("Failed to unregister sqlite vfs wrapper.");
}
}
}
void Service::unregisterConnection(Connection* aConnection) { // If this is the last Connection it might be the only thing keeping Service // alive. So ensure that Service is destroyed only after the Connection is // cleanly unregistered and destroyed.
RefPtr<Service> kungFuDeathGrip(this);
RefPtr<Connection> forgettingRef;
{
mRegistrationMutex.AssertNotCurrentThreadOwns();
MutexAutoLock mutex(mRegistrationMutex);
for (uint32_t i = 0; i < mConnections.Length(); ++i) { if (mConnections[i] == aConnection) { // Because dropping the final reference can potentially result in // spinning a nested event loop if the connection was not properly // shutdown, we want to do that outside this loop so that we can finish // mutating the array and drop our mutex.
forgettingRef = std::move(mConnections[i]);
mConnections.RemoveElementAt(i); break;
}
}
}
MOZ_ASSERT(forgettingRef, "Attempt to unregister unknown storage connection!");
// Do not proxy the release anywhere, just let this reference drop here. (We // previously did proxy the release, but that was because we invoked Close() // in the destructor and Close() likes to complain if it's not invoked on the // opener event target, so it was essential that the last reference be dropped // on the opener event target. We now enqueue Close() inside our caller, // Release(), so it doesn't actually matter what thread our reference drops // on.)
}
for (uint32_t i = 0; i < connections.Length(); i++) {
RefPtr<Connection> conn = connections[i]; // For non-main-thread owning/opening threads, we may be racing against them // closing their connection or their thread. That's okay, see below. if (!conn->connectionReady()) { continue;
}
constexpr auto shrinkPragma = "PRAGMA shrink_memory"_ns;
if (!conn->operationSupported(Connection::SYNCHRONOUS)) { // This is a mozIStorageAsyncConnection, it can only be used on the main // thread, so we can do a straight API call.
nsCOMPtr<mozIStoragePendingStatement> ps;
DebugOnly<nsresult> rv = conn->ExecuteSimpleSQLAsync(
shrinkPragma, nullptr, getter_AddRefs(ps));
MOZ_ASSERT(NS_SUCCEEDED(rv), "Should have purged sqlite caches");
} elseif (IsOnCurrentSerialEventTarget(conn->eventTargetOpenedOn)) { if (conn->isAsyncExecutionThreadAvailable()) {
nsCOMPtr<mozIStoragePendingStatement> ps;
DebugOnly<nsresult> rv = conn->ExecuteSimpleSQLAsync(
shrinkPragma, nullptr, getter_AddRefs(ps));
MOZ_ASSERT(NS_SUCCEEDED(rv), "Should have purged sqlite caches");
} else {
conn->ExecuteSimpleSQL(shrinkPragma);
}
} else { // We are on the wrong event target, the query should be executed on the // opener event target, so we must dispatch to it. // It's possible the connection is already closed or will be closed by the // time our runnable runs. ExecuteSimpleSQL will safely return with a // failure in that case. If the event target is shutting down or shut // down, the dispatch will fail and that's okay.
nsCOMPtr<nsIRunnable> event = NewRunnableMethod<const nsCString>( "Connection::ExecuteSimpleSQL", conn, &Connection::ExecuteSimpleSQL,
shrinkPragma);
Unused << conn->eventTargetOpenedOn->Dispatch(event, NS_DISPATCH_NORMAL);
}
}
}
int Service::localeCompareStrings(const nsAString& aStr1, const nsAString& aStr2,
Collator::Sensitivity aSensitivity) { // The mozilla::intl::Collator is not thread safe, since the Collator::Options // can be changed.
MutexAutoLock mutex(mMutex);
Collator* collator = getCollator(); if (!collator) {
NS_ERROR("Storage service has no collation"); return 0;
}
if (aSensitivity != mLastSensitivity) {
Collator::Options options{};
options.sensitivity = aSensitivity; auto result = mCollator->SetOptions(options);
if (result.isErr()) {
NS_WARNING("Could not configure the mozilla::intl::Collation."); return 0;
}
mLastSensitivity = aSensitivity;
}
auto result = mozilla::intl::LocaleService::TryCreateComponent<Collator>(); if (result.isErr()) {
NS_WARNING("Could not create mozilla::intl::Collation."); return nullptr;
}
mCollator = result.unwrap();
// Sort in a case-insensitive way, where "base" letters are considered // equal, e.g: a = á, a = A, a ≠ b.
Collator::Options options{};
options.sensitivity = Collator::Sensitivity::Base; auto optResult = mCollator->SetOptions(options);
if (optResult.isErr()) {
NS_WARNING("Could not configure the mozilla::intl::Collation.");
mCollator = nullptr; return nullptr;
}
if (mGrowthIncrement >= 0) { // Ignore errors. In the future, we might wish to log them.
(void)mConnection->SetGrowthIncrement(mGrowthIncrement, ""_ns);
}
// Generally, the callback will be released by CallbackComplete. // However, if for some reason Run() is not executed, we still // need to ensure that it is released here.
NS_ReleaseOnMainThread("AsyncInitDatabase::mCallback", mCallback.forget());
}
if (!readOnly) { // Ensure that SQLITE_OPEN_CREATE is passed in for compatibility reasons.
flags |= SQLITE_OPEN_CREATE;
}
// Apply the shared-cache option.
flags |= shared ? SQLITE_OPEN_SHAREDCACHE : SQLITE_OPEN_PRIVATECACHE;
} else { // Sometimes, however, it's a special database name.
nsAutoCString keyString;
rv = aDatabaseStore->GetAsACString(keyString); if (NS_FAILED(rv) || !keyString.Equals(kMozStorageMemoryStorageKey)) { return NS_ERROR_INVALID_ARG;
}
// Just fall through with nullptr storageFile, this will cause the storage // connection to use a memory DB.
}
// Create connection on this thread, but initialize it on its helper thread.
nsAutoCString telemetryFilename; if (!storageFile) {
telemetryFilename.Assign(kMozStorageMemoryStorageKey);
} else {
rv = storageFile->GetNativeLeafName(telemetryFilename);
NS_ENSURE_SUCCESS(rv, rv);
}
RefPtr<Connection> msc = new Connection( this, flags, Connection::ASYNCHRONOUS, telemetryFilename, /* interruptible */ true, ignoreLockingMode, openNotExclusive);
nsCOMPtr<nsIEventTarget> target = msc->getAsyncExecutionTarget();
MOZ_ASSERT(target, "Cannot initialize a connection that has been closed already");
NS_IMETHODIMP
Service::Observe(nsISupports*, constchar* aTopic, const char16_t*) { if (strcmp(aTopic, "memory-pressure") == 0) {
minimizeMemory();
} elseif (strcmp(aTopic, "xpcom-shutdown-threads") == 0) { // The Service is kept alive by our strong observer references and // references held by Connection instances. Since we're about to remove the // former and then wait for the latter ones to go away, it behooves us to // hold a strong reference to ourselves so our calls to getConnections() do // not happen on a deleted object.
RefPtr<Service> kungFuDeathGrip = this;
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
for (auto& sObserverTopic : sObserverTopics) {
(void)os->RemoveObserver(this, sObserverTopic);
}
SpinEventLoopUntil("storage::Service::Observe(xpcom-shutdown-threads)"_ns,
[&]() -> bool { // We must wait until all the closing connections are // closed.
nsTArray<RefPtr<Connection>> connections;
getConnections(connections); for (auto& conn : connections) { if (conn->isClosing()) { returnfalse;
}
} returntrue;
});
#ifdef DEBUG
nsTArray<RefPtr<Connection>> connections;
getConnections(connections); for (uint32_t i = 0, n = connections.Length(); i < n; i++) { if (!connections[i]->isClosed()) { // getFilename is only the leaf name for the database file, // so it shouldn't contain privacy-sensitive information.
CrashReporter::RecordAnnotationNSCString(
CrashReporter::Annotation::StorageConnectionNotClosed,
connections[i]->getFilename());
printf_stderr("Storage connection not closed: %s",
connections[i]->getFilename().get());
MOZ_CRASH();
}
} #endif
}
return NS_OK;
}
} // namespace mozilla::storage
¤ Dauer der Verarbeitung: 0.5 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.