a new library Global Object Manager submission interest check
Hi, I have code that deals with problems related to initialization of global objects (singletons). 1. Multi-threading - problems occurring in some singleton patterns based on the fact that local for a function or method static variables are not initialized in a thread safe manner 2. Initialization order - there is no multi-platform/multi-compiler standard way to control the global objects initialization order 3. Static linking of global objects code without need - some patterns result in global objects to be linked in the final executable, even if they are provided from static libraries and the code that uses them is not in use 4. Capability for easy replacement of a global implementation object with a mock object I was struggling with shutdown issues in complex c++ application for years in several different projects. With the solution I come up a few years ago I have no issues in any project I have used it. Please let me know if this is something of interest to the boost community. Note that the boost singleton pattern used for path_locale (boost filesystem path) is having the multi-thread issue I mentioned in point 1. This is a small test that demonstrates the problem: https://github.com/ggeorgiev/boost-filesystem-singletons/blob/master/filesys... Thanks, George
Hi George, On Sat, Jun 7, 2014 at 3:28 PM, George Georgiev wrote:
I have code that deals with problems related to initialization of global objects (singletons). 1. Multi-threading - problems occurring in some singleton patterns based on the fact that local for a function or method static variables are not initialized in a thread safe manner
Did you mean specifically for C++03 / non-C++11 compilers? With C++11, initialization of function-local statics is guaranteed to be thread-safe. For those compilers that do not implement that C++11 requirement (like VC12), how does your solution for thread-safe singleton initialization compare (performance-wise) to, say, http://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html#boo... or other alternatives? In any case, I'm curious to see your solution. Is it on GitHub? [ Side note: Instead of boost::shared_ptr<T>(new T(...)) always prefer boost::make_shared<T>(...). For better performance (one allocation instead of two) and exception-safety. Granted, this is a test snippet, but I also noticed it in Compil. :-) ] Glen
Hi Glen,
It solves other issues, not just the multi-threading problem, which makes
it applicable even for c++11 compilers.
Performance wise it does not include multi-thread primitives which makes it
I would assume many times faster. Arguable it is even faster than the
pattern used in path_locale, because it does not check if the object
is initialized for every call.
It is not in GitHub yet, I will let you know when I publish it.
If I ignore some of the smarts the main concept is very simple:
The access to the global object can be raw, shared or other pointer, say
typedef boost::shared_ptr<GlobalLogger> GlobalLoggerSPtr;
extern GlobalLoggerSPtr gLogger;
then the initialization of the pointer is, say
GlobalLoggerSPtr gLogger = GlobalObjectManager::registerPointer(orderIndex,
gLogger, initFn, uninitFn);
registerPointer method does NOT call the initFn, but instead returns
something arbitrary (say null pointer), but as side effect stores the
address of the global object pointer in a container.
then you start your main (something that I hope I could find a way to
avoid) with constructing a GlobalObjectManager object.
int main(int argc, char **argv)
{
GlobalObjectManager gom(argc, argv);
...
}
The constructor of GlobalObjectManager initialize all the registered
objects pointers in the appropriate order. The destructor uninitializes in
reverse order. For the rest of the code it seems like the initialization
was part of the crt initialization - but we had control over the order. In
case of one global object depends on another all you need to do is to make
sure that their orderIndexes are correctly offseted.
My implementation have some smarts to help for defining the order
relatively easy, and offers a set of helper methods and default
initializers and uninitializes that allow to do the most of the most common
cases relatively simple - for example the code for my logger looks:
GlobalLoggerSPtr gLogger = GlobalObjectManager::sharedPointer(gLogger);
Now when I need access to the logger object all I need to do is:
gLogger->...
Note that the consumer library does not need to know how this pointer is
initilized (no dependencies to the GlobalObjectManager). Also, if my logger
is in a static library that I am linking, but I do not use it - the gLogger
pointer is not going to be initialized, this will result its initialization
to not be added to the GlobalObjectManager and as result all the code
required for the logger will not be added to the binary.
If I would like to replace the logger with a mock, all I need to do is -
static void initilizeGlobalLoggerMock()
{
gLogger = boost::make_shared<GlobalLoggerMock>();
}
GlobalObjectManager::genericRegister(orderIndex - 1,
&initilizeGlobalLoggerMock, NULL);
Note that I am subtracting one from the orderIndex - this will guarantee
that this initialization will be called before the original one. Now
because GlobalObjectManager::sharedPointer does not replace the pointer
unless it is NULL, when I run the code (in test environment) the same code
that uses the pointer will be execute against the mock object without any
change.
Please let me know what do you think?
Thanks,
George
On Sat, Jun 7, 2014 at 4:58 PM, Glen Fernandes
Hi George,
On Sat, Jun 7, 2014 at 3:28 PM, George Georgiev wrote:
I have code that deals with problems related to initialization of global objects (singletons). 1. Multi-threading - problems occurring in some singleton patterns based on the fact that local for a function or method static variables are not initialized in a thread safe manner
Did you mean specifically for C++03 / non-C++11 compilers? With C++11, initialization of function-local statics is guaranteed to be thread-safe.
For those compilers that do not implement that C++11 requirement (like VC12), how does your solution for thread-safe singleton initialization compare (performance-wise) to, say,
http://www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html#boo... or other alternatives?
In any case, I'm curious to see your solution. Is it on GitHub?
[ Side note: Instead of boost::shared_ptr<T>(new T(...)) always prefer boost::make_shared<T>(...). For better performance (one allocation instead of two) and exception-safety. Granted, this is a test snippet, but I also noticed it in Compil. :-) ]
Glen
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
Hi, I added to github https://github.com/ggeorgiev/gom a very raw implementation, that obviously have a lot of work until it gets to the boost standards, but it should be enough to grasp the idea. Thanks, George
On 7 Jun 2014 at 15:28, George Georgiev wrote:
I have code that deals with problems related to initialization of global objects (singletons).
As do we all :)
1. Multi-threading - problems occurring in some singleton patterns based on the fact that local for a function or method static variables are not initialized in a thread safe manner
Fixed in C++ 11, but MSVC is lagging on this.
2. Initialization order - there is no multi-platform/multi-compiler standard way to control the global objects initialization order
Partially fixed in proposed C++ Modules for C++ 17.
3. Static linking of global objects code without need - some patterns result in global objects to be linked in the final executable, even if they are provided from static libraries and the code that uses them is not in use
Huge improvements on this coming in MSVC soon actually. It's always a battle though.
4. Capability for easy replacement of a global implementation object with a mock object
Mmm, I've always wished you could have easy random exception throws for testing without lots of extra macro based plumbing. Hard without modifying the compiler though.
I was struggling with shutdown issues in complex c++ application for years in several different projects. With the solution I come up a few years ago I have no issues in any project I have used it.
I had a system for building a thread aware static global init and deinit DAG nearly two decades ago. It worked beautifully, and I have always wished something like it were standard in C++.
Please let me know if this is something of interest to the boost community.
Definitely yes, but probably not in the form you have already implemented. Here's what I'd need to see for me to feel positive: 1. It needs to provide the same facilities as C++ Modules on older compilers in a way which falls through to C++ Modules on newer compilers. 2. It needs to work correctly as shared libraries are dynamically loaded and unloaded. 3. On top of that base you need thread ordering support such that the DAG can coordinate multiple threads during bootstrap and shutdown. 4. It needs to integrate well with debug tools like valgrind e.g. correctly get valgrind to account for true resource leaks versus fire and forget allocations. If you can do all that I'll volunteer to peer review manage it right now. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
1. It needs to provide the same facilities as C++ Modules on older compilers in a way which falls through to C++ Modules on newer compilers.
I have tested the implementation on several gcc and msvc compilers - and it works. It does not depend on any resent c++ improvements - and outperform the static initialization pattern even in its thread unsafe form.
2. It needs to work correctly as shared libraries are dynamically loaded and unloaded.
This is something that I need to evaluate and add some tests, but I believe it will.
3. On top of that base you need thread ordering support such that the DAG can coordinate multiple threads during bootstrap and shutdown.
I am not sure what this means exactly, Could you please elaborate. Usually what I do is adding my thread pools and other multi-thread related object to the GlobalObjectManager - this way it indirectly helps for the bootstrap and shutdown, but this is without any implicit knowledge about their multi-thread nature (it is not needed).
4. It needs to integrate well with debug tools like valgrind e.g. correctly get valgrind to account for true resource leaks versus fire and forget allocations.
This is done. It works also well with mcvs _CrtDumpMemoryLeaks()
On Sun, Jun 8, 2014 at 7:57 AM, Niall Douglas
On 7 Jun 2014 at 15:28, George Georgiev wrote:
I have code that deals with problems related to initialization of global objects (singletons).
As do we all :)
1. Multi-threading - problems occurring in some singleton patterns based on the fact that local for a function or method static variables are not initialized in a thread safe manner
Fixed in C++ 11, but MSVC is lagging on this.
2. Initialization order - there is no multi-platform/multi-compiler standard way to control the global objects initialization order
Partially fixed in proposed C++ Modules for C++ 17.
3. Static linking of global objects code without need - some patterns result in global objects to be linked in the final executable, even if they are provided from static libraries and the code that uses them is not in use
Huge improvements on this coming in MSVC soon actually. It's always a battle though.
4. Capability for easy replacement of a global implementation object with a mock object
Mmm, I've always wished you could have easy random exception throws for testing without lots of extra macro based plumbing. Hard without modifying the compiler though.
I was struggling with shutdown issues in complex c++ application for years in several different projects. With the solution I come up a few years ago I have no issues in any project I have used it.
I had a system for building a thread aware static global init and deinit DAG nearly two decades ago. It worked beautifully, and I have always wished something like it were standard in C++.
Please let me know if this is something of interest to the boost community.
Definitely yes, but probably not in the form you have already implemented. Here's what I'd need to see for me to feel positive:
1. It needs to provide the same facilities as C++ Modules on older compilers in a way which falls through to C++ Modules on newer compilers.
2. It needs to work correctly as shared libraries are dynamically loaded and unloaded.
3. On top of that base you need thread ordering support such that the DAG can coordinate multiple threads during bootstrap and shutdown.
4. It needs to integrate well with debug tools like valgrind e.g. correctly get valgrind to account for true resource leaks versus fire and forget allocations.
If you can do all that I'll volunteer to peer review manage it right now.
Niall
-- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
On 8 Jun 2014 at 8:47, George Georgiev wrote:
1. It needs to provide the same facilities as C++ Modules on older compilers in a way which falls through to C++ Modules on newer compilers.
I have tested the implementation on several gcc and msvc compilers - and it works. It does not depend on any resent c++ improvements - and outperform the static initialization pattern even in its thread unsafe form.
I think you misunderstand me George. I'm specifically asking for an implementation which emulates the C++ Modules static data system for older compilers, but which disables itself and passes through to C++ Modules on newer compilers. That I think would get people here excited.
2. It needs to work correctly as shared libraries are dynamically loaded and unloaded.
This is something that I need to evaluate and add some tests, but I believe it will.
I would want to see correct handling of exception throws during shared library init and deinit. And running out of memory.
3. On top of that base you need thread ordering support such that the DAG can coordinate multiple threads during bootstrap and shutdown.
I am not sure what this means exactly, Could you please elaborate. Usually what I do is adding my thread pools and other multi-thread related object to the GlobalObjectManager - this way it indirectly helps for the bootstrap and shutdown, but this is without any implicit knowledge about their multi-thread nature (it is not needed).
Well how I handled it was DAG subgroups, so you could tag parts of the DAG as only being possible when a placeholder static init item said it was initialised. Basically I introduced asynchronicity into the static data init system. I'll admit now I retrofitted it, and the retrofit wasn't great. But it persuades me a decent static data init and deinit system ought to be async capable from the beginning. This is what I mean by thread ordering support, though of course i/o is another good use case e.g. initialise a static variable from the contents of a file.
4. It needs to integrate well with debug tools like valgrind e.g. correctly get valgrind to account for true resource leaks versus fire and forget allocations.
This is done. It works also well with mcvs _CrtDumpMemoryLeaks()
Oh good! I remember manually running the MSVC CRT final end of process routines, and then manually exiting the process to get the MSVCRT leak dump to work right. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
I'm specifically asking for an implementation which emulates the C++ Modules static data system for older compilers, but which disables itself and passes through to C++ Modules on newer compilers.
This feels a bit of an overkill for what I am suggesting - after all it is a single template header file (that does not require precompiling).
I would want to see correct handling of exception throws during shared library init and deinit. And running out of memory.
I do not see how what I have will affect the exception handling in any way, but I will add tests for this too.
Well how I handled it was DAG subgroups, so you could tag parts of the DAG as only being possible when a placeholder static init item said it was initialised. Basically I introduced asynchronicity into the static data init system.
I'll admit now I retrofitted it, and the retrofit wasn't great. But it persuades me a decent static data init and deinit system ought to be async capable from the beginning. This is what I mean by thread ordering support, though of course i/o is another good use case e.g. initialise a static variable from the contents of a file.
What I am suggesting is a very simple system to help for resolving the mentioned in the introduction issues. When a global object receives the control to initialize itself, it could apply whatever conditions and whatever source of input. It looks like the DAG system you are referring have a completely different scope of tasks that it was/is solving. I have feeling that we are talking for two completely different things. I have a very raw version of the GOM here: https://github.com/ggeorgiev/gom. If you would like to keep referencing your DAG system it will be helpful if you provide a little bit more information about it. Do you have it published somewhere?
On 9 Jun 2014 at 7:34, George Georgiev wrote:
I'm specifically asking for an implementation which emulates the C++ Modules static data system for older compilers, but which disables itself and passes through to C++ Modules on newer compilers.
This feels a bit of an overkill for what I am suggesting - after all it is a single template header file (that does not require precompiling).
I think it could be done using macros. Think __LINE__ and you're heading the right way to emulating the ordering support in C++ Modules.
I would want to see correct handling of exception throws during shared library init and deinit. And running out of memory.
I do not see how what I have will affect the exception handling in any way, but I will add tests for this too.
Heh. You have some fun coming so.
Well how I handled it was DAG subgroups, so you could tag parts of the DAG as only being possible when a placeholder static init item said it was initialised. Basically I introduced asynchronicity into the static data init system.
I'll admit now I retrofitted it, and the retrofit wasn't great. But it persuades me a decent static data init and deinit system ought to be async capable from the beginning. This is what I mean by thread ordering support, though of course i/o is another good use case e.g. initialise a static variable from the contents of a file.
What I am suggesting is a very simple system to help for resolving the mentioned in the introduction issues. When a global object receives the control to initialize itself, it could apply whatever conditions and whatever source of input. It looks like the DAG system you are referring have a completely different scope of tasks that it was/is solving.
Not really. Data initialisation ordering gets complex very quickly, because it's semantically identical to booting an OS. This is why a really good solution would mirror best in practice with OS booting. A sys V equivalent would work and be simple, but I'm not sure how useful with modern coding patterns. At a certain multinational I used to work for, poor static data initialisation implementation caused multi second process startup times, so this is a very real problem.
I have feeling that we are talking for two completely different things. I have a very raw version of the GOM here: https://github.com/ggeorgiev/gom. If you would like to keep referencing your DAG system it will be helpful if you provide a little bit more information about it. Do you have it published somewhere?
Sadly no. I'm simply preparing you for the kind of stuff which would be asked during peer review. I doubt I'd be alone in wanting to see async in there for example, as initialising static data from storage or network is not uncommon, nor is launching threads and syncing with them during static init. Moreover, the monadic continuations framework being prepared for C++ 17 is likely to become the right and proper way of specifying ordered sequences of stuff, and I see no reason it shouldn't do static data init too. The question would surely be asked "how does this plug into the monadic continuations framework, and if it doesn't, what good rationales are there why not?" Your best rationale right now is because the monadic continuations framework isn't fleshed out yet, but a year from now that will be harder to argue. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
I think it could be done using macros. Think __LINE__ and you're heading the right way to emulating the ordering support in C++ Modules.
Can you give me some code examples - a boost library that already does it?
Not really. Data initialisation ordering gets complex very quickly, because it's semantically identical to booting an OS.
This is why a really good solution would mirror best in practice with OS booting. A sys V equivalent would work and be simple, but I'm not sure how useful with modern coding patterns. At a certain multinational I used to work for, poor static data initialisation implementation caused multi second process startup times, so this is a very real problem.
I can do some things to improve conditional initializations, parallelization, etc. but frankly I do not share your perspective of what a usual c++ application should be doing with singletons. Singletons should be considered evil and should be chosen as pattern very carefully. My goal is not to create utility that will allow people to feel better to abuse the pattern. Moreover, the monadic continuations framework being prepared for C++
17 is likely to become the right and proper way of specifying ordered sequences of stuff, and I see no reason it shouldn't do static data init too. The question would surely be asked "how does this plug into the monadic continuations framework, and if it doesn't, what good rationales are there why not?"
Your best rationale right now is because the monadic continuations framework isn't fleshed out yet, but a year from now that will be harder to argue.
IMHO there is no system that could be perfect in every aspect from day one. Addressing potential misalignment with monadic continuations framework could be prioritised accordingly to their progress. Meanwhile we have a real problem at hand - multi-thread applications written against boost libraries have random crashes from a hard to reproduce and investigate source(s). I think this is the important aspect that boost community should focus on. Later on if we did mistakes and we did not make everything perfect we will learn from them and we will create GOM 2, 3, etc or we will abandon it as a completely bad idea and we will create something completely different.
On 11 Jun 2014 at 9:53, George Georgiev wrote:
I think it could be done using macros. Think __LINE__ and you're heading the right way to emulating the ordering support in C++ Modules.
Can you give me some code examples - a boost library that already does it?
Apologies, I have made a mistake. I had thought that static data is initialised in declaration order of the C++ Module interface file which overrode the C++98 ordering of per-compiland, but looking at N3347 I see no mention of that. It only says that module partitions ought to be initialised on the order of import. If one kept one source file per module, one could therefore enforce static data initialisation ordering, otherwise one is back to square one. Furthermore N4047 says nothing about initialisation order at all other than it is invariant to the code i.e. random i.e. same as now. You can forget about my wish for an emulation of C++ Modules. You cannot emulate no provision. Sorry for wasting your time.
Not really. Data initialisation ordering gets complex very quickly, because it's semantically identical to booting an OS.
This is why a really good solution would mirror best in practice with OS booting. A sys V equivalent would work and be simple, but I'm not sure how useful with modern coding patterns. At a certain multinational I used to work for, poor static data initialisation implementation caused multi second process startup times, so this is a very real problem.
I can do some things to improve conditional initializations, parallelization, etc. but frankly I do not share your perspective of what a usual c++ application should be doing with singletons. Singletons should be considered evil and should be chosen as pattern very carefully. My goal is not to create utility that will allow people to feel better to abuse the pattern.
Meh. I care about what works and scales out beautifully in an easy to use and safe fashion. Much of the time that matches CS theory, sometimes it doesn't.
IMHO there is no system that could be perfect in every aspect from day one. Addressing potential misalignment with monadic continuations framework could be prioritised accordingly to their progress. Meanwhile we have a real problem at hand - multi-thread applications written against boost libraries have random crashes from a hard to reproduce and investigate source(s). I think this is the important aspect that boost community should focus on. Later on if we did mistakes and we did not make everything perfect we will learn from them and we will create GOM 2, 3, etc or we will abandon it as a completely bad idea and we will create something completely different.
All very valid points. If you do decide to present for review, do write up a design rationale page for the docs which explains all the routes you didn't take with reasons why. There is never anything wrong with saying "this is an intentionally simple v1.0 to learn more about the problem", whether of course that will pass review or not is another matter. Niall -- ned Productions Limited Consulting http://www.nedproductions.biz/ http://ie.linkedin.com/in/nialldouglas/
participants (3)
-
George Georgiev
-
Glen Fernandes
-
Niall Douglas