Logo Search packages:      
Sourcecode: netams version File versions  Download package

mutex.c

/*************************************************************************
***   Authentication, authorization, accounting + firewalling package
***   Copyright 1998-2002 Anton Vinokurov <anton@netams.com>
***   Copyright 2002-2008 NeTAMS Development Team
***   This code is GPL v3
***   For latest version and more info, visit this project web page
***   located at http://www.netams.com
***
*************************************************************************/
/* $Id: mutex.c,v 1.16 2009-08-01 09:23:55 anton Exp $ */

#include "netams.h"

/////////////////////////////////////////////////////////////////////////////////////////
#if defined(DEBUG) && defined(MUTEX_DEBUG)

#define MAX_THREADS     10

const char* lock_name[2] = { "MUTEX", "RWLOCK" };
const char* lock_type[3] = { "lock", "rwlock", "rdlock" };

#define getLockName(type) lock_name[(type&0x0F)-1]
#define getLockType(type) lock_type[((type&0xF0)>>4)-1]

#define HEAD_FMT  "%9s|%-20s|%10s|%10s|%14s|%13s\n"
#define FMT       "%9p %-20s %10u %10u %14u %13u\n"
typedef struct mutex_unit {
      void *ptr;
      u_char type;
      char *from;
      pthread_t used[MAX_THREADS];
      unsigned lock_success, unlock_success;
      unsigned lock_failed, unlock_failed;
} mutex_unit;

static mutex_unit TMP;

//hash
LHASH *mutex_hash=NULL;
#ifndef WIPE_OPENSSL

unsigned long  MUTEX_hash(mutex_unit *m) {
      return (unsigned long)m->ptr;
}
int MUTEX_cmp(mutex_unit *m1, mutex_unit *m2) {
      return (m1->ptr == m2->ptr)?0:1;
}
void MUTEX_cleanup(mutex_unit *m) {
      aLog(D_INFO, FMT,
            m->ptr, m->from,
            m->lock_success, m->lock_failed,
            m->unlock_success, m->unlock_failed);
      if(m->from) free(m->from);
      free(m);
}

void MUTEX_show(mutex_unit *m, struct cli_def *cli) {
      cli_print(cli, FMT,
            m->ptr, m->from,
            m->lock_success, m->lock_failed,
            m->unlock_success, m->unlock_failed);
}

/* Create the type-safe wrapper functions for use in the LHASH internals */
static IMPLEMENT_LHASH_HASH_FN(MUTEX_hash, mutex_unit *);
static IMPLEMENT_LHASH_COMP_FN(MUTEX_cmp, mutex_unit *);
static IMPLEMENT_LHASH_DOALL_FN(MUTEX_cleanup, mutex_unit *);
static IMPLEMENT_LHASH_DOALL_ARG_FN(MUTEX_show, mutex_unit *, struct cli_def *);
#else
GHashTable *mutex_hash=NULL;
void MUTEX_cleanup(void *_m) {
      mutex_unit * m = (mutex_unit *)_m;
      aLog(D_INFO, FMT,
            m->ptr, m->from,
            m->lock_success, m->lock_failed,
            m->unlock_success, m->unlock_failed);
      if(m->from) free(m->from);
      free(m);
}
void MUTEX_show(void *ptr, void *_m, void *_cli) {
      mutex_unit *m = (mutex_unit *)_m;
      struct cli_def *cli = (struct cli_def *)_cli;
      cli_print(cli, FMT,
            m->ptr, m->from,
            m->lock_success, m->lock_failed,
            m->unlock_success, m->unlock_failed);
}
#endif

mutex_unit *getMutexUnit(void *ptr, u_char flags); //ADD, REMOVE
pthread_t getLock(mutex_unit *m);
#else
void aMutexDebugInit() {};
void aMutexDebugRelease() {};
#endif


#if defined(DEBUG) && defined(MUTEX_DEBUG)
pthread_mutex_t mutex_lock = PTHREAD_MUTEX_INITIALIZER;

void aMutexDebugInit() {
#ifndef WIPE_OPENSSL
      mutex_hash=lh_new(LHASH_HASH_FN(MUTEX_hash), LHASH_COMP_FN(MUTEX_cmp));
#else
      mutex_hash=g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, MUTEX_cleanup);
#endif
      aLog(D_INFO, "Mutexes Debugging Initialized\n");
}

void aMutexDebugRelease(){
      aLog(D_INFO, "Mutexes Debugging Stopped\n");
      aLog(D_INFO, HEAD_FMT,
            "Mutex","Created","Lock success","Lock error","Unlock success","Unlock errors");

#ifndef WIPE_OPENSSL
      /* So to run "MEM_cleanup" against all items in a hash table ... */
      lh_doall(mutex_hash, LHASH_DOALL_FN(MUTEX_cleanup));
      lh_free(mutex_hash);
#else
      g_hash_table_destroy(mutex_hash);
#endif
}

mutex_unit *getMutexUnit(void *ptr, u_char flags) {
      mutex_unit *m;

      TMP.ptr=ptr;

      if(flags == ADD) { //add 2 hash
#ifndef WIPE_OPENSSL
            m=(mutex_unit*)lh_retrieve(mutex_hash, &TMP);
#else
            m=(mutex_unit*)g_hash_table_lookup(mutex_hash, ptr);
#endif
            if(m==NULL) {
                  m=(mutex_unit*)calloc(1,sizeof(mutex_unit));
                  m->ptr=ptr;
#ifndef WIPE_OPENSSL
                  lh_insert(mutex_hash, m);
#else
                  g_hash_table_insert(mutex_hash, ptr, m);
#endif
            }
      } else { //remove from hash
#ifndef WIPE_OPENSSL
            m = (mutex_unit*)lh_delete(mutex_hash, &TMP);
            if(m==NULL) {
                  free(m);
                  m = NULL;
            }
#else
            g_hash_table_remove(mutex_hash, ptr);
#endif
            if(m==NULL) {
                  free(m->from);
                  free(m);
                  m = NULL;
            }
      }
      return m;
}

pthread_t getLock(mutex_unit *m, u_char flags) {
      pthread_t caller=pthread_self();
      pthread_t where=0;
      u_char i;

      for(i=0; i< MAX_THREADS; i++) {
            if(m->used[i] == caller) {
                  where = caller;
                  break;
            }
      }

      if(flags==ADD) {
            if(!where) {
                  for(i=0; i< MAX_THREADS; i++){
                        if(m->used[i]==0) {
                              m->used[i]=caller;
                              return where;
                        }
                  }
                  aLog(D_WARN, "MUTEX Debug: Not enough slots: Increase MAX_THREADS(%u) and recompile.\n", MAX_THREADS);
            }
      } else { //REMOVE
            if(where) m->used[i]=0;
      }

      return where;
}
//library substitution
//mutexes
int   netams_lock_init(const char *file, unsigned line, u_char type, void *lock, void *attr) {
      mutex_unit *m;

      pthread_mutex_lock(&mutex_lock);
      m = getMutexUnit(lock, ADD);
      asprintf(&m->from, "%s:%u", file, line);
      pthread_mutex_unlock(&mutex_lock);

      aDebug(DEBUG_MUTEX, "%s %s %p initialized by thread %s\n", \
            m->from, getLockName(type), lock, pthread_self());

      if(type&LOCK_MUTEX) {
            m->type=LOCK_MUTEX;
            return pthread_mutex_init((pthread_mutex_t*)lock, (const pthread_mutexattr_t*)attr);
      }
      else if(type&LOCK_RWLOCK) {
            m->type=LOCK_RWLOCK;
            return pthread_rwlock_init((pthread_rwlock_t*)lock, (const pthread_rwlockattr_t*)attr);
      }
      return -1;
}

int   netams_lock_destroy(const char *file, unsigned line, u_char type, void *lock) {

      pthread_mutex_lock(&mutex_lock);
//    getMutexUnit(lock, REMOVE);
      pthread_mutex_unlock(&mutex_lock);

      aDebug(DEBUG_MUTEX, "%s %p destroyed by thread %p from %s:%u\n", \
            getLockName(type), lock, pthread_self(), file, line);

      if(type&LOCK_MUTEX) {
            return pthread_mutex_destroy((pthread_mutex_t*)lock);
      } else if(type&LOCK_RWLOCK) {
            return pthread_rwlock_destroy((pthread_rwlock_t*)lock);
      }
      return -1;
}

