Go to the documentation of this file.
15 #include <linux/unistd.h>
19 #include <sys/prctl.h>
20 #include <sys/resource.h>
21 #include <sys/syscall.h>
27 #define ABORT { dsyslog("ABORT!"); cBackTrace::BackTrace(); abort(); }
30 #define DEBUG_LOCKSEQ // uncomment this line to activate debug output for invalid locking sequence
34 #define dbglocking(a...) fprintf(stderr, a)
36 #define dbglocking(a...)
39 static bool GetAbsTime(
struct timespec *Abstime,
int MillisecondsFromNow)
42 if (gettimeofday(&now, NULL) == 0) {
43 now.tv_sec += MillisecondsFromNow / 1000;
44 now.tv_usec += (MillisecondsFromNow % 1000) * 1000;
45 if (now.tv_usec >= 1000000) {
47 now.tv_usec -= 1000000;
49 Abstime->tv_sec = now.tv_sec;
50 Abstime->tv_nsec = now.tv_usec * 1000;
61 pthread_mutex_init(&
mutex, NULL);
62 pthread_cond_init(&
cond, NULL);
67 pthread_cond_broadcast(&
cond);
68 pthread_cond_destroy(&
cond);
69 pthread_mutex_destroy(&
mutex);
80 pthread_mutex_lock(&
mutex);
83 struct timespec abstime;
86 if (pthread_cond_timedwait(&
cond, &
mutex, &abstime) == ETIMEDOUT)
96 pthread_mutex_unlock(&
mutex);
102 pthread_mutex_lock(&
mutex);
104 pthread_cond_broadcast(&
cond);
105 pthread_mutex_unlock(&
mutex);
112 pthread_cond_init(&
cond, 0);
117 pthread_cond_broadcast(&
cond);
118 pthread_cond_destroy(&
cond);
124 int locked = Mutex.
locked;
137 struct timespec abstime;
139 int locked = Mutex.
locked;
142 if (pthread_cond_timedwait(&
cond, &Mutex.
mutex, &abstime) == ETIMEDOUT)
152 pthread_cond_broadcast(&
cond);
161 pthread_rwlockattr_t attr;
162 pthread_rwlockattr_init(&attr);
163 pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP);
164 pthread_rwlock_init(&
rwlock, &attr);
169 pthread_rwlock_destroy(&
rwlock);
175 struct timespec abstime;
181 Result = TimeoutMs ? pthread_rwlock_timedwrlock(&
rwlock, &abstime) : pthread_rwlock_wrlock(&
rwlock);
190 Result = TimeoutMs ? pthread_rwlock_timedrdlock(&
rwlock, &abstime) : pthread_rwlock_rdlock(&
rwlock);
203 pthread_rwlock_unlock(&
rwlock);
211 pthread_mutexattr_t attr;
212 pthread_mutexattr_init(&attr);
213 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
214 pthread_mutex_init(&
mutex, &attr);
219 pthread_mutex_destroy(&
mutex);
224 pthread_mutex_lock(&
mutex);
231 pthread_mutex_unlock(&
mutex);
257 if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
263 if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (3 << 13)) < 0)
273 va_start(ap, Description);
285 if (prctl(PR_SET_NAME, Thread->
description, 0, 0, 0) < 0)
301 #define THREAD_STOP_TIMEOUT 3000 // ms to wait for a thread to stop before newly starting it
302 #define THREAD_STOP_SLEEP 30 // ms to sleep while waiting for a thread to stop
316 if (pthread_create(&
childTid, NULL, (
void *(*) (
void *))&
StartThread, (
void *)
this) == 0) {
342 if ((err = pthread_kill(
childTid, 0)) != 0) {
357 if (
active && WaitSeconds > -1) {
358 if (WaitSeconds > 0) {
359 for (time_t t0 = time(NULL) + WaitSeconds; time(NULL) < t0; ) {
374 return syscall(__NR_gettid);
402 if (Mutex && !
mutex) {
439 #define BT_BUF_SIZE 100
444 char *Function = NULL;
446 char *Address = NULL;
448 for (
char *q = Module; *q; q++) {
453 else if (*q ==
'+') {
461 else if (*q ==
']') {
467 char *DemangledFunction = NULL;
470 DemangledFunction = abi::__cxa_demangle(Function, NULL, 0, &status);
471 if (DemangledFunction)
472 Function = DemangledFunction;
478 unsigned long long addr = Address ? strtoull(Address, NULL, 0) : 0;
479 unsigned long long offs = Offset ? strtoull(Offset, NULL, 0) : 0;
485 while (e = strstr(e,
".so"))
487 if (p && !strchr(p,
'/')) {
489 if (dladdr(
reinterpret_cast<void*
>(addr), &dlinfo)) {
490 if ((strcmp(Module, dlinfo.dli_fname) == 0) && dlinfo.dli_fbase) {
491 unsigned long long base =
reinterpret_cast<unsigned long long>(dlinfo.dli_fbase);
499 cString cmd =
cString::sprintf(
"addr2line --functions --demangle --inlines --basename --exe=%s 0x%llx", Module, Function ? addr : offs);
501 if (p.
Open(cmd,
"r")) {
504 while (
char *l = rl.
Read(p)) {
506 if (Function && strcmp(l, Function))
515 free(DemangledFunction);
523 if (
char **s = backtrace_symbols(b, n)) {
524 for (
int i =
max(Level, 0) + 1; i < n; i++)
534 for (
int i = 0; i < sl.
Size(); i++) {
536 fprintf(f,
"%s\n", sl[i]);
545 Level =
max(Level, 0) + 1;
548 if (
char **s = backtrace_symbols(b, n)) {
550 Caller = Mangled ? s[Level] : *
Demangle(s[Level]);
559 #define SLL_SIZE 20 // the number of log entries
560 #define SLL_LENGTH 512 // the maximum length of log entries
561 #define SLL_THREADS 20 // the maximum number of threads holding locks at the same time (typically well below 10)
562 #define SLL_MAX_LIST 9 // max. number of lists to log
563 #define SLL_WRITE_FLAG 0x80000000
564 #define SLL_LOCK_FLAG 0x40000000
574 #ifdef DEBUG_LOCKCALL
582 void Check(
const char *Name,
bool Lock,
bool Write =
false);
590 #ifdef DEBUG_LOCKCALL
591 memset(logCaller, 0,
sizeof(logCaller));
599 dsyslog(
"--- begin invalid lock sequence report");
601 for (
int i = 0; i <
SLL_SIZE; i++) {
605 q += sprintf(q,
"%5d", tid);
610 int Changed = LastFlags ^ Flags;
615 if ((Flags & b) != 0)
617 if ((Changed & b) != 0)
618 c = Lock ? Write ?
'W' :
'R' :
'U';
619 q += sprintf(q,
" %c", c);
621 q += sprintf(q,
" %c", Lock ?
'L' :
'U');
622 #ifdef DEBUG_LOCKCALL
633 dsyslog(
"%5d invalid lock sequence: %s", ThreadId, Name);
636 dsyslog(
"--- end invalid lock sequence report");
637 dsyslog(
"--- THERE WILL BE NO FURTHER REPORTS UNTIL VDR IS RESTARTED!");
638 fprintf(stderr,
"invalid lock sequence at %s\n", *
DayDateTime(time(NULL)));
644 int n = *Name -
'0' - 1;
650 int AvailableIndex = -1;
660 if (AvailableIndex < 0) {
666 Index = AvailableIndex;
672 esyslog(
"ERROR: too many threads holding list locks at the same time - stopped logging locks!");
678 if ((
flags[Index] & ~b) < b)
680 else if ((
flags[Index] & b) == 0)
689 if (
flags[Index] == 0)
691 #ifdef DEBUG_LOCKCALL
697 Dump(Name, ThreadId);
706 #define dbglockseq(n, l, w) StateLockLog.Check(n, l, w)
708 #define dbglockseq(n, l, w)
709 #endif // DEBUG_LOCKSEQ
738 StateKey.
write =
true;
752 else if (TimeoutMs) {
763 esyslog(
"ERROR: cStateLock::Unlock() called with an unused key (tid=%d, lock=%s)",
threadId,
name);
768 esyslog(
"ERROR: cStateLock::Unlock() called without holding a write lock (tid=%d, lock=%s)",
threadId,
name);
779 if (StateKey.
write) {
780 StateKey.
write =
false;
793 esyslog(
"ERROR: cStateLock::SetSyncStateKey() called without holding a write lock (tid=%d, lock=%s)",
threadId,
name);
798 esyslog(
"ERROR: cStateLock::SetSyncStateKey() called with locked key (tid=%d, lock=%s)",
threadId,
name);
803 esyslog(
"ERROR: cStateLock::SetSyncStateKey() called twice (tid=%d, lock=%s)",
threadId,
name);
813 esyslog(
"ERROR: cStateLock::SetExplicitModify() called without holding a write lock (tid=%d, lock=%s)",
threadId,
name);
818 esyslog(
"ERROR: cStateLock::SetExplicitModify() called twice (tid=%d, lock=%s)",
threadId,
name);
828 esyslog(
"ERROR: cStateLock::SetModified() called without holding a write lock (tid=%d, lock=%s)",
threadId,
name);
864 esyslog(
"ERROR: cStateKey::Remove() called without holding a lock (key=%p)",
this);
872 esyslog(
"ERROR: cStateKey::StateChanged() called without holding a lock (tid=%d, key=%p)",
cThread::ThreadId(),
this);
947 if ((
pid = fork()) < 0) {
954 const char *mode =
"w";
958 if (strcmp(Mode,
"r") == 0) {
963 if ((
f = fdopen(fd[1 - iopipe], mode)) == NULL) {
965 close(fd[1 - iopipe]);
970 int iofd = STDOUT_FILENO;
971 if (strcmp(Mode,
"w") == 0) {
976 if (dup2(fd[1 - iopipe], iofd) == -1) {
978 close(fd[1 - iopipe]);
982 int MaxPossibleFileDescriptors = getdtablesize();
983 for (
int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
985 if (execl(
"/bin/sh",
"sh",
"-c", Command, NULL) == -1) {
987 close(fd[1 - iopipe]);
1008 ret = waitpid(
pid, &status, WNOHANG);
1010 if (errno != EINTR && errno != ECHILD) {
1015 else if (ret ==
pid)
1024 else if (ret == -1 || !WIFEXITED(status))
1038 if ((pid = fork()) < 0) {
1045 if (waitpid(pid, &status, 0) < 0) {
1057 pid_t sid = setsid();
1061 int devnull = open(
"/dev/null", O_RDONLY);
1062 if (devnull < 0 || dup2(devnull, 0) < 0)
1065 int MaxPossibleFileDescriptors = getdtablesize();
1066 for (
int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
1068 if (execl(
"/bin/sh",
"sh",
"-c", Command, NULL) == -1) {
#define THREAD_STOP_SLEEP
static cString sprintf(const char *fmt,...) __attribute__((format(printf
void Signal(void)
Signals a caller of Wait() that the condition it is waiting for is met.
cRwLock(bool PreferWriter=false)
void SetIOPriority(int Priority)
#define dbglockseq(n, l, w)
bool Lock(bool Write, int TimeoutMs=0)
#define THREAD_STOP_TIMEOUT
cThreadLock(cThread *Thread=NULL)
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
uint8_t logCounter[SLL_THREADS][SLL_MAX_LIST]
tThreadId logThreadIds[SLL_SIZE]
void SetModified(void)
Sets this lock to have its state incremented when the current write lock state key is removed.
static tThreadId ThreadId(void)
virtual void Action(void)=0
A derived cThread class must implement the code it wants to execute as a separate thread in this func...
bool Wait(int TimeoutMs=0)
Waits at most TimeoutMs milliseconds for a call to Signal(), or forever if TimeoutMs is 0.
static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
tThreadId writeLockThreadId
static cStateLockLog StateLockLog
static void SetMainThreadId(void)
static cString static cString vsprintf(const char *fmt, va_list &ap)
static cString GetCaller(int Level=0, bool Mangled=false)
Returns the caller at the given Level (or the immediate caller, if Level is 0).
void SetPriority(int Priority)
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
void Check(const char *Name, bool Lock, bool Write=false)
static cString Demangle(char *s)
Demangles the function name in the given string and returns the converted version of s.
static void * StartThread(cThread *Thread)
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.
bool StateChanged(void)
Returns true if this key is used for obtaining a write lock, and the lock's state differs from that o...
cVector< tThreadId > threadIds
bool Open(const char *Command, const char *Mode)
void SetSyncStateKey(cStateKey &StateKey)
Sets the given StateKey to be synchronized to the state of this lock.
static tThreadId mainThreadId
void Release(void)
Releases the global I/O throttling mechanism.
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
bool Lock(cThread *Thread)
void SetExplicitModify(void)
If you have obtained a write lock on this lock, and you don't want its state to be automatically incr...
void Reset(void)
Resets the state of this key, so that the next call to a lock's Lock() function with this key will re...
bool Active(void)
Checks whether the thread is still alive.
void SetDescription(const char *Description,...) __attribute__((format(printf
bool TimedWait(cMutex &Mutex, int TimeoutMs)
cStateKey(bool IgnoreFirst=false)
Sets up a new state key.
cMutexLock(cMutex *Mutex=NULL)
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
void Activate(void)
Activates the global I/O throttling mechanism.
virtual void Append(T Data)
int SystemExec(const char *Command, bool Detached)
void Unlock(cStateKey &StateKey, bool IncState=true)
Releases a lock that has been obtained by a previous call to Lock() with the given StateKey.
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
uint64_t Elapsed(void) const
void Dump(const char *Name, tThreadId ThreadId)
static void BackTrace(cStringList &StringList, int Level=0, bool Mangled=false)
Produces a backtrace and stores it in the given StringList.