Boost logo

Boost :

From: Gennadiy Rozental (gennadiy.rozental_at_[hidden])
Date: 2005-08-02 22:10:20


>>>A) class destructors preferably shouldn't be doing anything smart
>>>
>>>I don't understand the reasoning behind this... class destructors exist
>>>so that resources can be disposed of intelligently. If this were valid
>>>then we wouldn't have reference counted smart pointers.
>>
>>Disposing of resource this instance own is one (simple) thing. Meddle with
>>other templates is something completely different (complex) and in most
>>cases would involve function invocations which in turn may throw an
>>exceptions and all the hell break loose. On the other hand if destructors
>>are simple they are independent and all this complex machinery in
>>unnecessary.
>>
> None of the automated cleanup functionality in my original library did
> anything that could result in an exception being thrown. The
> dependency_lifetime used simple reference counting to release the
> instance, and the static_lifetime just used the property of order of
> initialization of static variables to guarantee an order of
> destruction. Neither of these was complex and/or coupling to other
> singletons. I admit that the longevity_lifetime and lifo_lifetime did
> use a rather expensive internal registry, but quite honestly I don't
> think that these lifetimes were as useful as the other two and they were
> merely provided for completeness. Regardless, their automated cleanup
> mechanisms could not throw either.

Whatever your library doing is beside the point here. My position is that
*user* singletons shouldn't do anything smart in destructors that may cause
a exception to be thrown. Therefore there should not be any dependencies in
between singletons during destruction. Therefore order of destruction
shouldn't matter and whatever you are doing is unnecessary.

>>>B) if teardown procedure is not trivial it's part of normal program flow
>>>and should reside within main() bounds
>>>
>>>This forces the highest level code to be aware of every detail that
>>>needs to be cleaned up. What if a library wants to use some sort of
>>>resource manager in the background? Should it force client code to be
>>>aware that said resource manager exists, and make client code
>>>responsible for cleaning it up?
>>>
>>>
>>Client code should be aware about single function teardown() that user
>>should call somewhere during program exit. After that any attempts to
>>access
>>library functionality is invalid. If you want you could even automate it
>>by
>>RAII based manager that does lib_init() in constructor and lib_teardown()
>>in destructor.
>>
> My singleton library gave the client code that option. Client code

You library has nothing to do with this option. Nobody could prevent me from
implementing teardown() method and RAII based manager.

> could explicitly destroy the instance early rather than relying on it
> being destroyed automatically. There was even a lifetime policy
> available which did not do any automated cleanup whatsoever, and a
> cleanup class designed specifically to lock singleton lifetimes to
> specific scopes (like the scope of main).

I do not see how it related.

> I don't see why the client code should be forced to clean up global
> resources manually

It should not necessarily need to be manual. Just explicit.

> when reliable automated methods exist. I think it
I think you way overstate this "reliability". Considering that even Andrei
admit there was in issue in existing standard, plus unclear interaction in
case of shared libraries and issues in MT environments.

> should be allowed to manage such things manually if it wants to, but the
> choice for automated resource management should be available.
>
>>>This seems like a C-oriented way of thinking, and does not scale.
>>>
>>Both statements seems to be born out of thin air. Could you elaborate?
>>
>>
> I was referring to managing all global resources and their dependencies
> at the bottom of main, or in cleanup functions that had to be called by
> main, rather than in destructors. This seems like a poor organizational
> method to use for large projects, especially if such projects have many
> shared resources.

In majority of the cases singleton cleanup shouldn't do anything smart but
resource release. So the wouldn't be any intersingleton dependencies and
order of destruction is not important. And in large projects that are
implemented by combined efforts and or using third party libs you shouldn't
expect everybody follow you organization. Even single "bad sheep" will cause
all the hell break loose.

>>>C) it doesn't seem to be ever portable to rely on order of global
>>>variables destruction in different libraries (dynamic or static)

[...]

>>That was my point that Meyers singletons is all that we need. Anything
>>smarter than that (IOW all these games with enforcing unnatural order of
>>destruction) is just unnecessary "smart".
>>
>>
> I don't understand how my automated methods of destruction are
> unnatural.

Do not garble my words. I mean "unnatural order of destruction". There is
one *automatically* selected/enforced by compiler order of global variables
destruction. Do not mess with it.

> When you can express dependencies between singletons
> directly and cleanly, rather than having to hard code lifetime
> relationships into the bottom of main, it seems more natural to me.

