/* This file is part of Telegram Desktop, the official desktop application for the Telegram messaging service. For license and copyright information please follow this link: https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL */ #pragma once #include namespace base { namespace details { struct alive_tracker { std::atomic counter = 1; std::atomic dead = false; }; inline alive_tracker *check_and_increment(alive_tracker *tracker) noexcept { if (tracker) { ++tracker->counter; } return tracker; } inline void decrement(alive_tracker *tracker) noexcept { if (tracker->counter.fetch_sub(1) == 0) { delete tracker; } } } // namespace details class has_weak_ptr; template class weak_ptr; class has_weak_ptr { public: has_weak_ptr() = default; has_weak_ptr(const has_weak_ptr &other) noexcept { } has_weak_ptr(has_weak_ptr &&other) noexcept { } has_weak_ptr &operator=(const has_weak_ptr &other) noexcept { return *this; } has_weak_ptr &operator=(has_weak_ptr &&other) noexcept { return *this; } ~has_weak_ptr() { if (auto alive = _alive.load()) { alive->dead.store(true); details::decrement(alive); } } private: template friend class weak_ptr; details::alive_tracker *incrementAliveTracker() const { auto current = _alive.load(); if (!current) { auto alive = std::make_unique(); if (_alive.compare_exchange_strong(current, alive.get())) { return alive.release(); } } ++current->counter; return current; } mutable std::atomic _alive = nullptr; }; template class weak_ptr { public: weak_ptr() = default; weak_ptr(T *value) : _alive(value ? value->incrementAliveTracker() : nullptr) , _value(value) { } weak_ptr(const std::unique_ptr &value) : weak_ptr(value.get()) { } weak_ptr(const std::shared_ptr &value) : weak_ptr(value.get()) { } weak_ptr(const std::weak_ptr &value) : weak_ptr(value.lock().get()) { } weak_ptr(const weak_ptr &other) noexcept : _alive(details::check_and_increment(other._alive)) , _value(other._value) { } weak_ptr(weak_ptr &&other) noexcept : _alive(std::exchange(other._alive, nullptr)) , _value(std::exchange(other._value, nullptr)) { } template < typename Other, typename = std::enable_if_t< std::is_base_of_v && !std::is_same_v>> weak_ptr(const weak_ptr &other) noexcept : _alive(details::check_and_increment(other._alive)) , _value(other._value) { } template < typename Other, typename = std::enable_if_t< std::is_base_of_v && !std::is_same_v>> weak_ptr(weak_ptr &&other) noexcept : _alive(std::exchange(other._alive, nullptr)) , _value(std::exchange(other._value, nullptr)) { } weak_ptr &operator=(T *value) { reset(value); return *this; } weak_ptr &operator=(const std::unique_ptr &value) { reset(value.get()); return *this; } weak_ptr &operator=(const std::shared_ptr &value) { reset(value.get()); return *this; } weak_ptr &operator=(const std::weak_ptr &value) { reset(value.lock().get()); return *this; } weak_ptr &operator=(const weak_ptr &other) noexcept { if (_value != other._value) { destroy(); _alive = details::check_and_increment(other._alive); _value = other._value; } return *this; } weak_ptr &operator=(weak_ptr &&other) noexcept { if (_value != other._value) { destroy(); _alive = std::exchange(other._alive, nullptr); _value = std::exchange(other._value, nullptr); } return *this; } template < typename Other, typename = std::enable_if_t< std::is_base_of_v && !std::is_same_v>> weak_ptr &operator=(const weak_ptr &other) noexcept { if (_value != other._value) { destroy(); _alive = details::check_and_increment(other._alive); _value = other._value; } return *this; } template < typename Other, typename = std::enable_if_t< std::is_base_of_v && !std::is_same_v>> weak_ptr &operator=(weak_ptr &&other) noexcept { if (_value != other._value) { destroy(); _alive = std::exchange(other._alive, nullptr); _value = std::exchange(other._value, nullptr); } return *this; } ~weak_ptr() { destroy(); } T *get() const noexcept { return (_alive && !_alive->dead) ? _value : nullptr; } explicit operator bool() const noexcept { return (_alive && !_alive->dead); } T &operator*() const noexcept { return *get(); } T *operator->() const noexcept { return get(); } void reset(T *value = nullptr) { if (_value != value) { destroy(); _alive = value ? value->incrementAliveTracker() : nullptr; _value = value; } } private: void destroy() noexcept { if (_alive) { details::decrement(_alive); } } details::alive_tracker *_alive = nullptr; T *_value = nullptr; template friend class weak_ptr; }; template inline bool operator==(const weak_ptr &pointer, std::nullptr_t) noexcept { return (pointer.get() == nullptr); } template inline bool operator==(std::nullptr_t, const weak_ptr &pointer) noexcept { return (pointer == nullptr); } template inline bool operator!=(const weak_ptr &pointer, std::nullptr_t) noexcept { return !(pointer == nullptr); } template inline bool operator!=(std::nullptr_t, const weak_ptr &pointer) noexcept { return !(pointer == nullptr); } template < typename T, typename = std::enable_if_t>> weak_ptr make_weak(T *value) { return value; } template < typename T, typename = std::enable_if_t>> weak_ptr make_weak(const std::unique_ptr &value) { return value; } template < typename T, typename = std::enable_if_t>> weak_ptr make_weak(const std::shared_ptr &value) { return value; } template < typename T, typename = std::enable_if_t>> weak_ptr make_weak(const std::weak_ptr &value) { return value; } } // namespace base namespace crl { template struct guard_traits; template struct guard_traits, void> { static base::weak_ptr create(const base::weak_ptr &value) { return value; } static base::weak_ptr create(base::weak_ptr &&value) { return std::move(value); } static bool check(const base::weak_ptr &guard) { return guard.get() != nullptr; } }; template struct guard_traits< T*, std::enable_if_t< std::is_base_of_v>>> { static base::weak_ptr create(T *value) { return value; } static bool check(const base::weak_ptr &guard) { return guard.get() != nullptr; } }; template struct guard_traits< gsl::not_null, std::enable_if_t< std::is_base_of_v>>> { static base::weak_ptr create(gsl::not_null value) { return value.get(); } static bool check(const base::weak_ptr &guard) { return guard.get() != nullptr; } }; } // namespace crl #ifdef QT_VERSION template inline void InvokeQueued(const base::has_weak_ptr *context, Lambda &&lambda) { auto callback = [ guard = base::make_weak(context), lambda = std::forward(lambda) ] { if (guard) { lambda(); } }; QObject proxy; QObject::connect( &proxy, &QObject::destroyed, QCoreApplication::instance(), std::move(callback), Qt::QueuedConnection); } #endif // QT_VERSION