summaryrefslogtreecommitdiff
path: root/thread.c
diff options
context:
space:
mode:
authorKlaus Schmidinger <vdr@tvdr.de>2015-09-01 11:14:27 +0200
committerKlaus Schmidinger <vdr@tvdr.de>2015-09-01 11:14:27 +0200
commit3cd5294d8a337ee5cd2ec894c9fbe04ad3a7690d (patch)
treeda57ce74189de9bfb27e1a747063c37cd62de501 /thread.c
parent8a7bc6a0bbf60cae8b6391a630880aad5cba3363 (diff)
downloadvdr-3cd5294d8a337ee5cd2ec894c9fbe04ad3a7690d.tar.gz
vdr-3cd5294d8a337ee5cd2ec894c9fbe04ad3a7690d.tar.bz2
Implemented strict locking of global lists
Diffstat (limited to 'thread.c')
-rw-r--r--thread.c138
1 files changed, 137 insertions, 1 deletions
diff --git a/thread.c b/thread.c
index 4245f32e..993d16dd 100644
--- a/thread.c
+++ b/thread.c
@@ -4,7 +4,7 @@
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
- * $Id: thread.c 3.2 2013/12/29 17:21:53 kls Exp $
+ * $Id: thread.c 4.1 2015/08/29 14:43:03 kls Exp $
*/
#include "thread.h"
@@ -21,6 +21,16 @@
#include <unistd.h>
#include "tools.h"
+#define ABORT { dsyslog("ABORT!"); abort(); } // use debugger to trace back the problem
+
+//#define DEBUG_LOCKING // uncomment this line to activate debug output for locking
+
+#ifdef DEBUG_LOCKING
+#define dbglocking(a...) fprintf(stderr, a)
+#else
+#define dbglocking(a...)
+#endif
+
static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
{
struct timeval now;
@@ -403,6 +413,132 @@ bool cThreadLock::Lock(cThread *Thread)
return false;
}
+// --- cStateLock ------------------------------------------------------------
+
+cStateLock::cStateLock(const char *Name)
+:rwLock(true)
+{
+ name = Name;
+ threadId = 0;
+ state = 0;
+ explicitModify = false;
+}
+
+bool cStateLock::Lock(cStateKey &StateKey, bool Write, int TimeoutMs)
+{
+ dbglocking("%5d %-10s %10p lock state = %d/%d write = %d timeout = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, Write, TimeoutMs);
+ StateKey.timedOut = false;
+ if (StateKey.stateLock) {
+ esyslog("ERROR: StateKey already in use in call to cStateLock::Lock() (tid=%d, lock=%s)", StateKey.stateLock->threadId, name);
+ ABORT;
+ return false;
+ }
+ if (rwLock.Lock(Write, TimeoutMs)) {
+ StateKey.stateLock = this;
+ if (Write) {
+ dbglocking("%5d %-10s %10p locked write\n", cThread::ThreadId(), name, &StateKey);
+ threadId = cThread::ThreadId();
+ StateKey.write = true;
+ return true;
+ }
+ else if (state != StateKey.state) {
+ dbglocking("%5d %-10s %10p locked read\n", cThread::ThreadId(), name, &StateKey);
+ return true;
+ }
+ else {
+ dbglocking("%5d %-10s %10p state unchanged\n", cThread::ThreadId(), name, &StateKey);
+ StateKey.stateLock = NULL;
+ rwLock.Unlock();
+ }
+ }
+ else if (TimeoutMs) {
+ dbglocking("%5d %-10s %10p timeout\n", cThread::ThreadId(), name, &StateKey);
+ StateKey.timedOut = true;
+ }
+ return false;
+}
+
+void cStateLock::Unlock(cStateKey &StateKey, bool IncState)
+{
+ dbglocking("%5d %-10s %10p unlock state = %d/%d inc = %d\n", cThread::ThreadId(), name, &StateKey, state, StateKey.state, IncState);
+ if (StateKey.stateLock != this) {
+ esyslog("ERROR: cStateLock::Unlock() called with an unused key (tid=%d, lock=%s)", threadId, name);
+ ABORT;
+ return;
+ }
+ if (StateKey.write && threadId != cThread::ThreadId()) {
+ esyslog("ERROR: cStateLock::Unlock() called without holding a lock (tid=%d, lock=%s)", threadId, name);
+ ABORT;
+ return;
+ }
+ if (StateKey.write && IncState && !explicitModify)
+ state++;
+ StateKey.state = state;
+ StateKey.write = false;
+ threadId = 0;
+ explicitModify = false;
+ rwLock.Unlock();
+}
+
+void cStateLock::IncState(void)
+{
+ if (threadId != cThread::ThreadId()) {
+ esyslog("ERROR: cStateLock::IncState() called without holding a lock (tid=%d, lock=%s)", threadId, name);
+ ABORT;
+ }
+ else
+ state++;
+}
+
+// --- cStateKey -------------------------------------------------------------
+
+cStateKey::cStateKey(bool IgnoreFirst)
+{
+ stateLock = NULL;
+ write = false;
+ state = 0;
+ if (!IgnoreFirst)
+ Reset();
+}
+
+cStateKey::~cStateKey()
+{
+ if (stateLock) {
+ esyslog("ERROR: cStateKey::~cStateKey() called without releasing the lock first (tid=%d, lock=%s, key=%p)", stateLock->threadId, stateLock->name, this);
+ ABORT;
+ }
+}
+
+void cStateKey::Reset(void)
+{
+ state = -1; // lock and key are initialized differently, to make the first check return true
+}
+
+void cStateKey::Remove(bool IncState)
+{
+ if (stateLock) {
+ stateLock->Unlock(*this, IncState);
+ stateLock = NULL;
+ }
+ else {
+ esyslog("ERROR: cStateKey::Remove() called without holding a lock (key=%p)", this);
+ ABORT;
+ }
+}
+
+bool cStateKey::StateChanged(void)
+{
+ if (!stateLock) {
+ esyslog("ERROR: cStateKey::StateChanged() called without holding a lock (tid=%d, key=%p)", cThread::ThreadId(), this);
+ ABORT;
+ }
+ else if (write)
+ return state != stateLock->state;
+ else
+ return true;
+ return false;
+}
+
// --- cIoThrottle -----------------------------------------------------------
cMutex cIoThrottle::mutex;