user1182183
user1182183

Reputation:

Mutex can't acquire lock

I have a problem where one of my functions can't aquire the lock on one of the 2 mutexes I use. I did a basic debug in VC++2010 , setting some breakpoints and it seems if anywhere the lock is acquired, it does get unlocked.

The code that uses mutexes is as follow:

#define SLEEP(x) { Sleep(x); }
#include<windows.h>

    void Thread::BackgroundCalculator( void *unused ){
    while( true ){
        if(MUTEX_LOCK(&mutex_q, 5) == 1){
            if(!QueueVector.empty()){
//cut
                MUTEX_UNLOCK(&mutex_q);
                    //cut
                while(MUTEX_LOCK(&mutex_p,90000) != 1){}
                //cut
                MUTEX_UNLOCK(&mutex_p);
            }

        }
        SLEEP(25);
    }
}

Then somwhere else:

PLUGIN_EXPORT void PLUGIN_CALL
    ProcessTick(){
    if(g_Ticked == g_TickMax){
        if(MUTEX_LOCK(&mutex_p, 1) == 1){
            if(!PassVector.empty()){
                PassVector.pop();
            }
            MUTEX_UNLOCK(&mutex_p);
        }
        g_Ticked = -1;
    }
    g_Ticked += 1;
}

static cell AMX_NATIVE_CALL n_CalculatePath( AMX* amx, cell* params ){
    if(MUTEX_LOCK(&mutex_q,1) == 1){
        QueueVector.push_back(QuedData(params[1],params[2],params[3],amx));
        MUTEX_UNLOCK(&mutex_q);
        return 1;
    }
    return 0;
}

init:

PLUGIN_EXPORT bool PLUGIN_CALL Load( void **ppData ) {
    MUTEX_INIT(&mutex_q);
    MUTEX_INIT(&mutex_p);
    START_THREAD( Thread::BackgroundCalculator, 0);
    return true;
}

Some variables and functions:

int MUTEX_INIT(MUTEX *mutex){
    *mutex = CreateMutex(0, FALSE, 0);
    return (*mutex==0);
}

int MUTEX_LOCK(MUTEX *mutex, int Timex = -1){
    if(WaitForSingleObject(*mutex, Timex) == WAIT_OBJECT_0){
        return 1;
    }
    return 0;
}
int MUTEX_UNLOCK(MUTEX *mutex){
    return ReleaseMutex(*mutex);
}

MUTEX mutex_q = NULL;
MUTEX mutex_p = NULL;

and defines:

#   include <process.h>
#   define OS_WINDOWS
#   define MUTEX HANDLE
#   include <Windows.h>
#   define EXIT_THREAD() { _endthread(); }
#   define START_THREAD(a, b) { _beginthread( a, 0, (void *)( b ) ); }

Thread header file:

#ifndef __THREAD_H
#define __THREAD_H

class Thread{
    public:
                                    Thread                      ( void );
                                   ~Thread                      ( void );
    static void                     BackgroundCalculator        ( void *unused );

};

#endif

Well I can't seem to find the issue. After debugging I wanted to "force" aquiring the lock by this code (from the pawn abstract machine):

if (strcmp("/routeme", cmdtext, true) == 0){
    new fromnode = NearestPlayerNode(playerid);
    new start = GetTickCount();
    while(CalculatePath(fromnode,14,playerid+100) == 0){
        printf("0 %d",fromnode);
    }
    printf("1 %d",fromnode);
    printf("Time: %d",GetTickCount()-start);
    return 1;
}

but it keeps endless going on, CalculatePath calls static cell AMX_NATIVE_CALL n_CalculatePath( AMX* amx, cell* params )

That was a bit of surprise. Does anyone maybe see a mistake?

If you need the full source code it is available at:

http://gpb.googlecode.com/files/RouteConnector_174alpha.zip

Extra info: PLUGIN_EXPORT bool PLUGIN_CALL Load gets only executed at startup.

static cell AMX_NATIVE_CALLs get only executed when called from a vitrual machine

ProcessTick() gets executed every process tick of the application, after it has finished its own jobs it calls this one in the extensions.

For now I only tested the code on windows, but it does compile fine on linux.

Edit: removed linux code to shorten post.

Upvotes: 0

Views: 3943

Answers (2)

user319799
user319799

Reputation:

From what I see your first snippet unlocks mutex based on some condition only, i.e. in pseudocode it is like:

mutex.lock ():
if some_unrelated_thing:
    mutex.unlock ()

As I understand your code, this way the first snippet can in principle lock and then never unlock.

Another potential problem is that your code is ultimately exception-unsafe. Are you really able to guarantee that no exceptions happen between lock/unlock operations? Because if any uncaught exception is ever thrown, you get into a deadlock like described. I'd suggest using some sort of RAII here.

EDIT:

Untested RAII way of performing lock/unlock:

struct Lock
{
  MUTEX&  mutex;
  bool    locked;

  Lock (MUTEX& mutex)
    : mutex (mutex),
      locked (false)
  { }

  ~Lock ()
  { release (); }

  bool acquire (int timeout = -1)
  {
    if (!locked && WaitForSingleObject (mutex, timeout) == WAIT_OBJECT_0)
      locked = true;
    return locked;
  }

  int release ()
  {
    if (locked)
      locked = ReleaseMutex (mutex);
    return !locked;
  }
};

Usage could be like this:

{
  Lock  q (mutex_q);
  if (q.acquire (5)) {
      if (!QueueVector.empty ()) {
          q.release ();
          ...
      }
  }
}

Note that this way ~Lock always releases the mutex, whether you did that explicitly or not, whether the scope block exited normally or due to an uncaught exception.

Upvotes: 2

Tudor
Tudor

Reputation: 62459

I'm not sure if this is intended behavior, but in this code:

void Thread::BackgroundCalculator( void *unused ){
while( true ){
    if(MUTEX_LOCK(&mutex_q, 5) == 1){
        if(!QueueVector.empty()){
            //cut
            MUTEX_UNLOCK(&mutex_q);
            //cut
            while(MUTEX_LOCK(&mutex_p,90000) != 1){}
            //cut
            MUTEX_UNLOCK(&mutex_p);
        }
    }
    SLEEP(25);
}

if the QueueVector.empty is true you are never unlocking mutex_q.

Upvotes: 2

Related Questions