summaryrefslogtreecommitdiffstats
path: root/osdep
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2020-03-18 22:34:48 +0100
committerwm4 <wm4@nowhere>2020-03-18 22:42:13 +0100
commitcb82cbbbae99d5b8beebae76320a9097a2bc34f7 (patch)
tree144715054c3ac0ee203dcc23004e583923334961 /osdep
parent373dad9962e4d7b23b95fc95bbbe811c8adafc92 (diff)
downloadmpv-cb82cbbbae99d5b8beebae76320a9097a2bc34f7.tar.bz2
mpv-cb82cbbbae99d5b8beebae76320a9097a2bc34f7.tar.xz
osdep: add a pthread debugging wrapper
Because pthread failures are virtually undebuggable (which sure is pretty strange, given all these heavy instrumentation tools these days). Of course it affects only files which include osdep/threads.h. I'm departing from the usual way to add symbols with config.h and using "#if", and defining it on the compiler command line + "#ifdef" because I don't want to include config.h from a header (which would be necessary in this case) to keep things slightly cleaner. Maybe this is misguided, but still. This would have been easier if mpv defined its own wrappers for all thread functions. But we don't (which to be honest is probably better than e.g. going crazy like VLC and essentially reimplementing everything). This seems to be a good compromise. Since it's off by default and basically a developer tool, the minor undefined behavior (redefining reserved symbols) isn't much of an issue.
Diffstat (limited to 'osdep')
-rw-r--r--osdep/threads.c28
-rw-r--r--osdep/threads.h55
2 files changed, 82 insertions, 1 deletions
diff --git a/osdep/threads.c b/osdep/threads.c
index 8cd4545c1e..5b164c5179 100644
--- a/osdep/threads.c
+++ b/osdep/threads.c
@@ -19,8 +19,8 @@
#include <errno.h>
#include <pthread.h>
+#include "common/common.h"
#include "config.h"
-
#include "threads.h"
#include "timer.h"
@@ -49,3 +49,29 @@ void mpthread_set_name(const char *name)
pthread_setname_np(tname);
#endif
}
+
+int mp_ptwrap_check(const char *file, int line, int res)
+{
+ if (res && res != ETIMEDOUT) {
+ fprintf(stderr, "%s:%d: internal error: pthread result %d (%s)\n",
+ file, line, res, mp_strerror(res));
+ abort();
+ }
+ return res;
+}
+
+int mp_ptwrap_mutex_init(const char *file, int line, pthread_mutex_t *m,
+ const pthread_mutexattr_t *attr)
+{
+ pthread_mutexattr_t m_attr;
+ if (!attr) {
+ attr = &m_attr;
+ pthread_mutexattr_init(&m_attr);
+ // Force normal mutexes to error checking.
+ pthread_mutexattr_settype(&m_attr, PTHREAD_MUTEX_ERRORCHECK);
+ }
+ int res = mp_ptwrap_check(file, line, (pthread_mutex_init)(m, attr));
+ if (attr == &m_attr)
+ pthread_mutexattr_destroy(&m_attr);
+ return res;
+}
diff --git a/osdep/threads.h b/osdep/threads.h
index 8633618009..1c5dbf8a3e 100644
--- a/osdep/threads.h
+++ b/osdep/threads.h
@@ -10,4 +10,59 @@ int mpthread_mutex_init_recursive(pthread_mutex_t *mutex);
// Set thread name (for debuggers).
void mpthread_set_name(const char *name);
+int mp_ptwrap_check(const char *file, int line, int res);
+int mp_ptwrap_mutex_init(const char *file, int line, pthread_mutex_t *m,
+ const pthread_mutexattr_t *attr);
+
+#ifdef MP_PTHREAD_DEBUG
+
+// pthread debugging wrappers. Technically, this is undefined behavior, because
+// you are not supposed to define any symbols that clash with reserved names.
+// Other than that, they should be fine.
+
+// Note: mpv normally never checks pthread error return values of certain
+// functions that should never fail. It does so because these cases would
+// be undefined behavior anyway (such as double-frees etc.). However,
+// since there are no good pthread debugging tools, these wrappers are
+// provided for the sake of debugging. They crash on unexpected errors.
+//
+// Technically, pthread_cond/mutex_init() can fail with ENOMEM. We don't
+// really respect this for normal/recursive mutex types, as due to the
+// existence of static initializers, no sane implementation could actually
+// require allocating memory.
+
+#define MP_PTWRAP(fn, ...) \
+ mp_ptwrap_check(__FILE__, __LINE__, (fn)(__VA_ARGS__))
+
+// ISO C defines that all standard functions can be macros, except undef'ing
+// them is allowed and must make the "real" definitions available. (Whatever.)
+#undef pthread_cond_init
+#undef pthread_cond_destroy
+#undef pthread_cond_broadcast
+#undef pthread_cond_signal
+#undef pthread_cond_wait
+#undef pthread_cond_timedwait
+#undef pthread_detach
+#undef pthread_join
+#undef pthread_mutex_destroy
+#undef pthread_mutex_lock
+#undef pthread_mutex_unlock
+
+#define pthread_cond_init(...) MP_PTWRAP(pthread_cond_init, __VA_ARGS__)
+#define pthread_cond_destroy(...) MP_PTWRAP(pthread_cond_destroy, __VA_ARGS__)
+#define pthread_cond_broadcast(...) MP_PTWRAP(pthread_cond_broadcast, __VA_ARGS__)
+#define pthread_cond_signal(...) MP_PTWRAP(pthread_cond_signal, __VA_ARGS__)
+#define pthread_cond_wait(...) MP_PTWRAP(pthread_cond_wait, __VA_ARGS__)
+#define pthread_cond_timedwait(...) MP_PTWRAP(pthread_cond_timedwait, __VA_ARGS__)
+#define pthread_detach(...) MP_PTWRAP(pthread_detach, __VA_ARGS__)
+#define pthread_join(...) MP_PTWRAP(pthread_join, __VA_ARGS__)
+#define pthread_mutex_destroy(...) MP_PTWRAP(pthread_mutex_destroy, __VA_ARGS__)
+#define pthread_mutex_lock(...) MP_PTWRAP(pthread_mutex_lock, __VA_ARGS__)
+#define pthread_mutex_unlock(...) MP_PTWRAP(pthread_mutex_unlock, __VA_ARGS__)
+
+#define pthread_mutex_init(...) \
+ mp_ptwrap_mutex_init(__FILE__, __LINE__, __VA_ARGS__)
+
+#endif
+
#endif