Jump to content
  • Advertisement
Sign in to follow this  
trajectorymodifier

object lifetime issues

This topic is 3750 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

originally working with SDL on Mac, I've found a strange anomaly after importing my project to Dev-Cpp under a BootCamp Windows XP. for my game subsystems (that mostly wrap SDL) I use singletonized classes (SingletonHolder<Subsystem>), which sit in a namespace and are identified as references. some relevant code:
// singletonholder

template <typename T>
class SingletonHolder
{
public:
  static T& Instance() {
    static T *pInst = new T;
    return pInst;
  }

private:
  SingletonHolder();
  SingletonHolder(const SingletonHolder &rhs);
  SingletonHolder& operator=(const SingletonHolder &rhs);
  ~SingletonHolder();
};

// core.hpp
class Core
{
public:
  Core(Uint32 flags = SDL_INIT_EVERYTHING);
  ~Core();
};

// core.cpp
Core::Core(Uint32 flags) {
  atexit(SDL_Quit);
  if(::SDL_Init(flags) < 0) {
    exit(1);
  }
  LJ::LOG << "Core was succesfully initialized.";
}

Core::~Core() {
  LJ::LOG << "Core is shutting down";
}

// framework.hpp
namespace LJ
{
  extern class Log LOG; // not a singleton

  extern class Core &CORE;  
};

// framework.cpp
namespace LJ
{
  Log LOG("system.log");

  Core &CORE(SingletonHolder<Core>::Instance());
};


Now, the anomaly is that in the windows version the singletonized classes' destructors are never called (their shutdown isn't logged) - whereas the OSX version works like a charm. This had been like that since forever, and didn't seem to cause much trouble. The more severe problem is that, for a while now, the program will NOT execute twice in the same windows session. It will crash on second execution, so hard, that there's no way to shut it down (after some random time and amount of punching it, it will go away, or will it?), and will likely crash DevCpp. Oh, by the way, the first time this happened was after I've introduced some compilation switches to make it easier to use the same source for Mac and Win, such as:
#if defined(__APPLE__)

#include	"SDL.h"
#include	"SDL_opengl.h"
#include	"SDL_image/SDL_image.h"
#include	"SDL_mixer/SDL_mixer.h"

#elif defined(__WIN32__)

#include	"SDL/SDL.h"
#include	"SDL/SDL_opengl.h"
#include	"SDL/SDL_image.h"
#include	"SDL/SDL_mixer.h"

#endif

Now this doesn't make sense at all. Or does it? My two cents would be that this has to do with SDL not being shut down properly (so I'm not saying the OSX version is perfect, maybe the OS takes care of whatever is in the background). So first, I've got rid of atexit and called SDL_Quit() from ~Core(), then tried to do without the singletons, but the problem remained the same. As far as I know, other SDL applications (say, G++ ones rather than VC++) don't have this sort of problem. Does anybody know what's causing this? Actually, as I'm writing this, I get the idea that the windows version might be shutting LJ::LOG down early, then, when the other subsystems' destructor want to call it, it's not there, they crash, and SDL_Quit is never called? Could this be it?

Share this post


Link to post
Share on other sites
Advertisement
I do not see a call to delete pInst. So why do you expect the destructor to be called? You don't have a Singleton either, because I see no code to prevent the creation of multiple objects.

In my opinion, your code depends too much on global objects. This creates hard to diagnose order of creation and destruction issues. I am guessing that you have even more global objects, otherwise the SingletonHolder<> class is over-engineered.

Another possible problem is that you don't prevent multiple Core objects being created. It is possible that somewhere in your code something creates a copy of the Core object. You should disable the copy constructor and operator=().

While a good argument can be made that you should really remove all globals, most practical people will strike a balance. A log isn't too bad as a global.

Consider: why is "core" global? Who needs global access to it? Since it has no interface, the answer is nobody. The inly reason I can think of as to why it might be global is because you need SDL initialised for the constructors of some other global classes. This is bad. In my opinion, only the most trivial code should run before main(), like a log constructor.

If you could give us an overview of your other global or SingletonHolder<> classes, perhaps we could give you some more advice.

Share this post


Link to post
Share on other sites
thank you for your answer, Rip-off.

sorry I opted for a terser version, there is more to the whole code all right:
- copy prevention: actually both the SingletonHolder and the subsystems are derived privately from the following class:

class NonCopyable
{
public:
NonCopyable() {};
~NonCopyable() {};
protected:
NonCopyable(const NonCopyable& rhs);
NonCopyable& operator=(const NonCopyable& rhs);
};







