views:

603

answers:

2

Hi,

I'm working on a simulation that uses multithreading extensively. The thing is that, until now i've never used any mutex objects to protect my data. And the result, is that i'm getting bunch of segmentation faults..

I'm trying to lock/unlock with mutex while : reading/writing but that causes me another segfault :

#0 77D27DD2 ntdll!RtlEnumerateGenericTableLikeADirectory() (C:\Windows\system32\ntdll.dll:??)
#1 00000000 ??() (??:??)

Of course, I created a test project where I applied the lock/unlock thing for a basic situation and it worked, here is a basic example that shows how to deal with Mutex objects using GLFW :

#include <GL/glfw.h>
#include <iostream>
#include <vector>

using namespace std;

vector<int> table;
GLFWmutex th_mutex;
void GLFWCALL Thread_1(void* arg) {


    glfwLockMutex(th_mutex);
    table.pop_back();
    glfwUnlockMutex(th_mutex);
}

void GLFWCALL Thread_2(void* arg) {

    glfwLockMutex(th_mutex);
    table.erase(table.begin());
    glfwUnlockMutex(th_mutex);

}

int main()
{

    bool    running = true;
    GLFWthread th_1, th_2;

    glfwInit();

    if( !glfwOpenWindow( 512, 512, 0, 0, 0, 0, 0, 0, GLFW_WINDOW ) )
    {
        glfwTerminate();
        return 0;
    }

    glfwSetWindowTitle("GLFW Application");

    for(int i = 0;i < 10; i++) {
        table.push_back(i);
    }


    th_mutex = glfwCreateMutex();
    th_1 = glfwCreateThread(Thread_1, NULL);
    th_2 = glfwCreateThread(Thread_2, NULL);


    while(running)
    {

        // exit if ESC was pressed or window was closed
        running = !glfwGetKey(GLFW_KEY_ESC) && glfwGetWindowParam( GLFW_OPENED);
    }

    glfwTerminate();

    return 0;
}

The project, i'm working is bigger, i have 5 threads running on it, and a lot of vectors, maps, queues are accessed in the same time. Somewhere in the code, i tried to do something like :

void GLFWCALL CreateVehicleThread(void* arg) {

     int index = (*static_cast<PulseStateByEntrance*>(arg)).index;
     double t_initial = (*static_cast<PulseStateByEntrance*>(arg)).initial_time;
     double t_final = (*static_cast<PulseStateByEntrance*>(arg)).final_time;
     int pulse = (*static_cast<PulseStateByEntrance*>(arg)).pulse;
     int nb_entrance = (*static_cast<PulseStateByEntrance*>(arg)).nb_entrance;
     int min_time_creation = static_cast<int>(ceil(3600 / pulse));


     while((glfwGetTime() - (*static_cast<PulseStateByEntrance*>(arg)).initial_time)
     < ((*static_cast<PulseStateByEntrance*>(arg)).final_time - (*static_cast<PulseStateByEntrance*>(arg)).initial_time)) {


           double t_elapsed = glfwGetTime() - t_initial;


           if(t_elapsed > min_time_creation) {

                 **int nb_vehicle_per_cycle = static_cast<int>((t_elapsed * pulse)/3600);
                 glfwLockMutex(th_mutex);
                 VehicleManager::CreateVehicles(nb_vehicle_per_cycle, nb_entrance);
                 glfwUnlockMutex(th_mutex);**
                 t_initial = glfwGetTime();

           }

    }


}

The reason why, i'm putting my VehicleManager:CreateVehicles() method between lock/unlock is because in this method there is this line :

VehicleManager::vehicles_.push_back(vehicle);

So i wanted to protect the vector : vehicles_. But, as a result i got that segfault above. And even with :

glfwLockMutex(th_mutex);
VehicleManager::vechicles_.push_back(vehicle);
glfwUnlockMutex(th_mutex);

i got the same segfault.

I hope, i've made my self clear enough for you to understand the nature of my problem. I suppose, not all of you have worked with GLFW that's why i gave you the first basic example so you can understand how mutexes work with this library.

Thanks !

+1  A: 

There is not enough information. But as a guess it looks like mutex was not created correctly (looks like a NULL reference). Find the point where you call glfwCreateMutex() and make sure that a valid value is returned.

Not related to your problem so a side note:

don't do this:

glfwLockMutex(th_mutex);
<ACTION>
glfwUnlockMutex(th_mutex);

This is C style and as a result not exception safe.
You should set up a class so that the lock is called in the constructor and the unlock is called in the destructor. Then have a lock object of this class. Thus the lock and unlock happen normally as part of the object creation derstruction, thus in the presence of exceptions the lock will be correctly released.

The princeiple is RAII and their are a lot of articles on the subject here.

Example:

class MutexLocker
{
    public:
    MutexLocker(GLFWmutex& mutex)
        :m_mutex(mutex)
    {
        glfwLockMutex(m_mutex);
    }
    ~MutexLocker()
    {
        glfwUnlockMutex(m_mutex);
    }
    private:
        GLFWmutex& m_mutex;
};

void myAction()
{
    MutexLocker   lock(th_mutex);

    // Do stuff here:
}
Martin York
Can you help me with a snippet or something? thanks :)
Amokrane
Thanks, i will give it a try !
Amokrane
+1  A: 

You could wrap your containers which will make your code easier to understand:

template<typename T>
class MultithreadedVector
{
public:
    void pushback( T data )
    {
     glfwLockMutex(m_mutex);
     m_container.push_back( data );
     glfwUnlockMutex(m_mutex);
    }
//then similar for erase etc
private:
    std::vector<T> m_container;
    GLFWmutex m_mutex;
};
Patrick
I find it elegant, will try it! Thanks !
Amokrane