/* * Copyright 2026 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #ifndef MODULES_PORTAL_PORTAL_GUARD_H_ #define MODULES_PORTAL_PORTAL_GUARD_H_ #include #include "api/ref_counted_base.h" #include "rtc_base/synchronization/mutex.h" namespace webrtc { // Ref-counted guard for safe cross-thread portal access from GDBus callbacks. // Callbacks lock the mutex and check the portal pointer before use. // Stop() nulls the pointer under the same mutex, waiting for any in-flight // callback to finish. struct PortalGuard : public RefCountedNonVirtual { Mutex mutex; gpointer portal = nullptr; gpointer AddRefAndGet() { AddRef(); return this; } }; inline void portal_guard_release(gpointer data) { static_cast(data)->Release(); } // RAII lock for PortalGuard. Use ScopedPortalLock for async callbacks // (releases the callback's ref) or ScopedPortalSignalLock for signal // callbacks (ref is owned by the subscription). enum class RefOwnership { kOwnedByCallback, // Async: callback owns the ref, release on unlock. kOwnedBySubscription, // Signal: subscription owns the ref via // GDestroyNotify. }; class ScopedPortalLockBase { public: ScopedPortalLockBase(const ScopedPortalLockBase&) = delete; ScopedPortalLockBase& operator=(const ScopedPortalLockBase&) = delete; gpointer portal() const { return guard_->portal; } protected: ScopedPortalLockBase(gpointer user_data, RefOwnership ownership) : guard_(static_cast(user_data)), ownership_(ownership) { guard_->mutex.Lock(); } ~ScopedPortalLockBase() { guard_->mutex.Unlock(); if (ownership_ == RefOwnership::kOwnedByCallback) guard_->Release(); } private: PortalGuard* guard_; RefOwnership ownership_; }; // For async callbacks. Releases the ref on destruction because async // callbacks fire exactly once and own their ref. class ScopedPortalLock : public ScopedPortalLockBase { public: explicit ScopedPortalLock(gpointer user_data) : ScopedPortalLockBase(user_data, RefOwnership::kOwnedByCallback) {} }; // For signal callbacks. Does not release the ref because the signal // subscription owns it. The ref is released by portal_guard_release // (GDestroyNotify) when the subscription is removed. class ScopedPortalSignalLock : public ScopedPortalLockBase { public: explicit ScopedPortalSignalLock(gpointer user_data) : ScopedPortalLockBase(user_data, RefOwnership::kOwnedBySubscription) {} }; } // namespace webrtc #endif // MODULES_PORTAL_PORTAL_GUARD_H_