pointersc++14allegro5

0xC0000005: Access violation reading location error in destructor


I have a class that is meant to load image files to bitmaps and keep references to those bitmaps. I want to deallocate these resources at some point later like just before shutting the application down.

So my class (is a singleton) designed like:

class ImageManager
{
public:
   static ImageManager &getInstance();
   ImageManager(ImageManager const&) = delete;
   void operator=(ImageManager const&) = delete;
   void loagImage(char *location);
   ~ImageManager();
private:
   ImageManager();
   ALLEGRO_BITMAP *image = nullptr;
}

There's nothing much in the constructor really. Just loads an add-on related to handling bitmaps. No raw pointers are created.

The loadImage() is implemented as below:

void ImageManager::loadImage(char *location)
{
    if(!location)
    {
       throw std::invalid_argument("Location cannot be null.");
    }
    image = al_load_bitmap(location);
}

The destructor is defined like

ImageManager::~ImageManager()
{
   if(image)
   {
       al_destroy_bitmap(image); // Here I get the access violation exception.
   }
}

The way this class is used in the main.cpp is like:

int main(int argc, char *args[])
{
    ImageManager &imgManager = ImageManager::getInstance();
    imgManager.loadImage("valid/location");

    return 0;
}

If I call the al_destroy_bitmap() in the same function that loads the bitmap, there's no error. It only happens when I try to call it in the destructor.

I'm on Windows 10 using VS17. I saw a number of questions on the same topic but I could not figure out the error using the answers there. I will also link to the two allegro methods here in case you need:

  1. al_load_bitmap()
  2. al_destroy_bitmap()

EDIT:

My getInstance() method is:

ImageManager &ImageManager::getInstance()
{
    static ImageManager instance;
    return instance;
}

EDIT 2:

Exact error is 0xC0000005: Access violation reading location 0xDDDDDDF1.


Solution

  • Allegro 5 doesn't play nice with globals like singletons.

    Things to remember :

    1) Global objects or global calls to static initialization happen before al_init is called. This means calls to allegro functions inside their constructors will fail.

    2) Global statics outlive main and atexit. This means their destructors will run after allegro is typically shutdown. al_install_system hooks atexit for shutting down it's library unless you specifically tell it not to. This means calls to allegro functions like al_destroy_bitmap will fail and segfault if you're lucky.

    You'll have to explictly 'shutdown' your ImageManager instance, before atexit and main exits OR before you call al_uninstall_system.