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) {
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 SetDescription(const char *Description,...) __attribute__((format(printf
#define dbglockseq(n, l, w)
cVector< tThreadId > threadIds
void Unlock(cStateKey &StateKey, bool IncState=true)
Releases a lock that has been obtained by a previous call to Lock() with the given StateKey...
uint64_t Elapsed(void) const
void Signal(void)
Signals a caller of Wait() that the condition it is waiting for is met.
static cString sprintf(const char *fmt,...) __attribute__((format(printf
static bool GetAbsTime(struct timespec *Abstime, int MillisecondsFromNow)
virtual void Append(T Data)
void Remove(bool IncState=true)
Removes this key from the lock it was previously used with.
void SetPriority(int Priority)
cThreadLock(cThread *Thread=NULL)
#define THREAD_STOP_TIMEOUT
static cStateLockLog StateLockLog
void Dump(const char *Name, tThreadId ThreadId)
tThreadId writeLockThreadId
static cString Demangle(char *s)
Demangles the function name in the given string and returns the converted version of s...
cRwLock(bool PreferWriter=false)
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...
void SetModified(void)
Sets this lock to have its state incremented when the current write lock state key is removed...
static cString static cString vsprintf(const char *fmt, va_list &ap)
cMutexLock(cMutex *Mutex=NULL)
static void SleepMs(int TimeoutMs)
Creates a cCondWait object and uses it to sleep for TimeoutMs milliseconds, immediately giving up the...
static void * StartThread(cThread *Thread)
bool StateChanged(void)
Returns true if this key is used for obtaining a write lock, and the lock's state differs from that o...
void bool Start(void)
Sets the description of this thread, which will be used when logging starting or stopping of the thre...
bool Open(const char *Command, const char *Mode)
#define THREAD_STOP_SLEEP
void SetSyncStateKey(cStateKey &StateKey)
Sets the given StateKey to be synchronized to the state of this lock.
bool Lock(bool Write, int TimeoutMs=0)
bool Lock(cStateKey &StateKey, bool Write=false, int TimeoutMs=0)
Tries to get a lock and returns true if successful.
bool Wait(int TimeoutMs=0)
Waits at most TimeoutMs milliseconds for a call to Signal(), or forever if TimeoutMs is 0...
static tThreadId ThreadId(void)
bool TimedWait(cMutex &Mutex, int TimeoutMs)
int SystemExec(const char *Command, bool Detached)
void Activate(void)
Activates the global I/O throttling mechanism.
cStateLock(const char *Name=NULL)
tThreadId logThreadIds[SLL_SIZE]
void SetIOPriority(int Priority)
static void SetMainThreadId(void)
bool Active(void)
Checks whether the thread is still alive.
void SetExplicitModify(void)
If you have obtained a write lock on this lock, and you don't want its state to be automatically incr...
static bool Engaged(void)
Returns true if any I/O throttling object is currently active.
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).
uint8_t logCounter[SLL_THREADS][SLL_MAX_LIST]
void Check(const char *Name, bool Lock, bool Write=false)
cThread(const char *Description=NULL, bool LowPriority=false)
Creates a new thread.
bool Lock(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...
static tThreadId mainThreadId
cStateKey(bool IgnoreFirst=false)
Sets up a new state key.
void Release(void)
Releases the global I/O throttling mechanism.