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
|
/*
* 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.2 2016/12/08 10:18:32 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 it 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:
const char *name;
tThreadId threadId;
cRwLock rwLock;
int state;
bool explicitModify;
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 SetExplicitModify(void) { explicitModify = true; }
///< 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 IncState()
///< to increment the state.
void IncState(void);
///< Increments the state of this lock.
};
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);
};
// 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
|