oscam-2.26.01-11942-802-wit.../oscam-lock.c
2026-02-17 09:41:05 +00:00

272 lines
6.3 KiB
C
Executable File

#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;
}