int   netams_lock_lock(const char *file, unsigned line, u_char type, void *lock) {
      mutex_unit *m;
      pthread_t t, caller;

      caller = pthread_self();

      pthread_mutex_lock(&mutex_lock);
      m = getMutexUnit(lock, ADD);
      t = getLock(m,ADD);
      pthread_mutex_unlock(&mutex_lock);

      if(t != caller) {
            m->lock_success++;
            aDebug(DEBUG_MUTEX, "Thread %p perform %s on %s %p from %s:%u\n", \
                  caller, getLockType(type), getLockName(type), lock, file, line);
      } else {
            m->lock_failed++;
            aLog(D_WARN, "Thread %p tries to %s %s %p locked by %p from %s:%u\n", \
                  pthread_self(), getLockType(type), getLockName(type), lock, t, file, line);
            return -1;
      }
      if(type&LOCK_MUTEX)
            return pthread_mutex_lock((pthread_mutex_t*)lock);
      else if(type&LOCK_RWLOCK_RDLOCK)
            return pthread_rwlock_rdlock((pthread_rwlock_t*)lock);
      else if(type&LOCK_RWLOCK_WRLOCK)
            return pthread_rwlock_wrlock((pthread_rwlock_t*)lock);

      return -1;
}

int   netams_lock_trylock(const char *file, unsigned line, u_char type, void *lock) {
      mutex_unit *m;
      pthread_t t, caller;

      caller = pthread_self();

      pthread_mutex_lock(&mutex_lock);
      m = getMutexUnit(lock, ADD);
      t = getLock(m,ADD);
      pthread_mutex_unlock(&mutex_lock);

      if(t != caller) {
            m->lock_success++;
            aDebug(DEBUG_MUTEX, "Thread %p perform %s on %s  %p from %s:%u\n", \
                  caller, getLockType(type), getLockName(type), lock, file, line);
      } else {
            m->lock_failed++;
            aDebug(DEBUG_MUTEX, "Thread %p %s %p busy for try%s by thread %p from %s:%u\n", \
                  caller, getLockName(type), lock, getLockType(type), t, file, line);
      }

      if(type&LOCK_MUTEX)
            return pthread_mutex_trylock((pthread_mutex_t*)lock);
      else if(type&LOCK_RWLOCK_RDLOCK)
            return pthread_rwlock_tryrdlock((pthread_rwlock_t*)lock);
      else if(type&LOCK_RWLOCK_WRLOCK)
            return pthread_rwlock_trywrlock((pthread_rwlock_t*)lock);

      return -1;
}

int   netams_lock_unlock(const char *file, unsigned line, u_char type, void *lock) {
      mutex_unit *m;
      pthread_t t, caller;

      caller = pthread_self();

      pthread_mutex_lock(&mutex_lock);
      m = getMutexUnit(lock, ADD);
      t = getLock(m,REMOVE);
      pthread_mutex_unlock(&mutex_lock);

      if(t == caller) {
            m->unlock_success++;
            aDebug(DEBUG_MUTEX, "Thread %p perform unlock on %s %p from %s:%u\n", \
                  caller, getLockName(type), lock, file, line);
      } else {
            m->unlock_failed++;
            aLog(D_WARN, "Thread %p tries to unlock %s %p not locked by anything from %s:%u\n", \
                  pthread_self(), getLockName(type), lock, file, line);
            return -1;
      }

      if(type&LOCK_MUTEX)
            return pthread_mutex_unlock((pthread_mutex_t*)lock);
      else if(type&LOCK_RWLOCK_RDLOCK)
            return pthread_rwlock_unlock((pthread_rwlock_t*)lock);
      else if(type&LOCK_RWLOCK_WRLOCK)
            return pthread_rwlock_unlock((pthread_rwlock_t*)lock);

      return -1;
}
#endif

//show mutex
int cShowDebugMutex(struct cli_def *cli, const char *cmd, char **argv, int argc) {
#if defined(DEBUG) && defined(MUTEX_DEBUG)
      cli_print(cli, HEAD_FMT,
            "Mutex","Created","Lock success","Lock error","Unlock success","Unlock errors");

      pthread_mutex_lock(&mutex_lock);
#ifndef WIPE_OPENSSL
      lh_doall_arg(mutex_hash, LHASH_DOALL_ARG_FN(MUTEX_show), cli);
#else
      g_hash_table_foreach(mutex_hash, MUTEX_show, cli);
#endif
      pthread_mutex_unlock(&mutex_lock);
#else
      cli_error(cli, "This command enabled only if compiled with -DDEBUG and -DMUTEX_DEBUG");
#endif
      return CLI_OK;
}
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////

Generated by  Doxygen 1.6.0   Back to index