summaryrefslogtreecommitdiff
path: root/src/libpcp/src/lock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/libpcp/src/lock.c')
-rw-r--r--src/libpcp/src/lock.c284
1 files changed, 284 insertions, 0 deletions
diff --git a/src/libpcp/src/lock.c b/src/libpcp/src/lock.c
new file mode 100644
index 0000000..0bcf2a1
--- /dev/null
+++ b/src/libpcp/src/lock.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 2013 Red Hat.
+ * Copyright (c) 2011 Ken McDonell. All Rights Reserved.
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published
+ * by the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
+ * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
+ * License for more details.
+ */
+
+#include "pmapi.h"
+#include "impl.h"
+#include "internal.h"
+#ifdef HAVE_EXECINFO_H
+#include <execinfo.h>
+#endif
+
+#ifdef PM_MULTI_THREAD
+typedef struct {
+ void *lock;
+ int count;
+} lockdbg_t;
+
+#define PM_LOCK_OP 1
+#define PM_UNLOCK_OP 2
+
+static int multi_init[PM_SCOPE_MAX+1];
+static pthread_t multi_seen[PM_SCOPE_MAX+1];
+
+/* the big libpcp lock */
+#ifdef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+pthread_mutex_t __pmLock_libpcp = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;
+#else
+pthread_mutex_t __pmLock_libpcp = PTHREAD_MUTEX_INITIALIZER;
+
+#ifndef HAVE___THREAD
+pthread_key_t __pmTPDKey = 0;
+
+static void
+__pmTPD__destroy(void *addr)
+{
+ free(addr);
+}
+#endif
+#endif
+
+void
+__pmInitLocks(void)
+{
+ static pthread_mutex_t init = PTHREAD_MUTEX_INITIALIZER;
+ static int done = 0;
+ int psts;
+ char errmsg[PM_MAXERRMSGLEN];
+ if ((psts = pthread_mutex_lock(&init)) != 0) {
+ pmErrStr_r(-psts, errmsg, sizeof(errmsg));
+ fprintf(stderr, "__pmInitLocks: pthread_mutex_lock failed: %s", errmsg);
+ exit(4);
+ }
+ if (!done) {
+#ifndef PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP
+ /*
+ * Unable to initialize at compile time, need to do it here in
+ * a one trip for all threads run-time initialization.
+ */
+ pthread_mutexattr_t attr;
+
+ if ((psts = pthread_mutexattr_init(&attr)) != 0) {
+ pmErrStr_r(-psts, errmsg, sizeof(errmsg));
+ fprintf(stderr, "__pmInitLocks: pthread_mutexattr_init failed: %s", errmsg);
+ exit(4);
+ }
+ if ((psts = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) != 0) {
+ pmErrStr_r(-psts, errmsg, sizeof(errmsg));
+ fprintf(stderr, "__pmInitLocks: pthread_mutexattr_settype failed: %s", errmsg);
+ exit(4);
+ }
+ if ((psts = pthread_mutex_init(&__pmLock_libpcp, &attr)) != 0) {
+ pmErrStr_r(-psts, errmsg, sizeof(errmsg));
+ fprintf(stderr, "__pmInitLocks: pthread_mutex_init failed: %s", errmsg);
+ exit(4);
+ }
+#endif
+#ifndef HAVE___THREAD
+ /* first thread here creates the thread private data key */
+ if ((psts = pthread_key_create(&__pmTPDKey, __pmTPD__destroy)) != 0) {
+ pmErrStr_r(-psts, errmsg, sizeof(errmsg));
+ fprintf(stderr, "__pmInitLocks: pthread_key_create failed: %s", errmsg);
+ exit(4);
+ }
+#endif
+ done = 1;
+ }
+ if ((psts = pthread_mutex_unlock(&init)) != 0) {
+ pmErrStr_r(-psts, errmsg, sizeof(errmsg));
+ fprintf(stderr, "__pmInitLocks: pthread_mutex_unlock failed: %s", errmsg);
+ exit(4);
+ }
+#ifndef HAVE___THREAD
+ if (pthread_getspecific(__pmTPDKey) == NULL) {
+ __pmTPD *tpd = (__pmTPD *)malloc(sizeof(__pmTPD));
+ if (tpd == NULL) {
+ __pmNoMem("__pmInitLocks: __pmTPD", sizeof(__pmTPD), PM_FATAL_ERR);
+ /*NOTREACHED*/
+ }
+ if ((psts = pthread_setspecific(__pmTPDKey, tpd)) != 0) {
+ pmErrStr_r(-psts, errmsg, sizeof(errmsg));
+ fprintf(stderr, "__pmInitLocks: pthread_setspecific failed: %s", errmsg);
+ exit(4);
+ }
+ tpd->curcontext = PM_CONTEXT_UNDEF;
+ }
+#endif
+}
+
+int
+__pmMultiThreaded(int scope)
+{
+ int sts = 0;
+
+ PM_INIT_LOCKS();
+ PM_LOCK(__pmLock_libpcp);
+ if (!multi_init[scope]) {
+ multi_init[scope] = 1;
+ multi_seen[scope] = pthread_self();
+ }
+ else {
+ if (!pthread_equal(multi_seen[scope], pthread_self()))
+ sts = 1;
+ }
+ PM_UNLOCK(__pmLock_libpcp);
+ return sts;
+}
+
+#ifdef PM_MULTI_THREAD_DEBUG
+void
+__pmDebugLock(int op, void *lock, const char *file, int line)
+{
+ int report = 0;
+ int ctx;
+ static __pmHashCtl hashctl = { 0, 0, NULL };
+ __pmHashNode *hp = NULL;
+ lockdbg_t *ldp;
+ int try;
+ int sts;
+
+ if (lock == (void *)&__pmLock_libpcp) {
+ if ((pmDebug & DBG_TRACE_APPL0) | ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1 | DBG_TRACE_APPL2)) == 0))
+ report = DBG_TRACE_APPL0;
+ }
+ else if ((ctx = __pmIsContextLock(lock)) >= 0) {
+ if ((pmDebug & DBG_TRACE_APPL1) | ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1 | DBG_TRACE_APPL2)) == 0))
+ report = DBG_TRACE_APPL1;
+ }
+ else {
+ if ((pmDebug & DBG_TRACE_APPL2) | ((pmDebug & (DBG_TRACE_APPL0 | DBG_TRACE_APPL1 | DBG_TRACE_APPL2)) == 0))
+ report = DBG_TRACE_APPL2;
+ }
+
+ if (report) {
+ __psint_t key = (__psint_t)lock;
+ fprintf(stderr, "%s:%d %s", file, line, op == PM_LOCK_OP ? "lock" : "unlock");
+ try = 0;
+again:
+ hp = __pmHashSearch((unsigned int)key, &hashctl);
+ while (hp != NULL) {
+ ldp = (lockdbg_t *)hp->data;
+ if (ldp->lock == lock)
+ break;
+ hp = hp->next;
+ }
+ if (hp == NULL) {
+ char errmsg[PM_MAXERRMSGLEN];
+ ldp = (lockdbg_t *)malloc(sizeof(lockdbg_t));
+ ldp->lock = lock;
+ ldp->count = 0;
+ sts = __pmHashAdd((unsigned int)key, (void *)ldp, &hashctl);
+ if (sts == 1) {
+ try++;
+ if (try == 1)
+ goto again;
+ }
+ hp = NULL;
+ fprintf(stderr, " hash control failure: %s\n", pmErrStr_r(-sts, errmsg, sizeof(errmsg)));
+ }
+ }
+
+ if (report == DBG_TRACE_APPL0) {
+ fprintf(stderr, "(global_libpcp)");
+ }
+ else if (report == DBG_TRACE_APPL1) {
+ fprintf(stderr, "(ctx %d)", ctx);
+ }
+ else if (report == DBG_TRACE_APPL2) {
+ if ((ctx = __pmIsChannelLock(lock)) >= 0)
+ fprintf(stderr, "(ctx %d ipc channel)", ctx);
+ else if (__pmIsDeriveLock(lock))
+ fprintf(stderr, "(derived_metric)");
+ else
+ fprintf(stderr, "(" PRINTF_P_PFX "%p)", lock);
+ }
+ if (report) {
+ if (hp != NULL) {
+ ldp = (lockdbg_t *)hp->data;
+ if (op == PM_LOCK_OP) {
+ if (ldp->count != 0)
+ fprintf(stderr, " [count=%d]", ldp->count);
+ ldp->count++;
+ }
+ else {
+ if (ldp->count != 1)
+ fprintf(stderr, " [count=%d]", ldp->count);
+ ldp->count--;
+ }
+ }
+ fputc('\n', stderr);
+#ifdef HAVE_BACKTRACE
+#define MAX_TRACE_DEPTH 32
+ {
+ void *backaddr[MAX_TRACE_DEPTH];
+ sts = backtrace(backaddr, MAX_TRACE_DEPTH);
+ if (sts > 0) {
+ char **symbols;
+ symbols = backtrace_symbols(backaddr, MAX_TRACE_DEPTH);
+ if (symbols != NULL) {
+ int i;
+ fprintf(stderr, "backtrace:\n");
+ for (i = 0; i < sts; i++)
+ fprintf(stderr, " %s\n", symbols[i]);
+ free(symbols);
+ }
+ }
+ }
+
+#endif
+ }
+}
+#else
+#define __pmDebugLock(op, lock, file, line) do { } while (0)
+#endif
+
+int
+__pmLock(void *lock, const char *file, int line)
+{
+ int sts;
+
+ if (pmDebug & DBG_TRACE_LOCK)
+ __pmDebugLock(PM_LOCK_OP, lock, file, line);
+
+ if ((sts = pthread_mutex_lock(lock)) != 0) {
+ sts = -sts;
+ if (pmDebug & DBG_TRACE_DESPERATE)
+ fprintf(stderr, "%s:%d: lock failed: %s\n", file, line, pmErrStr(sts));
+ }
+ return sts;
+}
+
+int
+__pmUnlock(void *lock, const char *file, int line)
+{
+ int sts;
+
+ if (pmDebug & DBG_TRACE_LOCK)
+ __pmDebugLock(PM_UNLOCK_OP, lock, file, line);
+
+ if ((sts = pthread_mutex_unlock(lock)) != 0) {
+ sts = -sts;
+ if (pmDebug & DBG_TRACE_DESPERATE)
+ fprintf(stderr, "%s:%d: unlock failed: %s\n", file, line, pmErrStr(sts));
+ }
+ return sts;
+}
+
+#else /* !PM_MULTI_THREAD - symbols exposed at the shlib ABI level */
+void *__pmLock_libpcp;
+void __pmInitLocks(void) { }
+int __pmMultiThreaded(int scope) { (void)scope; return 0; }
+int __pmLock(void *l, const char *f, int n) { (void)l, (void)f, (void)n; return 0; }
+int __pmUnlock(void *l, const char *f, int n) { (void)l, (void)f, (void)n; return 0; }
+#endif