Reference Counter and destructor being called twice

lhahn

I am creating a class OpenGLManager, the idea of this class is to manage references to resources allocated in OpenGL (like Vertex Array Objects and Vertex Buffer Objects).

So what I want to do is whenever I allocate a new resource I add the reference into this class, and whenever the object that uses this resource is destroyed (destructor) the reference to this resource is decremented. One problem that I realized is that in some instances the destructor is called twice for an object, and that makes my code break. destructor called twice

What I am basically creating something like a shared_ptr, but instead of pointers I have handlers. Here is the code:

class OpenGLManager
{
public:
    static OpenGLManager& getInstance()
    {
        static OpenGLManager instance; 

        return instance;
    }
    // Increment reference counter
    void incrementVBO(GLuint vbo);
    void incrementVAO(GLuint vao);
    void incrementShaderProgram(GLuint program);
    // Decrement reference counter
    void decrementVAO(GLuint vao);
    void decrementVBO(GLuint vbo);
    void decrementShaderProgram(GLuint program);
private:
    std::unordered_map<GLuint, int> _vbos;
    std::unordered_map<GLuint, int> _vaos;
    std::unordered_map<GLuint, int> _programs;

    OpenGLManager() {};
    OpenGLManager(const OpenGLManager&) = delete;
    void operator=(OpenGLManager const&) = delete;

};
---------------------------------------------------------------
#include "opengl_manager.h"
using std::vector;

void lh::OpenGLManager::incrementShaderProgram(GLuint program)
{
    if (_programs.find(program) == _programs.end())
        _programs[program] = 1;
    else
        _programs[program]++;
}

void lh::OpenGLManager::incrementVAO(GLuint vao)
{
    if (_vaos.find(vao) == _vaos.end())
        _vaos[vao] = 1;
    else
        _vaos[vao]++;

}

void lh::OpenGLManager::incrementVBO(GLuint vbo)
{
    if (_vbos.find(vbo) == _vbos.end())
        _vbos[vbo] = 1;
    else
        _vbos[vbo]++;
}

void lh::OpenGLManager::decrementVAO(GLuint vao)
{
    if (_vaos.count(vao) == 0)
    {
        std::cerr << "Trying to decrement VAO: " << vao << ". Fatal Error." << std::endl;
        exit(EXIT_FAILURE);
    }

    _vaos[vao]--;

    if (_vaos[vao] == 0) // There are no more references
    {
        auto iter = _vaos.find(vao);
        if (iter == _vaos.end())
        {
            std::cerr << "Trying to remove inexistent hashmap (vao) key." << std::endl;
            exit(EXIT_FAILURE);
        }
        _vaos.erase(iter);

        glDeleteVertexArrays(1, &vao);
    }
}

void lh::OpenGLManager::decrementVBO(GLuint vbo)
{
    if (_vbos.count(vbo) == 0)
    {
        std::cerr << "Trying to decrement VBO: " << vbo << ". Fatal Error." << std::endl;
        exit(EXIT_FAILURE);
    }

    _vbos[vbo]--;

    if (_vbos[vbo] == 0) // There are no more references
    {
        auto iter = _vbos.find(vbo);
        if (iter == _vbos.end())
        {
            std::cerr << "Trying to remove inexistent hashmap (vbo) key." << std::endl;
            exit(EXIT_FAILURE);
        }
        _vbos.erase(iter);

        glDeleteBuffers(1, &vbo);
    }
}

void lh::OpenGLManager::decrementShaderProgram(GLuint program)
{
    if (_programs.count(program) == 0)
    {
        std::cerr << "Trying to decrement Program: " << program << ". Fatal Error." << std::endl;
        exit(EXIT_FAILURE);
    }

    _programs[program]--;

    if (_programs[program] == 0) // There are no more references
    {
        auto iter = _programs.find(program);
        if (iter == _programs.end())
        {
            std::cerr << "Trying to remove inexistent hashmap (program) key." << std::endl;
            exit(EXIT_FAILURE);
        }
        _programs.erase(iter);

        glDeleteProgram(program);
    }
} 

This seems to work, except when I add objects on a std::vector with push_back, then the destructor is called twice, which decrements the reference by 2. How can I avoid this? Or is this just a bad design?

EDIT

Implementation of class that uses OpenGLManager

lh::RawModel::RawModel(vector<GLfloat> vertices, vector<GLint> indices, GLenum usage) 
: _vao(-1), _indicesCount(indices.size())
{
    std::cout << "CREATING MYSELF" << std::endl;
    createAndBindVAO();
    storeVerticesDataInVBO(0, vertices, 0, usage);
    storeVerticesDataInVBO(1, vertices, VERTEX_SIZE * sizeof(GLfloat), usage);
    storeIndicesDataInEBO(indices, usage);
    unbindVAO();
}

lh::RawModel::~RawModel()
{
    // Decrement reference counters for opengl resources
    std::cout << "DESTROYING MYSELF" << std::endl;
    lh::OpenGLManager::getInstance().decrementVAO(_vao);
    lh::OpenGLManager::getInstance().decrementVBO(_vbo);
    lh::OpenGLManager::getInstance().decrementVBO(_ebo);
}

const int lh::RawModel::VERTEX_SIZE = 3;

void lh::RawModel::storeIndicesDataInEBO(vector<GLint> indices, GLenum usage)
{
    glGenBuffers(1, &_ebo);
    lh::OpenGLManager::getInstance().incrementVBO(_ebo);

    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, _ebo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLint), &indices[0], usage);
}

void lh::RawModel::storeVerticesDataInVBO(int iAttribute, vector<GLfloat> data, int offset, GLenum usage)
{
    glGenBuffers(1, &_vbo);
    lh::OpenGLManager::getInstance().incrementVBO(_vbo);

    glBindBuffer(GL_ARRAY_BUFFER, _vbo);
    glBufferData(GL_ARRAY_BUFFER, data.size() * sizeof(GLfloat), &data[0], usage);
    glVertexAttribPointer(iAttribute, VERTEX_SIZE, GL_FLOAT, GL_FALSE, 2 * (VERTEX_SIZE * sizeof(float)), (GLvoid*)offset);
    glEnableVertexAttribArray(iAttribute);
    glBindBuffer(GL_ARRAY_BUFFER, 0);
}

void lh::RawModel::createAndBindVAO()
{
    glGenVertexArrays(1, &_vao);
    glBindVertexArray(_vao);
    std::cout << "incrementing vao " << _vao << std::endl;
    lh::OpenGLManager::getInstance().incrementVAO(_vao);
}

int lh::RawModel::getNumberOfIndices()
{
    return _indicesCount;
}

GLuint lh::RawModel::getVAO()
{
    return _vao;
}

void lh::RawModel::unbindVAO()
{
    glBindVertexArray(0);
}

Example of a call that breaks:

models_.push_back(lh::RawModel(vertices, indices, GL_STATIC_DRAW));
Aymar Fisherman

This is expected behavior.

Your design unfortunately isn't safe. If you create a scoped variable, increment it, copy it to a varible outside the scope, then the scoped variable will be destroyed (call decrement) and then, when you finally destroy the other variable, it will decrement a second time, crashing the program. Something like this:

RawModel rawModel;
{
    RawModel temp;
    temp.increment() //calls increment methods.
    rawModel = temp;
} // destructor called once to clean up temp;
// destroy rawModel. destructor called twice to clean up rawModel;

And this is exactly what happens, will instantiace RawModel and then passes it to vector, which copies your variable inside, now you have two instances to call the destructor and break your program.

Options: 1. (Recommended) Use shared_ptr. 2. Define copy constructor and operator =, copying the content from one RawModel to another AND incrementing the count.

Although the second options sounds easier if you have some free time, go with option 1, learn shared_ptr, it'll help you a lot in future programs.

1.
shared_ptr<RawModel> rawModel = make_shared(new RawModel);
rawModel->increment() //increment stuff
vector.push_back(rawModel);
//now it'll work.

2.
//additional RawModel methods:
RawModel(const RawModel& rawModel) {
    // copies everything and if it is initialized increment
}

const RawModel& operator =(const RawModel& other) {
    //same as copy constructur
}

Hope this helps.

Collected from the Internet

Please contact [email protected] to delete if infringement.

edited at
0

Comments

0 comments
Login to comment

Related

From Dev

Why destructor is called twice?

From Dev

Destructor called twice

From Dev

UIViewController Destructor not being called

From Dev

Why is destructor for a form called twice?

From Dev

Why is the destructor of an exception called twice?

From Dev

Why is the destructor in this program called twice?

From Dev

HandleFunc being called twice

From Dev

page being called twice

From Dev

Singleton: how can destructor be called twice?

From Dev

Why is automatic object's destructor called twice?

From Dev

Why is viewDidLoad being called twice

From Dev

Why constructor is being called twice

From Dev

JS function being called twice?

From Dev

ViewPager Fragment being called twice

From Dev

Issue with addAuthStateDidChangeListener being called twice

From Dev

Why constructor is being called twice

From Dev

Callback function being called twice

From Dev

Constructor of Part is being called twice

From Dev

Why is viewDidLoad being called twice

From Dev

Why is the destructor being called three times?

From Dev

Prevent destructor from being called manually

From Dev

Destructor being called more than expected

From Dev

Why is the destructor being called during copy assignmment?

From Dev

Destructor being called more than expected

From Dev

Meteor Router data function being called twice

From Dev

Why is my onCreateView method being called twice?

From Dev

Prevent on('click') from being called twice

From Dev

javascript click being called twice and more

From Dev

Ninject, WebAPI and ExceptionFilterAttribute being called twice