15#include <linux/unistd.h>
20#include <sys/resource.h>
21#include <sys/syscall.h>
27#define ABORT { dsyslog("ABORT!"); cBackTrace::BackTrace(); abort(); }
34#define dbglocking(a...) fprintf(stderr, a)
36#define dbglocking(a...)
39static bool GetAbsTime(
struct timespec *Abstime,
int MillisecondsFromNow)
42 if (gettimeofday(&now, NULL) == 0) {
43 MillisecondsFromNow =
max(MillisecondsFromNow, 3);
44 now.tv_sec += MillisecondsFromNow / 1000;
45 now.tv_usec += (MillisecondsFromNow % 1000) * 1000;
46 if (now.tv_usec >= 1000000) {
48 now.tv_usec -= 1000000;
50 Abstime->tv_sec = now.tv_sec;
51 Abstime->tv_nsec = now.tv_usec * 1000;
62 pthread_mutex_init(&
mutex, NULL);
63 pthread_cond_init(&
cond, NULL);
68 pthread_cond_broadcast(&
cond);
69 pthread_cond_destroy(&
cond);
70 pthread_mutex_destroy(&
mutex);
81 pthread_mutex_lock(&
mutex);
84 struct timespec abstime;
87 if (pthread_cond_timedwait(&
cond, &
mutex, &abstime) == ETIMEDOUT)
97 pthread_mutex_unlock(&
mutex);
103 pthread_mutex_lock(&
mutex);
105 pthread_cond_broadcast(&
cond);
106 pthread_mutex_unlock(&
mutex);
113 pthread_cond_init(&
cond, 0);
118 pthread_cond_broadcast(&
cond);
119 pthread_cond_destroy(&
cond);
125 int locked = Mutex.
locked;
140 struct timespec abstime;
142 int locked = Mutex.
locked;
146 if (pthread_cond_timedwait(&
cond, &Mutex.
mutex, &abstime) == ETIMEDOUT)
157 pthread_cond_broadcast(&
cond);
166 pthread_rwlockattr_t attr;
167 pthread_rwlockattr_init(&attr);
168 pthread_rwlockattr_setkind_np(&attr, PreferWriter ? PTHREAD_RWLOCK_PREFER_WRITER_NP : PTHREAD_RWLOCK_PREFER_READER_NP);
169 pthread_rwlock_init(&
rwlock, &attr);
174 pthread_rwlock_destroy(&
rwlock);
180 struct timespec abstime;
186 Result = TimeoutMs ? pthread_rwlock_timedwrlock(&
rwlock, &abstime) : pthread_rwlock_wrlock(&
rwlock);
195 Result = TimeoutMs ? pthread_rwlock_timedrdlock(&
rwlock, &abstime) : pthread_rwlock_rdlock(&
rwlock);
208 pthread_rwlock_unlock(&
rwlock);
217 pthread_mutexattr_t attr;
218 pthread_mutexattr_init(&attr);
219 pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK_NP);
220 pthread_mutex_init(&
mutex, &attr);
225 pthread_mutex_destroy(&
mutex);
238 struct timespec abstime;
239 if (!
GetAbsTime(&abstime, TimeoutMs) || pthread_mutex_timedlock(&
mutex, &abstime) != 0) {
246 pthread_mutex_lock(&
mutex);
256 pthread_mutex_unlock(&
mutex);
283 if (setpriority(PRIO_PROCESS, 0, Priority) < 0)
289 if (syscall(SYS_ioprio_set, 1, 0, (Priority & 0xff) | (3 << 13)) < 0)
299 va_start(ap, Description);
311 if (prctl(PR_SET_NAME, Thread->
description, 0, 0, 0) < 0)
327#define THREAD_STOP_TIMEOUT 3000
328#define THREAD_STOP_SLEEP 30
342 if (pthread_create(&
childTid, NULL, (
void *(*) (
void *))&
StartThread, (
void *)
this) == 0) {
368 if ((err = pthread_kill(
childTid, 0)) != 0) {
384 if (
active && WaitSeconds > -1) {
385 if (WaitSeconds > 0) {
407 static thread_local tThreadId threadId = 0;
409 threadId = syscall(__NR_gettid);
438 if (Mutex && !
mutex) {
439 if (!Mutex->
Lock(TimeoutMs, TimedOut))
476#define BT_BUF_SIZE 100
481 char *Function = NULL;
483 char *Address = NULL;
485 for (
char *q = Module; *q; q++) {
490 else if (*q ==
'+') {
498 else if (*q ==
']') {
504 char *DemangledFunction = NULL;
507 DemangledFunction = abi::__cxa_demangle(Function, NULL, 0, &status);
508 if (DemangledFunction)
509 Function = DemangledFunction;
515 unsigned long long addr = Address ? strtoull(Address, NULL, 0) : 0;
516 unsigned long long offs = Offset ? strtoull(Offset, NULL, 0) : 0;
522 while (e = strstr(e,
".so"))
524 if (p && !strchr(p,
'/')) {
526 if (dladdr(
reinterpret_cast<void*
>(addr), &dlinfo)) {
527 if ((strcmp(Module, dlinfo.dli_fname) == 0) && dlinfo.dli_fbase) {
528 unsigned long long base =
reinterpret_cast<unsigned long long>(dlinfo.dli_fbase);
536 cString cmd =
cString::sprintf(
"addr2line --functions --demangle --inlines --basename --exe=%s 0x%llx", Module, Function ? addr : offs);
538 if (p.
Open(cmd,
"r")) {
541 while (
char *l = rl.
Read(p)) {
543 if (Function && strcmp(l, Function))
552 free(DemangledFunction);
560 if (
char **s = backtrace_symbols(b, n)) {
561 for (
int i =
max(Level, 0) + 1; i < n; i++)
571 for (
int i = 0; i < sl.
Size(); i++) {
573 fprintf(f,
"%s\n", sl[i]);
582 Level =
max(Level, 0) + 1;
585 if (
char **s = backtrace_symbols(b, n)) {
587 Caller = Mangled ? s[Level] : *
Demangle(s[Level]);
597#define SLL_LENGTH 512
598#define SLL_THREADS 20
599#define SLL_MAX_LIST 9
600#define SLL_WRITE_FLAG 0x80000000
601#define SLL_LOCK_FLAG 0x40000000
619 void Check(
const char *Name,
bool Lock,
bool Write =
false);
628 memset(logCaller, 0,
sizeof(logCaller));
636 dsyslog(
"--- begin invalid lock sequence report");
639 for (
int i = 0; i <
SLL_SIZE; i++) {
643 q += sprintf(q,
"%5d", tid);
648 int Changed = LastFlags ^ Flags;
653 if ((Flags & b) != 0)
655 if ((Changed & b) != 0)
656 c = Lock ? Write ?
'W' :
'R' :
'U';
657 q += sprintf(q,
" %c", c);
659 q += sprintf(q,
" %c", Lock ?
'L' :
'U');
671 dsyslog(
"%5d invalid lock sequence: %s", ThreadId, Name);
674 dsyslog(
"--- end invalid lock sequence report");
675 dsyslog(
"--- THERE WILL BE NO FURTHER REPORTS UNTIL VDR IS RESTARTED!");
676 fprintf(stderr,
"invalid lock sequence at %s\n", *
DayDateTime(time(NULL)));
682 int n = *Name -
'0' - 1;
688 int AvailableIndex = -1;
698 if (AvailableIndex < 0) {
704 Index = AvailableIndex;
710 esyslog(
"ERROR: too many threads holding list locks at the same time - stopped logging locks!");
716 if ((
flags[Index] & ~b) < b)
718 else if ((
flags[Index] & b) == 0)
727 if (
flags[Index] == 0)
735 Dump(Name, ThreadId);
744#define dbglockseq(n, l, w) StateLockLog.Check(n, l, w)
746#define dbglockseq(n, l, w)
776 StateKey.
write =
true;
790 else if (TimeoutMs) {
795 static bool DoubleWriteLockReported =
false;
796 if (!DoubleWriteLockReported) {
797 dsyslog(
"WARNING: attempt to acquire write lock while already holding a write lock in the same thread - this may crash! (backtrace follows)");
799 DoubleWriteLockReported =
true;
809 esyslog(
"ERROR: cStateLock::Unlock() called with an unused key (tid=%d, lock=%s)",
threadId,
name);
814 esyslog(
"ERROR: cStateLock::Unlock() called without holding a write lock (tid=%d, lock=%s)",
threadId,
name);
825 if (StateKey.
write) {
826 StateKey.
write =
false;
839 esyslog(
"ERROR: cStateLock::SetSyncStateKey() called without holding a write lock (tid=%d, lock=%s)",
threadId,
name);
844 esyslog(
"ERROR: cStateLock::SetSyncStateKey() called with locked key (tid=%d, lock=%s)",
threadId,
name);
849 esyslog(
"ERROR: cStateLock::SetSyncStateKey() called twice (tid=%d, lock=%s)",
threadId,
name);
859 esyslog(
"ERROR: cStateLock::SetExplicitModify() called without holding a write lock (tid=%d, lock=%s)",
threadId,
name);
864 esyslog(
"ERROR: cStateLock::SetExplicitModify() called twice (tid=%d, lock=%s)",
threadId,
name);
874 esyslog(
"ERROR: cStateLock::SetModified() called without holding a write lock (tid=%d, lock=%s)",
threadId,
name);
910 esyslog(
"ERROR: cStateKey::Remove() called without holding a lock (key=%p)",
this);
918 esyslog(
"ERROR: cStateKey::StateChanged() called without holding a lock (tid=%d, key=%p)",
cThread::ThreadId(),
this);
993 if ((
pid = fork()) < 0) {
1000 const char *mode =
"w";
1004 if (strcmp(Mode,
"r") == 0) {
1009 if ((
f = fdopen(fd[1 - iopipe], mode)) == NULL) {
1011 close(fd[1 - iopipe]);
1016 int iofd = STDOUT_FILENO;
1017 if (strcmp(Mode,
"w") == 0) {
1019 iofd = STDIN_FILENO;
1022 if (dup2(fd[1 - iopipe], iofd) == -1) {
1023 close(fd[1 - iopipe]);
1027 int MaxPossibleFileDescriptors = getdtablesize();
1028 for (
int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
1030 if (execl(
"/bin/sh",
"sh",
"-c", Command, NULL) == -1) {
1031 close(fd[1 - iopipe]);
1052 ret = waitpid(
pid, &status, WNOHANG);
1054 if (errno != EINTR && errno != ECHILD) {
1059 else if (ret ==
pid)
1068 else if (ret == -1 || !WIFEXITED(status))
1082 if ((pid = fork()) < 0) {
1089 if (waitpid(pid, &status, 0) < 0) {
1101 pid_t sid = setsid();
1105 int devnull = open(
"/dev/null", O_RDONLY);
1106 if (devnull < 0 || dup2(devnull, 0) < 0)
1109 int MaxPossibleFileDescriptors = getdtablesize();
1110 for (
int i = STDERR_FILENO + 1; i < MaxPossibleFileDescriptors; i++)
1112 if (execl(
"/bin/sh",
"sh",
"-c", Command, NULL) == -1)
static void BackTrace(cStringList &StringList, int Level=0, bool Mangled=false)
Produces a backtrace and stores it in the given StringList.
static cString GetCaller(int Level=0, bool Mangled=false)
Returns the caller at the given Level (or the immediate caller, if Level is 0).
static cString Demangle(char *s)
Demangles the function name in the given string and returns the converted version of s.
bool TimedWait(cMutex &Mutex, int TimeoutMs)
bool Wait(int TimeoutMs=0)
Waits at most TimeoutMs milliseconds for a call to Signal(), or forever if TimeoutMs is 0.
void Signal(void)
Signals a caller of Wait() that the condition it is waiting for is met.
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
static std::atomic< int > count
void Activate(void)
Activates the global I/O throttling mechanism.
void Release(void)
Releases the global I/O throttling mechanism.
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
cMutexLock(cMutex *Mutex=NULL)
bool Lock(cMutex *Mutex, int TimeoutMs=0, bool *TimedOut=NULL)
Returns true if the mutex was successfully locked.
bool Lock(int TimeoutMs=0, bool *TimedOut=NULL)
Returns true if the mutex was successfully locked.
std::atomic< tThreadId > lockThreadId
bool Open(const char *Command, const char *Mode)
cRwLock(bool PreferWriter=false)
std::atomic< tThreadId > writeLockThreadId
bool Lock(bool Write, int TimeoutMs=0)
cStateKey(bool IgnoreFirst=false)
Sets up a new state key.
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
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 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
uint8_t logCounter[SLL_THREADS][SLL_MAX_LIST]
void Check(const char *Name, bool Lock, bool Write=false)
tThreadId logThreadIds[SLL_SIZE]
void Dump(const char *Name, tThreadId ThreadId)
void SetExplicitModify(void)
If you have obtained a write lock on this lock, and you don't want its state to be automatically incr...
cStateLock(const char *Name=NULL)
void Unlock(cStateKey &StateKey, bool IncState=true)
Releases a lock that has been obtained by a previous call to Lock() with the given StateKey.
void SetSyncStateKey(cStateKey &StateKey)
Sets the given StateKey to be synchronized to the state of this lock.
void SetModified(void)
Sets this lock to have its state incremented when the current write lock state key is removed.
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0)
Tries to get a lock and returns true if successful.
static cString static cString vsprintf(const char *fmt, va_list &ap)
static cString sprintf(const char *fmt,...) __attribute__((format(printf
cThreadLock(cThread *Thread=NULL)
bool Lock(cThread *Thread)
void SetIOPriority(int Priority)
std::atomic< tThreadId > childThreadId
static void SetMainThreadId(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...
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
void SetDescription(const char *Description,...) __attribute__((format(printf
std::atomic< bool > active
void SetPriority(int Priority)
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
static void * StartThread(cThread *Thread)
void Cancel(int WaitSeconds=0)
Cancels the thread by first setting 'running' to false, so that the Action() loop can finish in an or...
std::atomic< bool > running
static tThreadId mainThreadId
bool Active(void)
Checks whether the thread is still alive.
static tThreadId ThreadId(void)
uint64_t Elapsed(void) const
Returns the number of milliseconds that have elapsed since the last call to Set().
bool TimedOut(void) const
Returns true if the number of milliseconds given in the last call to Set() have passed.
virtual void Append(T Data)
static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
#define dbglockseq(n, l, w)
#define THREAD_STOP_SLEEP
int SystemExec(const char *Command, bool Detached)
#define THREAD_STOP_TIMEOUT
static cStateLockLog StateLockLog