I am working on a C++ app which internally has some controller objects that are created and destroyed regularly (using new). It is necessary that these controllers register themselves with another object (let's call it controllerSupervisor), and unregister themselves when they are destructed.

The problem I am now facing is happening when I quit the application: as order of destruction is not deterministic, it so happens that the single controllerSupervisor instance is destructed prior to (some) of the controllers themselves, and when they call the unregister method in their destructor, they do so upon an already destructed object.

The only idea I came up with so far (having a big cold, so this may not mean much) is not having the controllerSupervisor as a global variable on the stack, but rather on the heap (i.e. using new). However in that case I do not have a place to delete it (this is all in a 3rd party kind of library).

Any hints/suggestions on what possible options are would be appreciated.

by "3rd party kind of library" do you mean you don't write "main"?
–
David NehmeNov 13 '08 at 20:54

It means that the code for the controller and the supervisor is in a statically linked library. The supervisor is not visible outside this lib, but the controllers are created by the application.
–
StevenNov 13 '08 at 20:57

If you create the controllers, then you can control their destruction, can't you?
–
Null303Nov 13 '08 at 20:59

@Null303, I can destroy them, but if e.g. I have "Object x;" with x.controller being destroyed in x's destructor, I can't be sure whether the supervisor or x will be destroyed first.
–
StevenNov 13 '08 at 21:04

This sounds good. But is it thread-safe? (i.e. let the controllers and the supervisor be in different threads)
–
StevenNov 13 '08 at 20:59

It already looks like an observer pattern, he could use the fact that the supervisor already has a reference to its "observers"
–
Null303Nov 13 '08 at 21:18

It should be thread safe as long as only the Supervisor destroys anybody, the controllers can finish whatever they're working on before getting destroyed, and the controllers can destroy themselves without any further interaction with the Supervisor (to avoid deadlock).
–
Max LybbertNov 13 '08 at 23:09

The order of destruction of automatic variables (that include "normal" local variables that you use in functions) is in the reverse order of their creation. So place the controllerSupervisor at the top.

Order of destruction of globals is also in the reverse of their creation, which in turn depends on the order in which they are defined: Later defined objects are created later. But beware: Objects defined in different .cpp files (translation units) are not guaranteed to created in any defined order.

I think you should consider using it how Mike recommended:

Creation is done by using the singleton pattern (since initialization order of objects in different translation units are not defined) on first use, by returning a pointer to a function-static supervisor object.

The supervisor is normally destructed (using the rules about destruction of statics in functions). controllers deregister using a static function of the supervisor. That one checks whether the supervisor is already destructed (checking a pointer for != 0). If it is, then nothing is done. Otherwise the supervisor is notified.

Since i imagine there could be a supervisor without a controller being connected (and if only temporary), a smart pointer could not be used to destruct the supervisor automatically.

controllerSupervisor is a global variable. The controllers are created using new. Yet the controllerSupervisor is gone before the last of the controllers (which may well be because one of the controllers is part of another global object, but this can't be helped and order is random due to linking)
–
StevenNov 13 '08 at 20:50

@Steven: If you can't rely on stack order, then you need to manage the finalization yourself. Is there a reason controllerSupervisor can't clean up the controllers?
–
eduffyNov 13 '08 at 20:55

There is basically a whole chapter on this topic in Alexandrescu's Modern C++ Design (Chaper 6, Singletons). He defines a singleton class which can manage dependencies, even between singletons themselves.

make the controllerSupervisor a singleton (or wrap it in a singleton object you create for that purpose) that's accessed via a static method that returns a pointer, then the dtors of the registered objects can call the static accessor (which in the case of the application shutdown and the controllerSupervisor has been destroyed will return NULL) and those objects can avoid calling the de-register method in that case.

create the controllerSupervisor on the heap using new and use something like boost::shared_ptr<> to manage its lifetime. Hand out the shared_ptr<> in the singleton's static accessor method.

GNU gcc/g++ provides non portable attributes for types which are very useful. One of these attributes is init_priority that defines the order in which global objects are constructed and, as a consequence, the reverse order in which they get destructed. From the man:

init_priority (PRIORITY)

In Standard C++, objects defined at namespace scope are guaranteed
to be initialized in an order in strict accordance with that of
their definitions _in a given translation unit_. No guarantee is
made for initializations across translation units. However, GNU
C++ allows users to control the order of initialization of objects
defined at namespace scope with the init_priority attribute by
specifying a relative PRIORITY, a constant integral expression
currently bounded between 101 and 65535 inclusive. Lower numbers
indicate a higher priority.
In the following example, `A' would normally be created before
`B', but the `init_priority' attribute has reversed that order:
Some_Class A __attribute__ ((init_priority (2000)));
Some_Class B __attribute__ ((init_priority (543)));
Note that the particular values of PRIORITY do not matter; only
their relative ordering.

Thanks, however I can't delete the supervisor manually (it is created within a static library that does not have what you may think of as a main() routine - so I do not have a place to call supervisor->deleteWhenDone() without breaking some separation in code)
–
StevenNov 13 '08 at 21:06

Everywhere you construct a controller, add control.supervisor.insert(controller). Everywhere you destroy one, add control.erase(controller). You might be able to avoid the control. prefix by adding a global reference to control.supervisor.

The supervisor member of the coordinator won't be destroyed until after the destructor runs, so you're guaranteed that the supervisor will outlive the controllers.

just put a try catch around the unregister call. You don't have to change a lot of code and since the app is already shutting down it is not a big deal. (Or are there other ratifications for the order of shutdown?)

Others have pointed out better designs, but this one is simple. (and ugly)

Well, in the current case the app shuts down just fine, esp. as the memory belonging to the supervisor is still around and error checking code prevents anything really bad to happen. So you are right, there is no actual problem however I am afraid there might some day be and I'd rather fix it now ;)
–
StevenNov 14 '08 at 6:51

That is a good choice - just offering a possibility. The notifications to each other are the right thing to do. When a controller gets notified that the supervisor is going away, it can set the reference to null and not tell it to destroy itself. (as others have said)
–
TimNov 14 '08 at 19:34

Make the cotrol supervisor a singelton.
Make sure that a control constructor gets the supervisor during construction (not afterwords). This guarantees that the control supervisor is fully constructed before the control. Thuse the destructor will be called before the control supervisor destructor.

I think you should avoid the singleton pattern here, you might not know if you will ever need a different group of controllers to be handled by a different supervisor.
–
Null303Nov 13 '08 at 21:41

@Null303 - in that case you probably wouldn't want your controllers to be static/global, so the problem of not being able to control the controller's lifetime goes away.
–
Michael BurrNov 13 '08 at 22:56

@Null303: If you know the that a Control can have different CS then it must be inject into the Control at creation time. This implies that you could potentially pass it as a parameter to the constructor which implies that it is already created (thus the whole problem disappears anyway).
–
Loki AstariNov 13 '08 at 23:24

Actually this did not work, CS gets destructed before the last C instance.
–
StevenNov 14 '08 at 7:17

You must be doing something else or using a non conformant compiler? The above works correctly on conformant compilers and is guaranteed to do so by the standard.
–
Loki AstariNov 14 '08 at 9:50

The C++ standard specifies the order of initialization/destruction when the variables in question all fit in one file ("translation unit"). Anything that spans more than one file becomes non-portable.

I would second the suggestions to have the Supervisor destroy each controller. This is thread safe in the sense that only the Supervisor tells anybody to destroy themselves (nobody destroys themselves on their own), so there is no race condition. You'd also have to avoid any deadlock possibilities (hint: make sure the controllers can destroy themselves once they've been told to without needing anything else from the Supervisor).

It's possible to make this thread safe even if the controller needs to be destroyed before the end of the program (that is, if controllers may be short lived) then they (or somebody else).

First, it may not be a race condition to worry about if I decide to destroy myself and microseconds later the Supervisor decides to destroy me and tells me so.

Second, if you are concerned about this race condition you can fix it by, say, requiring that all destruction requests go through Supervisor. I want to destroy myself I either tell the Supervisor to tell me or I register that intent with the Supervisor. If somebody else -- including the Supervisor -- wants me destroyed, they do that through the Supervisor.

when I read the heading to this question I immediately asked myself "If there was a way that any object could ensure that it was destroyed (destructed?) last, then what would happen if two objects adopted that method?"