mirror of
https://github.com/mozilla/gecko-dev.git
synced 2025-11-02 17:28:50 +02:00
Before patch: - Access time was updated on first client directory lock registration, which could happen before the lock was actually acquired, and even before storage and origin initialization were complete. As a result, the update could run while the origin directory didn't yet exist, so the access time was not updated at all. - Access time was not updated when LSNG explicitly requested that the origin directory not be created yet. - Access time was also updated on the last client directory lock unregistration, but by that point, a clear or shutdown operation could have been scheduled, meaning the update would run too late and potentially after the origin directory had been deleted. - Client directory opening could sometimes finish before the access time update completed. - Tracking was done only for best-effort persistence types. After patch: - Access time is now updated on the first client directory lock handle registration, which happens after all necessary initialization. While it can still encounter an empty origin directory (due to LSNG optimizations), it's a cleaner and more predictable behavior. - Access time is not updated when LSNG explicitly requests that the origin directory not be created yet. - On last client directory lock handle unregistration, the access time is updated before the lock is dropped. This is slightly different, and even if it were updated after the lock is dropped, that wouldn't help much because dropping is asynchronous, and the given lock is not immediately unregistered. This difference will become irrelevant once we pre-acquire a directory lock for saving origin access time in OpenClientDirectory. The problem where the save operation might run after a clear or shutdown operation is still pending and will be addressed by pre-acquiring a lock for saving origin access time in a follow-up bug. - Client directory opening now always finishes before the access time update completes. While this will be improved later, the current behavior is at least consistent and predictable. Before it becomes critical (e.g., when the accessed flag is written alongside the access time), this ordering will be addressed. - Tracking is now done for all persistence types. (Access time updates are still performed only for best-effort persistence types, but this lays the groundwork for future improvements like updating the last maintenance date.) Additional changes: - Behavior hasn't changed much, but correctness has improved slightly and the logic is now simpler. - The `DirectoryLockImpl::ShouldUpdateLockTable()` method has been removed. It was not very clean and is no longer necessary with the new structure. - Registration has been simplified: instead of maintaining separate hash maps per persistence type or a map with array values keyed by origin, a single hash map using a composite key (persistence type + origin) is now used. - Also addresses an old `XXX` comment about avoiding raw pointers and replacing them with a simple counter. Differential Revision: https://phabricator.services.mozilla.com/D244288
136 lines
4.2 KiB
C++
136 lines
4.2 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||
/* 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 "mozilla/dom/quota/ClientDirectoryLockHandle.h"
|
||
|
||
#include "mozilla/dom/quota/ClientDirectoryLock.h"
|
||
#include "mozilla/dom/quota/DirectoryLock.h"
|
||
#include "mozilla/dom/quota/DirectoryLockInlines.h"
|
||
#include "mozilla/dom/quota/QuotaManager.h"
|
||
|
||
namespace mozilla::dom::quota {
|
||
|
||
ClientDirectoryLockHandle::ClientDirectoryLockHandle() {
|
||
MOZ_COUNT_CTOR(mozilla::dom::quota::ClientDirectoryLockHandle);
|
||
}
|
||
|
||
ClientDirectoryLockHandle::ClientDirectoryLockHandle(
|
||
RefPtr<ClientDirectoryLock> aClientDirectoryLock) {
|
||
aClientDirectoryLock->AssertIsOnOwningThread();
|
||
|
||
mClientDirectoryLock = std::move(aClientDirectoryLock);
|
||
|
||
MOZ_COUNT_CTOR(mozilla::dom::quota::ClientDirectoryLockHandle);
|
||
}
|
||
|
||
ClientDirectoryLockHandle::ClientDirectoryLockHandle(
|
||
ClientDirectoryLockHandle&& aOther) noexcept {
|
||
aOther.AssertIsOnOwningThread();
|
||
|
||
mClientDirectoryLock = std::move(aOther.mClientDirectoryLock);
|
||
|
||
// Explicitly null aOther.mClientDirectoryLock so that aOther appears inert
|
||
// immediately after the move. While RefPtr nulls out its mRawPtr internally,
|
||
// the store may be reordered in optimized builds, possibly occurring only
|
||
// just before RefPtr’s own destructor runs. Without this, the moved-from
|
||
// handle’s destructor may observe a stale non-null value.
|
||
aOther.mClientDirectoryLock = nullptr;
|
||
|
||
mRegistered = std::exchange(aOther.mRegistered, false);
|
||
|
||
MOZ_COUNT_CTOR(mozilla::dom::quota::ClientDirectoryLockHandle);
|
||
}
|
||
|
||
ClientDirectoryLockHandle::~ClientDirectoryLockHandle() {
|
||
// If mClientDirectoryLock is null, this handle is in an inert state — either
|
||
// it was default-constructed or moved.
|
||
//
|
||
// This check is safe here because destruction implies no other thread is
|
||
// using the handle. Any use-after-destroy bugs would indicate a much more
|
||
// serious issue (e.g., a dangling pointer), and should be caught by tools
|
||
// like AddressSanitizer.
|
||
if (mClientDirectoryLock) {
|
||
AssertIsOnOwningThread();
|
||
|
||
mClientDirectoryLock->MutableManagerRef().ClientDirectoryLockHandleDestroy(
|
||
*this);
|
||
|
||
DropDirectoryLock(mClientDirectoryLock);
|
||
}
|
||
|
||
MOZ_COUNT_DTOR(mozilla::dom::quota::ClientDirectoryLockHandle);
|
||
}
|
||
|
||
void ClientDirectoryLockHandle::AssertIsOnOwningThread() const {
|
||
NS_ASSERT_OWNINGTHREAD(ClientDirectoryLockHandle);
|
||
}
|
||
|
||
ClientDirectoryLockHandle& ClientDirectoryLockHandle::operator=(
|
||
ClientDirectoryLockHandle&& aOther) noexcept {
|
||
AssertIsOnOwningThread();
|
||
aOther.AssertIsOnOwningThread();
|
||
|
||
if (this != &aOther) {
|
||
mClientDirectoryLock = std::move(aOther.mClientDirectoryLock);
|
||
|
||
// Explicitly null aOther.mClientDirectoryLock so that aOther appears inert
|
||
// immediately after the move. While RefPtr nulls out its mRawPtr
|
||
// internally, the store may be reordered in optimized builds, possibly
|
||
// occurring only just before RefPtr’s own destructor runs. Without this,
|
||
// the moved-from handle’s destructor may observe a stale non-null value.
|
||
aOther.mClientDirectoryLock = nullptr;
|
||
|
||
mRegistered = std::exchange(aOther.mRegistered, false);
|
||
}
|
||
|
||
return *this;
|
||
}
|
||
|
||
ClientDirectoryLockHandle::operator bool() const {
|
||
AssertIsOnOwningThread();
|
||
|
||
return !!mClientDirectoryLock;
|
||
}
|
||
|
||
ClientDirectoryLock* ClientDirectoryLockHandle::get() const {
|
||
AssertIsOnOwningThread();
|
||
|
||
return mClientDirectoryLock ? mClientDirectoryLock.get() : nullptr;
|
||
}
|
||
|
||
ClientDirectoryLock& ClientDirectoryLockHandle::operator*() const {
|
||
AssertIsOnOwningThread();
|
||
|
||
return *get();
|
||
}
|
||
|
||
ClientDirectoryLock* ClientDirectoryLockHandle::operator->() const {
|
||
AssertIsOnOwningThread();
|
||
|
||
return get();
|
||
}
|
||
|
||
bool ClientDirectoryLockHandle::IsRegistered() const {
|
||
AssertIsOnOwningThread();
|
||
|
||
return mRegistered;
|
||
}
|
||
|
||
void ClientDirectoryLockHandle::SetRegistered(bool aRegistered) {
|
||
AssertIsOnOwningThread();
|
||
|
||
mRegistered = aRegistered;
|
||
}
|
||
|
||
#ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
|
||
|
||
bool ClientDirectoryLockHandle::IsInert() const {
|
||
return !mClientDirectoryLock;
|
||
}
|
||
|
||
#endif
|
||
|
||
} // namespace mozilla::dom::quota
|