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