diff --git a/HISTORY b/HISTORY index 503f7707..4ae2b0e1 100644 --- a/HISTORY +++ b/HISTORY @@ -8839,3 +8839,7 @@ Video Disk Recorder Revision History instead. - The SVDRP command DELC now refuses to delete the very last channel in the list, to avoid ending up with an empty channel list. +- The cRwLock class now allows nested read locks within a write lock from the + same thread. This fixes possible crashes when moving or deleting channels in + the menu or through SVDRP (as well as other operations that try to acquire a + read lock within a write lock). diff --git a/thread.c b/thread.c index 993d16dd..2f57f815 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 4.1 2015/08/29 14:43:03 kls Exp $ + * $Id: thread.c 4.2 2016/12/08 10:18:32 kls Exp $ */ #include "thread.h" @@ -151,6 +151,8 @@ void cCondVar::Broadcast(void) cRwLock::cRwLock(bool PreferWriter) { + locked = 0; + writeLockThreadId = 0; pthread_rwlockattr_t attr; pthread_rwlockattr_init(&attr); pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP); @@ -170,8 +172,15 @@ bool cRwLock::Lock(bool Write, int TimeoutMs) if (!GetAbsTime(&abstime, TimeoutMs)) TimeoutMs = 0; } - if (Write) + if (Write) { Result = TimeoutMs ? pthread_rwlock_timedwrlock(&rwlock, &abstime) : pthread_rwlock_wrlock(&rwlock); + if (Result == 0) + writeLockThreadId = cThread::ThreadId(); + } + else if (writeLockThreadId == cThread::ThreadId()) { + locked++; // there can be any number of stacked read locks, so we keep track here + Result = 0; // aquiring a read lock while holding a write lock within the same thread is OK + } else Result = TimeoutMs ? pthread_rwlock_timedrdlock(&rwlock, &abstime) : pthread_rwlock_rdlock(&rwlock); return Result == 0; @@ -179,6 +188,13 @@ bool cRwLock::Lock(bool Write, int TimeoutMs) void cRwLock::Unlock(void) { + if (writeLockThreadId == cThread::ThreadId()) { // this is the thread that obtained the initial write lock + if (locked) { // this is the unlock of a read lock within the write lock + locked--; + return; + } + } + writeLockThreadId = 0; pthread_rwlock_unlock(&rwlock); } @@ -206,8 +222,8 @@ void cMutex::Lock(void) void cMutex::Unlock(void) { - if (!--locked) - pthread_mutex_unlock(&mutex); + if (!--locked) + pthread_mutex_unlock(&mutex); } // --- cThread --------------------------------------------------------------- @@ -474,9 +490,11 @@ void cStateLock::Unlock(cStateKey &StateKey, bool IncState) if (StateKey.write && IncState && !explicitModify) state++; StateKey.state = state; - StateKey.write = false; - threadId = 0; - explicitModify = false; + if (StateKey.write) { + StateKey.write = false; + threadId = 0; + explicitModify = false; + } rwLock.Unlock(); } diff --git a/thread.h b/thread.h index b5a07c79..9df677d3 100644 --- a/thread.h +++ b/thread.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: thread.h 4.1 2015/08/17 13:06:24 kls Exp $ + * $Id: thread.h 4.2 2016/12/08 10:18:32 kls Exp $ */ #ifndef __THREAD_H @@ -14,6 +14,8 @@ #include #include +typedef pid_t tThreadId; + class cCondWait { private: pthread_mutex_t mutex; @@ -53,6 +55,8 @@ public: class cRwLock { private: pthread_rwlock_t rwlock; + int locked; + tThreadId writeLockThreadId; public: cRwLock(bool PreferWriter = false); ~cRwLock(); @@ -72,8 +76,6 @@ public: void Unlock(void); }; -typedef pid_t tThreadId; - class cThread { friend class cThreadLock; private: