gecko-dev/dom/quota/ClientDirectoryLockHandle.h
Jan Varga d4251a512a Bug 1957633 - QM: Transition origin access time updates from directory lock to handle-based tracking; r=dom-storage-reviewers,jstutte
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
2025-05-29 00:15:31 +00:00

122 lines
4.1 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/. */
#ifndef DOM_QUOTA_CLIENTDIRECTORYLOCKHANDLE_H_
#define DOM_QUOTA_CLIENTDIRECTORYLOCKHANDLE_H_
#include "mozilla/RefPtr.h"
#include "mozilla/dom/quota/ConditionalCompilation.h"
#include "nsISupportsImpl.h"
namespace mozilla::dom::quota {
class ClientDirectoryLock;
/**
* @class ClientDirectoryLockHandle
* @brief RAII-style wrapper for managing a ClientDirectoryLock.
*
* ClientDirectoryLockHandle is a RAII-style wrapper that manages a
* ClientDirectoryLock created by QuotaManager::OpenClientDirectory.
*
* This class ensures that the associated directory lock remains acquired
* while the handle is in scope and automatically drops it when destroyed.
*
* ## Usage:
* - See QuotaManager::OpenClientDirectory for details on obtaining a
* ClientDirectoryLockHandle.
* - The handle should be retained for as long as access to the directory is
* needed.
*
* ## Threading:
* - Must be used only on the thread that created it, except that it may be
* safely destroyed from another thread after being moved (see also
* Destruction).
* - `AssertIsOnOwningThread()` is primarily used internally to verify correct
* threading, but clients can use it for additional thread-safety checks if
* needed.
*
* ## Destruction:
* - If the lock has already been dropped (e.g., due to move), the destructor
* does nothing.
* - The destructor automatically drops the lock if it is still held.
* - Thus, it is safe to destroy a handle from any thread as long as the handle
* was moved beforehand on the owning thread.
*
* ## Key Features:
* - Move-only: Prevents accidental copies.
* - Implicit boolean conversion to check if the handle holds a valid
* `ClientDirectoryLock`.
* - Easy access to the underlying ClientDirectoryLock using `operator*` and
* `operator->`.
* - Moved-from handles are placed in a well-defined inert state and can be
* safely inspected using `IsInert()` for diagnostic purposes.
*/
class ClientDirectoryLockHandle final {
public:
ClientDirectoryLockHandle();
explicit ClientDirectoryLockHandle(
RefPtr<ClientDirectoryLock> aClientDirectoryLock);
ClientDirectoryLockHandle(const ClientDirectoryLockHandle&) = delete;
ClientDirectoryLockHandle(ClientDirectoryLockHandle&& aOther) noexcept;
~ClientDirectoryLockHandle();
void AssertIsOnOwningThread() const;
ClientDirectoryLockHandle& operator=(const ClientDirectoryLockHandle&) =
delete;
ClientDirectoryLockHandle& operator=(
ClientDirectoryLockHandle&& aOther) noexcept;
explicit operator bool() const;
ClientDirectoryLock* get() const;
ClientDirectoryLock& operator*() const;
ClientDirectoryLock* operator->() const;
bool IsRegistered() const;
void SetRegistered(bool aRegistered);
/**
* Returns true if this handle is in an inert state — either it was
* default-constructed and never assigned a lock, or it was explicitly
* cleared (via move).
*
* This method is primarily intended for use in destructors of objects that
* own a ClientDirectoryLockHandle, to assert that the lock has been properly
* dropped and cleared before destruction.
*
* It is safe to call this method at any time on the owning thread. It may
* also be called from other threads during destruction, under the assumption
* that no other thread is concurrently accessing or modifying the handle.
*
* This method should not be used for control flow or runtime decision
* making.
*/
DIAGNOSTICONLY(bool IsInert() const);
private:
NS_DECL_OWNINGTHREAD
// If new members are added or existing ones are changed, make sure to update
// the move constructor and move assignment operator accordingly to preserve
// correct state during moves.
RefPtr<ClientDirectoryLock> mClientDirectoryLock;
bool mRegistered = false;
};
} // namespace mozilla::dom::quota
#endif // DOM_QUOTA_CLIENTDIRECTORYLOCKHANDLE_H_