I have implemented an singleton based on c++ 11. However the constructor can be called multiple times in some cases.
The class will be compiled to static lib and used by other so lib (more than one so lib). And the system is a multi-thread system (run in the Android HAL level)
/// The .h file:
class Logger { public: /// Return the singleton instance of Logger static Logger& GetInstance() { static Logger s_loggerSingleton; return s_loggerSingleton; } private: /// Constructor Logger(); /// Destructor ~Logger(); }
/// the .cpp file
Logger::Logger() { ALOGE("OfflineLogger create"); } Logger::~Logger() { }
It should be created once eg:
03-21 01:52:20.785 728 4522 E : OfflineLogger create
However I can see it has been created more than once
03-21 01:52:20.785 728 4522 E : OfflineLogger create 03-21 01:52:20.863 728 2274 E : OfflineLogger create 03-21 01:52:20.977 728 2273 E : OfflineLogger create 03-21 01:52:26.370 728 4522 E : OfflineLogger create
Questions:
Anything wrong with my singleton design? Is it a thread-safe issue?
Seems like my singleton works fine in one so scope, but each so lib which includes my singleton will create its own singleton, so that my singleton is no longer “be a singleton”. Is the problem caused from each dynamic linking to new so and the "static variable" becomes "local static"? Is it possible? If so, how to fix?
Well-designed singleton can have only one instance per application. Creating of multiple instances is a mistake in the application design. It might happen in some cases, e.g.: Non thread safe singleton with lazy initialization: several threads are trying to get an instance and creates multiple instances.
By using singletons in your project, you start to create technical debt. Singletons tend to spread like a virus because it's so easy to access them. It's difficult to keep track of where they're used and getting rid of a singleton can be a refactoring nightmare in large or complex projects.
Singletons hinder unit testing: A Singleton might cause issues for writing testable code if the object and the methods associated with it are so tightly coupled that it becomes impossible to test without writing a fully-functional class dedicated to the Singleton.
A singleton (in Java land) wouldn't work as a singleton if a given class is loaded by multiple class-loaders. Since a single class can exist (or can be loaded) in multiple classloaders, it's quite possible to have "multiple" instances of a "supposedly" singleton class for a given JVM instance.
- Anything wrong with my singleton design? Is it a thread-safe issue?
No. Initialization of function local static
variables is guaranteed to be thread-safe by the standard.
- Seems like my singleton works fine in one so scope, but each so lib which include my singleton will create its own singleton, so that my singleton is no longer “be a singleton”. Is the problem caused from each dynamic linking to new so and the "staic veriable" become "local static"? Is it possible? If so, how to fix
That is the correct conclusion.
Instead of creating a static library that contains the implementation of the singleton, make it a dynamic library.
Singletons are hard, especially with shared libraries.
Each of your shared libraries has an independent copy of the non-shared library. Without extra care, each will have a copy of the singleton.
In order to have non-trivial singletons, what I have had to do was
Create an extremely low level library to help with singletons -- call it LibSingleton
Create a singleton template that knows the type of the singleton. It uses magic statics to send a request to the LibSingleton with a size, typeid(T).name()
key, and type-erased construction and destruction code. LibSingleton returns a reference counting RAII object.
LibSingleton uses a shared mutex to either return a previously constructed object that matches the name/size or constructs it. If it constructs the object, it stores the destruction code.
When the last reference-counted handle to the LibSingleton data goes away, LibSingleton runs the destruction code and cleans up the memory in its unordered map.
This permits really simple singletons to be used nearly anywhere.
template<class T> class singleton { public: static T& Instance() { static auto smart_ptr = LibSingleton::RequestInstance( typeid(T).name(), sizeof(T), [](void* ptr){ return ::new( ptr ) T{}; }, [](void* ptr){ static_cast<T*>(ptr)->~T(); } ); if (!smart_ptr) exit(-1); // or throw something return *static_cast<T*>(smart_ptr.get()); } protected: singleton() = default; ~singleton() = default; private: singleton(singleton&&) = delete; singleton& operator=(singleton&&) = delete; };
use looks like:
struct Logger : LibSingleton::singleton<Logger> { friend class LibSingleton::singleton<Logger>; void do_log( char const* sting ) {} private: Logger() { /* ... */ } };
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With