You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
77 lines
2.9 KiB
77 lines
2.9 KiB
// License: Apache 2.0. See LICENSE file in root directory.
|
|
// Copyright(c) 2022 Intel Corporation. All Rights Reserved.
|
|
|
|
#pragma once
|
|
|
|
#include <mutex> // std::mutex
|
|
#include <memory> // std::shared_ptr
|
|
#include <utility> // std::forward
|
|
|
|
|
|
namespace rsutils {
|
|
|
|
|
|
// Implements a singleton-like object, managing access to a shared_ptr, such that:
|
|
// - defaults to nothing
|
|
// - an instance is created, if it isn't already, on first call to instance()
|
|
// - just like shared_ptr, when the last reference is removed, the underlying object is deleted
|
|
// - keeping the singleton itself does not use a reference (unlike a shared_ptr)
|
|
// - access is thread-safe
|
|
// Unlike an actual singleton:
|
|
// - multiple instances can exist (usage is up to the user)
|
|
//
|
|
// For example, an actual singleton is simple to implement by declaring a global variable:
|
|
// rsutils::shared_ptr_singleton< my_object > G_object; // empty, not created
|
|
// shared_ptr< my_object > obj_ptr = G_object.instance(); // creates the instance
|
|
// obj_ptr.reset(); // destroys the instance
|
|
//
|
|
template< class T >
|
|
class shared_ptr_singleton
|
|
{
|
|
// NOTE, see here:
|
|
// https://stackoverflow.com/questions/20705304/about-thread-safety-of-weak-ptr
|
|
// and:
|
|
// https://en.cppreference.com/w/cpp/memory/weak_ptr/lock
|
|
// lock() is executed atomically. We still "have to make sure not to modify the same weak_ptr from one thread while
|
|
// accessing it from another"
|
|
|
|
std::mutex _mutex;
|
|
std::weak_ptr< T > _weak_ptr;
|
|
|
|
public:
|
|
// Get it or an empty shared_ptr; don't create it
|
|
// (note: lock() is executed atomically; no need to use the mutex)
|
|
//
|
|
std::shared_ptr< T > get() const { return _weak_ptr.lock(); }
|
|
|
|
// Get it, or create it if it doesn't exist
|
|
// Arguments are optional and are passed to the constructor if called
|
|
//
|
|
template< typename... Args >
|
|
std::shared_ptr< T > instance( Args... args )
|
|
{
|
|
// The mutex is important when we have to actually create: without it we'll lock() atomically, then another
|
|
// thread locks, and both get nulls so both try to create.
|
|
std::lock_guard< std::mutex > lock( _mutex );
|
|
std::shared_ptr< T > ptr = _weak_ptr.lock();
|
|
if( ptr )
|
|
{
|
|
// The singleton is still alive and we can just use it
|
|
}
|
|
else
|
|
{
|
|
// First instance ever of T, or the singleton died (all references to the singleton were released), so
|
|
// we have to recreate it
|
|
ptr = std::make_shared< T >( std::forward< Args >( args )... );
|
|
_weak_ptr = ptr;
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
// No is_valid(), operator!(), operator bool(), etc.: the result would be volatile and by the next line could be
|
|
// invalid. Use get() instead and store the pointer until you're done with it!
|
|
};
|
|
|
|
|
|
} // namespace rsutils
|