From 72c03260d77a0de52ed57992e374f26211fe2063 Mon Sep 17 00:00:00 2001 From: Klaus Schmidinger Date: Sat, 22 Sep 2012 11:52:33 +0200 Subject: [PATCH] The new class cIoThrottle is used to allow I/O intense threads to temporarily suspend their activities in case buffers run full --- CONTRIBUTORS | 2 ++ HISTORY | 7 ++++++- cutter.c | 18 +++++++++++++++++- device.c | 3 ++- recorder.c | 3 ++- ringbuffer.c | 18 +++++++++++++++++- ringbuffer.h | 4 +++- thread.c | 44 +++++++++++++++++++++++++++++++++++++++++++- thread.h | 25 ++++++++++++++++++++++++- 9 files changed, 116 insertions(+), 8 deletions(-) diff --git a/CONTRIBUTORS b/CONTRIBUTORS index 0137743c..419dc216 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -2891,6 +2891,8 @@ Torsten Lang if the editing point merges two seamlessly fitting parts of the same stream for reporting a sluggish response when manipulating editing marks while a cutting thread is running + for suggesting to allow I/O intense threads to temporarily suspend their activities + in case buffers run full Christian Ruppert for some improvements to the Makefiles diff --git a/HISTORY b/HISTORY index e4a3fc4c..acfdda73 100644 --- a/HISTORY +++ b/HISTORY @@ -7235,7 +7235,7 @@ Video Disk Recorder Revision History function in order to make use of this new feature. See, for instance, the function cSkinClassicDisplayMenu::SetButtons() in skinclassic.c for details. -2012-09-19: Version 1.7.31 +2012-09-22: Version 1.7.31 - If regenerating an index file fails and no data is written to the file, VDR now reports this error and removes the empty index file. @@ -7251,3 +7251,8 @@ Video Disk Recorder Revision History (suggested by Jens Vogel). - Fixed a leftover frame counter in the LCARS skin's replay display after jumping to an editing mark and resuming replay. +- The new class cIoThrottle is used to allow I/O intense threads to temporarily + suspend their activities in case buffers run full (suggested by Torsten Lang). + Currently the cutter thread is suspended if the TS or Recorder buffer use more + than 50% of their capacity. Plugin authors may want to participate in this + mechanism if they use intense background I/O. diff --git a/cutter.c b/cutter.c index 62eae822..bcae2b72 100644 --- a/cutter.c +++ b/cutter.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: cutter.c 2.13 2012/06/10 14:33:36 kls Exp $ + * $Id: cutter.c 2.14 2012/09/20 09:12:47 kls Exp $ */ #include "cutter.h" @@ -88,12 +88,28 @@ void cCuttingThread::Action(void) bool CheckForSeamlessStream = false; bool LastMark = false; bool cutIn = true; + bool suspensionLogged = false; while (Running()) { uint16_t FileNumber; off_t FileOffset; int Length; bool Independent; + // Suspend cutting if we have severe throughput problems: + + if (cIoThrottle::Engaged()) { + if (!suspensionLogged) { + dsyslog("suspending cutter thread"); + suspensionLogged = true; + } + cCondWait::SleepMs(100); + continue; + } + else if (suspensionLogged) { + dsyslog("resuming cutter thread"); + suspensionLogged = false; + } + // Make sure there is enough disk space: AssertFreeDiskSpace(-1); diff --git a/device.c b/device.c index 6518e820..aa93a8e5 100644 --- a/device.c +++ b/device.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: device.c 2.67 2012/09/02 09:26:36 kls Exp $ + * $Id: device.c 2.68 2012/09/20 09:32:26 kls Exp $ */ #include "device.h" @@ -1721,6 +1721,7 @@ cTSBuffer::cTSBuffer(int File, int Size, int CardIndex) delivered = false; ringBuffer = new cRingBufferLinear(Size, TS_SIZE, true, "TS"); ringBuffer->SetTimeouts(100, 100); + ringBuffer->SetIoThrottle(); Start(); } diff --git a/recorder.c b/recorder.c index a6cab473..c11fdf7c 100644 --- a/recorder.c +++ b/recorder.c @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: recorder.c 2.15 2011/09/04 09:26:44 kls Exp $ + * $Id: recorder.c 2.16 2012/09/22 11:52:33 kls Exp $ */ #include "recorder.h" @@ -33,6 +33,7 @@ cRecorder::cRecorder(const char *FileName, const cChannel *Channel, int Priority ringBuffer = new cRingBufferLinear(RECORDERBUFSIZE, MIN_TS_PACKETS_FOR_FRAME_DETECTOR * TS_SIZE, true, "Recorder"); ringBuffer->SetTimeouts(0, 100); + ringBuffer->SetIoThrottle(); int Pid = Channel->Vpid(); int Type = Channel->Vtype(); diff --git a/ringbuffer.c b/ringbuffer.c index 269623d4..abe78990 100644 --- a/ringbuffer.c +++ b/ringbuffer.c @@ -7,7 +7,7 @@ * Parts of this file were inspired by the 'ringbuffy.c' from the * LinuxDVB driver (see linuxtv.org). * - * $Id: ringbuffer.c 2.4 2012/09/17 08:23:43 kls Exp $ + * $Id: ringbuffer.c 2.5 2012/09/22 11:26:49 kls Exp $ */ #include "ringbuffer.h" @@ -20,6 +20,8 @@ #define OVERFLOWREPORTDELTA 5 // seconds between reports #define PERCENTAGEDELTA 10 #define PERCENTAGETHRESHOLD 70 +#define IOTHROTTLELOW 20 +#define IOTHROTTLEHIGH 50 cRingBuffer::cRingBuffer(int Size, bool Statistics) { @@ -31,10 +33,12 @@ cRingBuffer::cRingBuffer(int Size, bool Statistics) putTimeout = getTimeout = 0; lastOverflowReport = 0; overflowCount = overflowBytes = 0; + ioThrottle = NULL; } cRingBuffer::~cRingBuffer() { + delete ioThrottle; if (statistics) dsyslog("buffer stats: %d (%d%%) used", maxFill, maxFill * 100 / (size - 1)); } @@ -50,6 +54,12 @@ void cRingBuffer::UpdatePercentage(int Fill) lastPercent = percent; } } + if (ioThrottle) { + if (percent >= IOTHROTTLEHIGH) + ioThrottle->Activate(); + else if (percent < IOTHROTTLELOW) + ioThrottle->Release(); + } } void cRingBuffer::WaitForPut(void) @@ -82,6 +92,12 @@ void cRingBuffer::SetTimeouts(int PutTimeout, int GetTimeout) getTimeout = GetTimeout; } +void cRingBuffer::SetIoThrottle(void) +{ + if (!ioThrottle) + ioThrottle = new cIoThrottle; +} + void cRingBuffer::ReportOverflow(int Bytes) { overflowCount++; diff --git a/ringbuffer.h b/ringbuffer.h index d234502d..5b2eeb1c 100644 --- a/ringbuffer.h +++ b/ringbuffer.h @@ -4,7 +4,7 @@ * See the main source file 'vdr.c' for copyright information and * how to reach the author. * - * $Id: ringbuffer.h 2.3 2011/12/04 13:38:17 kls Exp $ + * $Id: ringbuffer.h 2.4 2012/09/20 09:29:32 kls Exp $ */ #ifndef __RINGBUFFER_H @@ -22,6 +22,7 @@ private: time_t lastOverflowReport; int overflowCount; int overflowBytes; + cIoThrottle *ioThrottle; protected: tThreadId getThreadTid; int maxFill;//XXX @@ -40,6 +41,7 @@ public: cRingBuffer(int Size, bool Statistics = false); virtual ~cRingBuffer(); void SetTimeouts(int PutTimeout, int GetTimeout); + void SetIoThrottle(void); void ReportOverflow(int Bytes); }; diff --git a/thread.c b/thread.c index 5bcc9a54..a650fab8 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 2.4 2012/05/08 11:15:57 kls Exp $ + * $Id: thread.c 2.5 2012/09/20 09:05:50 kls Exp $ */ #include "thread.h" @@ -398,6 +398,48 @@ bool cThreadLock::Lock(cThread *Thread) return false; } +// --- cIoThrottle ----------------------------------------------------------- + +cMutex cIoThrottle::mutex; +int cIoThrottle::count = 0; + +cIoThrottle::cIoThrottle(void) +{ + active = false; +} + +cIoThrottle::~cIoThrottle() +{ + Release(); +} + +void cIoThrottle::Activate(void) +{ + if (!active) { + mutex.Lock(); + count++; + active = true; + dsyslog("i/o throttle activated, count = %d (tid=%d)", count, cThread::ThreadId()); + mutex.Unlock(); + } +} + +void cIoThrottle::Release(void) +{ + if (active) { + mutex.Lock(); + count--; + active = false; + dsyslog("i/o throttle released, count = %d (tid=%d)", count, cThread::ThreadId()); + mutex.Unlock(); + } +} + +bool cIoThrottle::Engaged(void) +{ + return count > 0; +} + // --- cPipe ----------------------------------------------------------------- // cPipe::Open() and cPipe::Close() are based on code originally received from diff --git a/thread.h b/thread.h index d4919eb6..f77e8198 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 2.1 2009/04/13 13:50:39 kls Exp $ + * $Id: thread.h 2.2 2012/09/20 08:46:27 kls Exp $ */ #ifndef __THREAD_H @@ -157,6 +157,29 @@ public: #define LOCK_THREAD cThreadLock ThreadLock(this) +class cIoThrottle { +private: + static cMutex mutex; + static int count; + bool active; +public: + cIoThrottle(void); + ~cIoThrottle(); + void Activate(void); + ///< Activates the global I/O throttling mechanism. + ///< This function may be called any number of times, but only + ///< the first call after an inactive state will have an effect. + void Release(void); + ///< Releases the global I/O throttling mechanism. + ///< This function may be called any number of times, but only + ///< the first call after an active state will have an effect. + bool Active(void) { return active; } + ///< Returns true if this I/O throttling object is currently active. + static bool Engaged(void); + ///< Returns true if any I/O throttling object is currently active. + }; + + // cPipe implements a pipe that closes all unnecessary file descriptors in // the child process.