272 lines
6.3 KiB
C
272 lines
6.3 KiB
C
#define MODULE_LOG_PREFIX "lock"
|
|
|
|
#include "globals.h"
|
|
#include "oscam-lock.h"
|
|
#include "oscam-time.h"
|
|
|
|
extern char *LOG_LIST;
|
|
|
|
/**
|
|
* creates a lock
|
|
**/
|
|
void cs_lock_create(const char *n, CS_MUTEX_LOCK *l, const char *name, uint32_t timeout_ms)
|
|
{
|
|
memset(l, 0, sizeof(CS_MUTEX_LOCK));
|
|
l->timeout = timeout_ms / 1000;
|
|
l->name = name;
|
|
SAFE_MUTEX_INIT_R(&l->lock, NULL, n);
|
|
__cs_pthread_cond_init(n, &l->writecond);
|
|
__cs_pthread_cond_init(n, &l->readcond);
|
|
#ifdef WITH_MUTEXDEBUG
|
|
cs_log_dbg(D_TRACE, "lock %s created", name);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* creates a lock
|
|
**/
|
|
void cs_lock_create_nolog(const char *n, CS_MUTEX_LOCK *l, const char *name, uint32_t timeout_ms)
|
|
{
|
|
memset(l, 0, sizeof(CS_MUTEX_LOCK));
|
|
l->timeout = timeout_ms / 1000;
|
|
l->name = name;
|
|
SAFE_MUTEX_INIT_NOLOG_R(&l->lock, NULL, n);
|
|
__cs_pthread_cond_init(n, &l->writecond);
|
|
__cs_pthread_cond_init(n, &l->readcond);
|
|
#ifdef WITH_MUTEXDEBUG
|
|
cs_log_dbg(D_TRACE, "lock %s created", name);
|
|
#endif
|
|
}
|
|
|
|
void cs_lock_destroy(const char *pn, CS_MUTEX_LOCK *l)
|
|
{
|
|
if(!l || !l->name || l->flag) { return; }
|
|
|
|
cs_rwlock_int(pn, l, WRITELOCK);
|
|
#ifdef WITH_DEBUG
|
|
const char *old_name = l->name;
|
|
#endif
|
|
l->name = NULL; // No new locks!
|
|
cs_rwunlock_int(pn, l, WRITELOCK);
|
|
|
|
// Do not destroy when having pending locks!
|
|
int32_t n = (l->timeout / 10) + 2;
|
|
while((--n > 0) && (l->writelock || l->readlock)) { cs_sleepms(10); }
|
|
|
|
cs_rwlock_int(pn, l, WRITELOCK);
|
|
l->flag++; // No new unlocks!
|
|
cs_rwunlock_int(pn, l, WRITELOCK);
|
|
|
|
#ifdef WITH_DEBUG
|
|
if(!n && old_name != LOG_LIST)
|
|
{ cs_log("WARNING lock %s destroy timed out.", old_name); }
|
|
#endif
|
|
|
|
pthread_mutex_destroy(&l->lock);
|
|
pthread_cond_destroy(&l->writecond);
|
|
pthread_cond_destroy(&l->readcond);
|
|
#ifdef WITH_MUTEXDEBUG
|
|
cs_log_dbg(D_TRACE, "lock %s destroyed", l->name);
|
|
#endif
|
|
}
|
|
|
|
void cs_rwlock_int(const char *n, CS_MUTEX_LOCK *l, int8_t type)
|
|
{
|
|
struct timespec ts;
|
|
int8_t ret = 0;
|
|
|
|
if(!l || !l->name || l->flag)
|
|
{ return; }
|
|
|
|
SAFE_MUTEX_LOCK_R(&l->lock, n);
|
|
|
|
add_ms_to_timespec(&ts, l->timeout * 1000);
|
|
ts.tv_nsec = 0; // 100% resemble previous code, I consider it wrong
|
|
if(type == WRITELOCK)
|
|
{
|
|
l->writelock++;
|
|
// if read- or writelock is busy, wait for unlock
|
|
if(l->writelock > 1 || l->readlock > 0)
|
|
{ ret = pthread_cond_timedwait(&l->writecond, &l->lock, &ts); }
|
|
}
|
|
else
|
|
{
|
|
l->readlock++;
|
|
// if writelock is busy, wait for unlock
|
|
if(l->writelock > 0)
|
|
{ ret = pthread_cond_timedwait(&l->readcond, &l->lock, &ts); }
|
|
}
|
|
|
|
if(ret > 0)
|
|
{
|
|
// lock wasn't returned within time, assume locking thread to
|
|
// be stuck or finished, so enforce lock.
|
|
l->writelock = (type == WRITELOCK) ? 1 : 0;
|
|
l->readlock = (type == WRITELOCK) ? 0 : 1;
|
|
#ifdef WITH_DEBUG
|
|
if(l->name != LOG_LIST)
|
|
{ cs_log("WARNING lock %s (%s) timed out.", l->name, (type == WRITELOCK) ? "WRITELOCK" : "READLOCK"); }
|
|
#endif
|
|
}
|
|
|
|
SAFE_MUTEX_UNLOCK_R(&l->lock, n);
|
|
#ifdef WITH_MUTEXDEBUG
|
|
//cs_log_dbg(D_TRACE, "lock %s locked", l->name);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void cs_rwlock_int_nolog(const char *n, CS_MUTEX_LOCK *l, int8_t type)
|
|
{
|
|
struct timespec ts;
|
|
int8_t ret = 0;
|
|
|
|
if(!l || !l->name || l->flag)
|
|
{ return; }
|
|
|
|
SAFE_MUTEX_LOCK_NOLOG_R(&l->lock, n);
|
|
|
|
add_ms_to_timespec(&ts, l->timeout * 1000);
|
|
ts.tv_nsec = 0; // 100% resemble previous code, I consider it wrong
|
|
if(type == WRITELOCK)
|
|
{
|
|
l->writelock++;
|
|
// if read- or writelock is busy, wait for unlock
|
|
if(l->writelock > 1 || l->readlock > 0)
|
|
{ ret = pthread_cond_timedwait(&l->writecond, &l->lock, &ts); }
|
|
}
|
|
else
|
|
{
|
|
l->readlock++;
|
|
// if writelock is busy, wait for unlock
|
|
if(l->writelock > 0)
|
|
{ ret = pthread_cond_timedwait(&l->readcond, &l->lock, &ts); }
|
|
}
|
|
|
|
if(ret > 0)
|
|
{
|
|
// lock wasn't returned within time, assume locking thread to
|
|
// be stuck or finished, so enforce lock.
|
|
l->writelock = (type == WRITELOCK) ? 1 : 0;
|
|
l->readlock = (type == WRITELOCK) ? 0 : 1;
|
|
#ifdef WITH_DEBUG
|
|
if(l->name != LOG_LIST)
|
|
{ cs_log("WARNING lock %s (%s) timed out.", l->name, (type == WRITELOCK) ? "WRITELOCK" : "READLOCK"); }
|
|
#endif
|
|
}
|
|
|
|
SAFE_MUTEX_UNLOCK_NOLOG_R(&l->lock, n);
|
|
#ifdef WITH_MUTEXDEBUG
|
|
//cs_log_dbg(D_TRACE, "lock %s locked", l->name);
|
|
#endif
|
|
return;
|
|
}
|
|
|
|
void cs_rwunlock_int(const char *n, CS_MUTEX_LOCK *l, int8_t type)
|
|
{
|
|
|
|
if(!l || l->flag) { return; }
|
|
|
|
SAFE_MUTEX_LOCK_R(&l->lock, n);
|
|
|
|
if(type == WRITELOCK)
|
|
{ l->writelock--; }
|
|
else
|
|
{ l->readlock--; }
|
|
|
|
if(l->writelock < 0) { l->writelock = 0; }
|
|
if(l->readlock < 0) { l->readlock = 0; }
|
|
|
|
// waiting writelocks always have priority. If one is waiting, signal it
|
|
if(l->writelock)
|
|
{ SAFE_COND_SIGNAL_R(&l->writecond, n); }
|
|
// Otherwise signal a waiting readlock (if any)
|
|
else if(l->readlock && type != READLOCK)
|
|
{ SAFE_COND_BROADCAST_R(&l->readcond, n); }
|
|
|
|
SAFE_MUTEX_UNLOCK_R(&l->lock, n);
|
|
|
|
#ifdef WITH_MUTEXDEBUG
|
|
#ifdef WITH_DEBUG
|
|
if(l->name != LOG_LIST)
|
|
{
|
|
const char *typetxt[] = { "", "write", "read" };
|
|
cs_log_dbg(D_TRACE, "%slock %s: released", typetxt[type], l->name);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
void cs_rwunlock_int_nolog(const char *n, CS_MUTEX_LOCK *l, int8_t type)
|
|
{
|
|
|
|
if(!l || l->flag) { return; }
|
|
|
|
SAFE_MUTEX_LOCK_NOLOG_R(&l->lock, n);
|
|
|
|
if(type == WRITELOCK)
|
|
{ l->writelock--; }
|
|
else
|
|
{ l->readlock--; }
|
|
|
|
if(l->writelock < 0) { l->writelock = 0; }
|
|
if(l->readlock < 0) { l->readlock = 0; }
|
|
|
|
// waiting writelocks always have priority. If one is waiting, signal it
|
|
if(l->writelock)
|
|
{ SAFE_COND_SIGNAL_R(&l->writecond, n); }
|
|
// Otherwise signal a waiting readlock (if any)
|
|
else if(l->readlock && type != READLOCK)
|
|
{ SAFE_COND_BROADCAST_R(&l->readcond, n); }
|
|
|
|
SAFE_MUTEX_UNLOCK_NOLOG_R(&l->lock, n);
|
|
|
|
#ifdef WITH_MUTEXDEBUG
|
|
#ifdef WITH_DEBUG
|
|
if(l->name != LOG_LIST)
|
|
{
|
|
const char *typetxt[] = { "", "write", "read" };
|
|
cs_log_dbg(D_TRACE, "%slock %s: released", typetxt[type], l->name);
|
|
}
|
|
#endif
|
|
#endif
|
|
}
|
|
|
|
int8_t cs_try_rwlock_int(const char *n, CS_MUTEX_LOCK *l, int8_t type)
|
|
{
|
|
if(!l || !l->name || l->flag)
|
|
{ return 0; }
|
|
|
|
int8_t status = 0;
|
|
|
|
SAFE_MUTEX_LOCK_R(&l->lock, n);
|
|
|
|
if(type == WRITELOCK)
|
|
{
|
|
if(l->writelock || l->readlock)
|
|
{ status = 1; }
|
|
else
|
|
{ l->writelock++; }
|
|
}
|
|
else
|
|
{
|
|
if(l->writelock)
|
|
{ status = 1; }
|
|
else
|
|
{ l->readlock++; }
|
|
}
|
|
|
|
SAFE_MUTEX_UNLOCK_R(&l->lock, n);
|
|
|
|
#ifdef WITH_MUTEXDEBUG
|
|
#ifdef WITH_DEBUG
|
|
if(l->name != LOG_LIST)
|
|
{
|
|
const char *typetxt[] = { "", "write", "read" };
|
|
cs_log_dbg(D_TRACE, "try_%slock %s: status=%d", typetxt[type], l->name, status);
|
|
}
|
|
#endif
|
|
#endif
|
|
return status;
|
|
}
|