- pInst deletion -- I made a mistake in the SingletonHolder code's copy. Here's the original:

template <typename T>
class SingletonHolder : private NonCopyable
{
public:
// structors
static T& Inst() {
static std::auto_ptr<T> pInst(new T);
return *pInst;
}

private:
// disabled
SingletonHolder();
~SingletonHolder();
};







- as for the other subsystems, you're right -- they don't need to access each other as global. there are the following exceptions:
1, the Log, which every other subsystem does access.
2, the OpenGL based Graphics subsystem has a call for the TextureManager to reload all textures if it was reinitialized.
3, the Input subsys queries the screen size from Graphics

The subsystems aren't constructing/destructing each other. The resource managers don't do a thing on creation (not even log), and definitely nothing to do with SDL.

other subsystems, in order of declaration and definition (I know, it doesnt necessarily correspond to creation):
- Core:

//==============================================================================
// Core
//==============================================================================
Core::Core(uint flags) {
if(InitSDL_(flags) == false) {
LJ::LOG << LOG_FATAL << "Core initialization failed: " << ::SDL_GetError();
LJ::LOG.Record();
}

const SDL_version *vLink(::SDL_Linked_Version());
StringT strLink(LJ::GetVersionString(vLink));

LJ::LOG << "Core created (SDL " << strLink << ").";
LJ::LOG.Record();
}

Core::~Core() {
::SDL_Quit();

LJ::LOG << "Core is exiting.";
LJ::LOG.Record();
}

//------------------------------------------------------------------------------
// InitSDL_()
// desc: initializes SDL's given subsystems
//------------------------------------------------------------------------------
bool Core::InitSDL_(uint flags) {
if(::SDL_Init(flags) < 0) {
return false;
}
return true;
}

// has one more method -- SetCaption() -- could be anywhere else.






- AppTimer: constructor logs the fact of construction. also uses SDL_GetTicks(). Destructor logs fact of destruction.

- Graphics: constructor logs the fact of the construction. also uses SDL_ShowCursor(). After construction, the client has to initialize it manually (member method) to resolution, color depth and mode (windowed / full screen). This calls the Texturemanager's method to reload all textures (if succesfully initialized). (the Texturemanager is a policy template class specialized for Texture type objects, which has the resource specific functionality, loads a textre file using IMG_LoadImage().) Destructor logs fact of destruction.

- Input: constructor logs the fact of construction. uses SDL_WM_GrabInput(). calls for an update, which in turn uses SDL_GetKeyState() and SDL_GetMouseState(). Destructor logs the fact of destruction.

- Mixer: logs fact of successful construction, also calling Mix_Linked_Version(). After construction, the client has to initialize it manually (member method) to frequency, number of output and mixing channels, sound format. only uses the Log. Destructor logs the fact of destruction.

what's your suggestion to use instead of global objects? is there any way I can prevent multiple instantiation but hard-coding those classes as singletons? where should I put SDL_Init and SDL_Quit? Shall I initialize SDL subsystem by subsystem, delegated to the corresponding subsystems?


In the meantime I've revised the code, the Log is also a singleton now - but the problem remains the same, plus from this time on nothing gets logged in the Windows version. The MacOSX version works on as expected (although there's a stupidd time format change in the log after the subsystems get initialized, and I definitely can't put it anywhere...). Also, the application used to work, logged perfectily, could start it more than once in a session -- back before the conditional compiling statements, when AppTimer wasn't a separate class, rather than a part of the game loop, and when the logger was just a global object rather than a singleton.


================================================================================
Sat Jul 12 13:34:51 2008 Logging has started.
Sat Jul 12 13:34:52 2008 Core created (SDL v1.2.13).
Sat Jul 12 13:34:52 2008 Timer created.
Sat Jul 12 13:34:52 2008 Graphics created.
Sat Jul 12 13:34:52 2008 Input created.
Sat Jul 12 13:34:52 2008 Mixer created (SDL_Mixer v1.2.8).
12/07/2008 13:34:52 opting for video mode: 800x600@0
12/07/2008 13:34:52 SUCCESS: Graphics initialized. video mode is 800x600@32
12/07/2008 13:34:52 SUCCESS: Mixer was successfully initialized.
12/07/2008 13:35:48 Mixer is exiting.
12/07/2008 13:35:48 Input is exiting.
12/07/2008 13:35:48 Graphics is exiting.
12/07/2008 13:35:48 Core is exiting.
12/07/2008 13:35:48 Logging has stopped.


[Edited by - trajectorymodifier on July 12, 2008 8:34:40 AM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!