tags:

views:

636

answers:

15

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.

+5  A: 

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:

  1. 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.
  2. 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.

Johannes Schaub - litb
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)
Steven
@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?
eduffy
+2  A: 

You could use the Observer pattern. A Controller communicates to it's supervisor that it's being destroyed. And the Supervisor communicates the same to it's child upon destruction.

Take a look at http://en.wikipedia.org/wiki/Observer_pattern

andy.gurin
This sounds good. But is it thread-safe? (i.e. let the controllers and the supervisor be in different threads)
Steven
It already looks like an observer pattern, he could use the fact that the supervisor already has a reference to its "observers"
Null303
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 Lybbert
A: 

You could do any of the following according to the circumstances.

  1. Use the Observer pattern as suggested by gurin. Basically the supervisor informs the controllers that it is going down...
  2. Have the Supervisor "own" the Controllers and be responsible for their destruction when it goes down.
  3. Keep the Controllers in shared_pointers so whoever goes down last will do the real destruction.
  4. Manage both the (smart pointers to the ) controllers and the supervisor on the stack which will allow you to determine the order of destruction
  5. others ...
Andrew Stein
A: 

You could look at using the number of registered controllers as sentinel for actual deletion.

The delete call is then just a request and you have to wait until the controllers deregister.

As mentioned this is one use of the observer pattern.

class Supervisor {
public:
    Supervisor() : inDeleteMode_(false) {}

    void deleteWhenDone() {
     inDeleteMode_ = true;
     if( controllers_.empty()){
      delete this;
     }
    }

    void deregister(Controller* controller) {
     controllers_.erase(
      remove(controllers_.begin(), 
                        controllers_.end(), 
                        controller));
     if( inDeleteMode_ && controllers_.empty()){
      delete this;
     }
    }


private:

    ~Supervisor() {}
    bool inDeleteMode_;
    vector<Controllers*> controllers_;
};

Supervisor* supervisor = Supervisor();
...
supervisor->deleteWhenDone();
maccullt
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)
Steven
A: 

It's not exactly elegant, but you could do something like this:

struct ControllerCoordinator {
    Supervisor supervisor;
    set<Controller *> controllers;

    ~ControllerDeallocator() {
        set<Controller *>::iterator i;
        for (i = controllers.begin(); i != controllers.end(); ++i) {
            delete *i;
        }
    }
}

New global:

ControllerCoordinator control;

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.

Nathan Kitchen
+1  A: 

A couple of suggestions:

  • 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.

Michael Burr
A: 

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.

class CS
{
    public:
        static CS& getInstance()
        {
            static CS  instance;
            return instance;
        }
        void doregister(C const&);
        void unregister(C const&);
    private:
        CS()
        {  // initialised
        }
        CS(CS const&);              // DO NOT IMPLEMENT
        void operator=(CS const&);  // DO NOT IMPLEMENT
 };

 class C
 {
      public:
          C()
          {
              CS::getInstance().doregister(*this);
          }
          ~C()
          {
              CS::getInstance().unregister(*this);
          }
 };
Martin York
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.
Null303
@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 Burr
@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).
Martin York
Actually this did not work, CS gets destructed before the last C instance.
Steven
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.
Martin York
Do you make instance a static member of the method?
Martin York
This is being tested with Visual Studio 2005's compiler. I don't know how compliant this is. And yes, I had instance as a static. However CS is instantiated in a static library, whereas in the application itself another object instantiates a C object ... and this gets destructed after CS.
Steven
@Martin and Mike: The fact that a library uses a global for the supervisor doesn't mean that another system that uses it in the future will do it the same way. Dependency injection can still be used if you will only have one instance.
Null303
+5  A: 

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.

The whole book is highly recommended too BTW.

Alastair
Phoenix singletons for instance...
Nicola Bonelli
Book has been ordered :-)
Steven
A: 

How about having the supervisor take care of destructing the controllers?

David Norman
A: 

Ok, as suggested elsewhere make the supervisor a singleton (or similarily controlled object, i.e., scoped to a session).

Use the appropriate guards (theading, etc) around singleton if required.

// -- client code --
class ControllerClient {
public:
    ControllerClient() : 
     controller_(NULL)
     {
      controller_ = Controller::create();
     }

    ~ControllerClient() {
     delete controller_;
    }
    Controller* controller_;
};

// -- library code --
class Supervisor {
public: 
    static Supervisor& getIt() {  
     if (!theSupervisor ) {
      theSupervisor = Supervisor();
     }
     return *theSupervisor;
    } 

    void deregister(Controller& controller) {
     remove( controller );
     if( controllers_.empty() ) {
      theSupervisor = NULL;
      delete this;
     }  
    }

private:    
    Supervisor() {} 

    vector<Controller*> controllers_;

    static Supervisor* theSupervisor;
};

class Controller {
public: 
    static Controller* create() {
     return new Controller(Supervisor::getIt()); 
    } 

    ~Controller() {
     supervisor_->deregister(*this);
     supervisor_ = NULL;
    }
private:    
    Controller(Supervisor& supervisor) : 
     supervisor_(&supervisor)
     {}
}
maccullt
A: 

While ugly this might be the simplest method:

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)

I prefer the observer pattern as well in this case.

Tim
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 ;)
Steven
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)
Tim
+1  A: 

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.
Nicola Bonelli
A: 

You may use Events to signal destruction of Controllers

Add WaitForMultipleObjects in destructor of Supervisor which will wait until all controllers are destroyed.

In destructor of controllers you can raised controller Exit event.

You need maintain global array of Exit event handles for each controller.

Alien01
this works, but it assumes the app is threaded and also is a bit more complicated than the observer solution.
Tim
It actually is threaded, but I agree that this is a bit overkill, plus it does not exclusively target Windows OS :-)
Steven
A: 

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.

Max Lybbert
This makes sense, however in my case the controllers also get destroyed prior to program termination (they can be quite short-lived).
Steven
A: 

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?"

Paul Mitchell