The most natural and safe strategy IMO is *do not think about dependencies
between singletons* in destructors. To express a specific order of
singletons cleanup function invocations order there are means that doesn't
require library support (and this is rarely needed).

>>>D) it's unreasonable to expect all global variables to employ single
>>>manager facility within bound any reasonably big application

[...]

> Perhaps you were not aware when you wrote this that my library does
> offer the option (regardless of the automated cleanup policy used) to
> destroy any singleton manually at any time. As such, it is perfectly
> possible to set up lifetime relationships between singletons from my
> framework, singletons from other libraries, and even global variables.

It's all beyond the point. The fact that it is possible doesn't mean
everybody going to be use it. If I an working in a different department (or
just third party), don't use/know your library and present singleton as a
part of my interface there is no way for you to refer to it from your
singleton destructor, whatever policy you are using to manage you singleton
lifetime.

>>>E) if any teardown procedure fails it's really non-trivial to insure
>>>proper destruction for the rest of globals (if possible at all), while
>>>reporting an error may be impossible/difficult.
>>>
>>Singletons with nontrivial logic in destructors ... should not do this in
>>destructor. And this is not a general problem. It's problem with all
>>global
>>variables that destructed in afterlife (most main() exit)
>>
> If code should not perform non-trivial cleanup in destructors, then when
> should it do it?

In teardown function (or similar). Explicitly called by user.

> Regardless of where the logic is put, if cleanup fails
> there isn't a whole lot you can do.

There is a whole lot you could do. From simple reporting to enabling end
user to resolve the issue somehow.

> You pretty much would just have to
> swallow exceptions or terminate the program anyhow,

So now nobody will notice that you failed to save an important piece of
information and/or failure in destructor will crash whole program preventing
other components cleanup.

> and with destructors
> you are at least guaranteed that cleanup is attempted.

There are different (automated) means to ensure that cleanup is performed.

>>>Many of your other points do not still apply, as there does exist a
>>>scalable mechanism to control destruction order, and the new design does
>>>take multi-threading into consideration.
>>>
>>>
>>Even if it exist (which you did not prove AFAICT) don't go there. "Don't
>>go
>>in language dark corners" experts told recently.
>>
> It isn't a dark corner, it is a well defined part of the standard. I am

Order of global variables destruction in big program linked with modules
built by different compilers/versions/options is a dark corner IMO. I am
sure there are other points affecting the issue.

>>>Just because it is relatively
>>>inefficient to access a singleton shared between threads safely does not
>>>mean that it is a bad approach.
>>>
>>>
>>Just because it is relatively inefficient it makes it completely unusable.
>>MT in many cases are very performance aware and singleton is a performance
>>bottleneck.
>>
>>
> So what would you advise as a mechanism to communicate global
> information between threads efficiently?

What are you talking about? There is one matter of singleton instance access
and completely different matter of its methods/state access.

*>> No need to synch on instance access at all.*
>>>destroys the possibility of delaying creation until a
>>>member function is accessed,
>>>
>>>
>>I do not see how. Third recommended technique in original post cover
>>this.
>>
>>
> I am talking about delaying creation until a member function is called,
> not until instance is called. I suppose this difference may be trivial,
> and is not the main point of my argument.

I do not see a difference at all. To call a method you need an instance
(unless you are talking about taking an address of class method)

>>>and ensures that your references will not
>>>stay up to date if the singleton instance is destroyed.
>>>
>>>
>>Don't keep references and/or don't destroy your global variables in a
>>middle
>>of program execution.
>>
>>
> That places unnecessary restrictions on client code. Code should be
> given many available options, not just one fixed way of thinking about
> and implementing things.

It seems to me that there exist a general trend to restrict a usage of
global variables. And do need to promote bad practices.

>>>It feels like much of your argument is against object oriented
>>>principles in general, and advocates a single flow of execution.
>>>
>>Let's not make unfounded accusations. Where did you see me argue against
>>OOD?
>>
> In promoting the idea that all global resource management be handled at
> the bottom of main,

I promote an idea to minimize global resource management and resorting to
explicit cleanup only if deemed necessary.

> rather than expressing dependencies between global
> resources in a more abstract and scalable way.

Yes. I definitely against expressing dependencies between global resources
in any way.

> Also in advocating
> against the use of destructors to perform cleanup.

Destructors should perform trivial/straightforward cleanup

Gennadiy


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk