1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342
|
/*
* thread.h: A simple thread base class
*
* See the main source file 'vdr.c' for copyright information and
* how to reach the author.
*
* $Id: thread.h 4.6 2020/09/16 13:48:33 kls Exp $
*/
#ifndef __THREAD_H
#define __THREAD_H
#include <pthread.h>
#include <stdio.h>
#include <sys/types.h>
typedef pid_t tThreadId;
class cCondWait {
private:
pthread_mutex_t mutex;
pthread_cond_t cond;
bool signaled;
public:
cCondWait(void);
~cCondWait();
static void SleepMs(int TimeoutMs);
///< Creates a cCondWait object and uses it to sleep for TimeoutMs
///< milliseconds, immediately giving up the calling thread's time
///< slice and thus avoiding a "busy wait".
///< In order to avoid a possible busy wait, TimeoutMs will be automatically
///< limited to values >2.
bool Wait(int TimeoutMs = 0);
///< Waits at most TimeoutMs milliseconds for a call to Signal(), or
///< forever if TimeoutMs is 0.
///< Returns true if Signal() has been called, false if the given
///< timeout has expired.
void Signal(void);
///< Signals a caller of Wait() that the condition it is waiting for is met.
};
class cMutex;
class cCondVar {
private:
pthread_cond_t cond;
public:
cCondVar(void);
~cCondVar();
void Wait(cMutex &Mutex);
bool TimedWait(cMutex &Mutex, int TimeoutMs);
void Broadcast(void);
};
class cRwLock {
private:
pthread_rwlock_t rwlock;
int locked;
tThreadId writeLockThreadId;
public:
cRwLock(bool PreferWriter = false);
~cRwLock();
bool Lock(bool Write, int TimeoutMs = 0);
void Unlock(void);
};
class cMutex {
friend class cCondVar;
private:
pthread_mutex_t mutex;
int locked;
public:
cMutex(void);
~cMutex();
void Lock(void);
void Unlock(void);
};
class cThread {
friend class cThreadLock;
private:
bool active;
bool running;
pthread_t childTid;
tThreadId childThreadId;
cMutex mutex;
char *description;
bool lowPriority;
static tThreadId mainThreadId;
static void *StartThread(cThread *Thread);
protected:
void SetPriority(int Priority);
void SetIOPriority(int Priority);
void Lock(void) { mutex.Lock(); }
void Unlock(void) { mutex.Unlock(); }
virtual void Action(void) = 0;
///< A derived cThread class must implement the code it wants to
///< execute as a separate thread in this function. If this is
///< a loop, it must check Running() repeatedly to see whether
///< it's time to stop.
bool Running(void) { return running; }
///< Returns false if a derived cThread object shall leave its Action()
///< function.
void Cancel(int WaitSeconds = 0);
///< Cancels the thread by first setting 'running' to false, so that
///< the Action() loop can finish in an orderly fashion and then waiting
///< up to WaitSeconds seconds for the thread to actually end. If the
///< thread doesn't end by itself, it is killed.
///< If WaitSeconds is -1, only 'running' is set to false and Cancel()
///< returns immediately, without killing the thread.
public:
cThread(const char *Description = NULL, bool LowPriority = false);
///< Creates a new thread.
///< If Description is present, a log file entry will be made when
///< the thread starts and stops (see SetDescription()).
///< The Start() function must be called to actually start the thread.
///< LowPriority can be set to true to make this thread run at a lower
///< priority.
virtual ~cThread();
void SetDescription(const char *Description, ...) __attribute__ ((format (printf, 2, 3)));
///< Sets the description of this thread, which will be used when logging
///< starting or stopping of the thread. Make sure any important information
///< is within the first 15 characters of Description, because only these
///< may be displayed in thread listings (like 'htop', for instance).
bool Start(void);
///< Actually starts the thread.
///< If the thread is already running, nothing happens.
bool Active(void);
///< Checks whether the thread is still alive.
static tThreadId ThreadId(void);
static tThreadId IsMainThread(void) { return ThreadId() == mainThreadId; }
static void SetMainThreadId(void);
};
// cMutexLock can be used to easily set a lock on mutex and make absolutely
// sure that it will be unlocked when the block will be left. Several locks can
// be stacked, so a function that makes many calls to another function which uses
// cMutexLock may itself use a cMutexLock to make one longer lock instead of many
// short ones.
class cMutexLock {
private:
cMutex *mutex;
bool locked;
public:
cMutexLock(cMutex *Mutex = NULL);
~cMutexLock();
bool Lock(cMutex *Mutex);
};
// cThreadLock can be used to easily set a lock in a thread and make absolutely
// sure that it will be unlocked when the block will be left. Several locks can
// be stacked, so a function that makes many calls to another function which uses
// cThreadLock may itself use a cThreadLock to make one longer lock instead of many
// short ones.
class cThreadLock {
private:
cThread *thread;
bool locked;
public:
cThreadLock(cThread *Thread = NULL);
~cThreadLock();
bool Lock(cThread *Thread);
};
#define LOCK_THREAD cThreadLock ThreadLock(this)
class cStateKey;
class cStateLock {
friend class cStateKey;
private:
enum { emDisabled = 0, emArmed, emEnabled };
const char *name;
tThreadId threadId;
cRwLock rwLock;
int state;
int explicitModify;
cStateKey *syncStateKey;
void Unlock(cStateKey &StateKey, bool IncState = true);
///< Releases a lock that has been obtained by a previous call to Lock()
///< with the given StateKey. If this was a write-lock, and IncState is true,
///< the state of the lock will be incremented. In any case, the (new) state
///< of the lock will be copied to the StateKey's state.
public:
cStateLock(const char *Name = NULL);
bool Lock(cStateKey &StateKey, bool Write = false, int TimeoutMs = 0);
///< Tries to get a lock and returns true if successful.
///< If TimoutMs is not 0, it waits for the given number of milliseconds
///< and returns false if no lock has been obtained within that time.
///< Otherwise it waits indefinitely for the lock. The given StateKey
///< will store which lock it has been used with, and will use that
///< information when its Remove() function is called.
///< There are two possible locks, one only for read access, and one
///< for reading and writing:
///<
///< If Write is false (i.e. a read-lock is requested), the lock's state
///< is compared to the given StateKey's state, and true is returned if
///< they differ.
///< If true is returned, the read-lock is still in place and the
///< protected data structures can be safely accessed (in read-only mode!).
///< Once the necessary operations have been performed, the lock must
///< be released by a call to the StateKey's Remove() function.
///< If false is returned, the state has not changed since the last check,
///< and the read-lock has been released. In that case the protected
///< data structures have not changed since the last call, so no action
///< is required. Note that if TimeoutMs is used with read-locks, Lock()
///< might return false even if the states of lock and key differ, just
///< because it was unable to obtain the lock within the given time.
///< You can call cStateKey::TimedOut() to detect this.
///<
///< If Write is true (i.e. a write-lock is requested), the states of the
///< lock and the given StateKey don't matter, it will always try to obtain
///< a write lock.
void SetSyncStateKey(cStateKey &StateKey);
///< Sets the given StateKey to be synchronized to the state of this lock.
///< The caller must currently hold a write lock on this lock, with a cStateKey
///< that is different from the given StateKey. If, when removing the key that
///< is holding the write lock, the StateKey's current state is the same as that
///< of the lock, it will be increased together with the lock's state.
void SetExplicitModify(void);
///< If you have obtained a write lock on this lock, and you don't want its
///< state to be automatically incremented when the lock is released, a call to
///< this function will disable this, and you can explicitly call SetModified()
///< to increment the state.
void SetModified(void);
///< Sets this lock to have its state incremented when the current write lock
///< state key is removed. Must have called SetExplicitModify() before calling
///< this function.
};
class cStateKey {
friend class cStateLock;
private:
cStateLock *stateLock;
bool write;
int state;
bool timedOut;
public:
cStateKey(bool IgnoreFirst = false);
///< Sets up a new state key. If IgnoreFirst is true, the first use
///< of this key with a lock will not return true if the lock's state
///< hasn't explicitly changed.
~cStateKey();
void Reset(void);
///< Resets the state of this key, so that the next call to a lock's
///< Lock() function with this key will return true, even if the
///< lock's state hasn't changed.
void Remove(bool IncState = true);
///< Removes this key from the lock it was previously used with.
///< If this key was used to obtain a write lock, the state of the lock will
///< be incremented and copied to this key. You can set IncState to false
///< to prevent this.
bool StateChanged(void);
///< Returns true if this key is used for obtaining a write lock, and the
///< lock's state differs from that of the key. When used with a read lock,
///< it always returns true, because otherwise the lock wouldn't have been
///< obtained in the first place.
bool InLock(void) { return stateLock; }
///< Returns true if this key is currently in a lock.
bool TimedOut(void) const { return timedOut; }
///< Returns true if the last lock attempt this key was used with failed due
///< to a timeout.
};
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.
class cPipe {
private:
pid_t pid;
FILE *f;
public:
cPipe(void);
~cPipe();
operator FILE* () { return f; }
bool Open(const char *Command, const char *Mode);
int Close(void);
};
// cBackTrace can be used for debugging.
class cStringList;
class cString;
class cBackTrace {
public:
static cString Demangle(char *s);
///< Demangles the function name in the given string and returns the converted
///< version of s. s must be one of the strings returned by a call to
///< BackTrace() or GetCaller().
///< Note that this function works on the given string by inserting '\0'
///< characters to separate the individual parts. Therefore the string
///< will be modified upon return.
static void BackTrace(cStringList &StringList, int Level = 0, bool Mangled = false);
///< Produces a backtrace and stores it in the given StringList.
///< If Level is given, only calls up to the given value are listed.
///< If Mangled is true, the raw backtrace will be returned and you can use
///< Demangle() to make the function names readable.
static void BackTrace(FILE *f = NULL, int Level = 0, bool Mangled = false);
///< Produces a backtrace beginning at the given Level, and
///< writes it to the given file. If no file is given, the backtrace is
///< written to the logfile. If Mangled is true, the raw backtrace will
///< be printed/logged.
static cString GetCaller(int Level = 0, bool Mangled = false);
///< Returns the caller at the given Level (or the immediate caller,
///< if Level is 0).
///< If Mangled is true, the raw backtrace will be returned and you can use
///< Demangle() to make the function name readable.
};
// SystemExec() implements a 'system()' call that closes all unnecessary file
// descriptors in the child process.
// With Detached=true, calls command in background and in a separate session,
// with stdin connected to /dev/null.
int SystemExec(const char *Command, bool Detached = false);
#endif //__THREAD_H
|