summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2017-04-10 13:16:15 +0200
committerwm4 <wm4@nowhere>2017-06-15 16:33:42 +0200
commitfa929eb0d0449bc88aa702f6790da324879c75a5 (patch)
tree8f666d4cf1a4388b0262eab79c8c657ad4aa1cbc
parenta3f0bf916ddf47ba4e94ad538e32e106756dedf7 (diff)
downloadmpv-fa929eb0d0449bc88aa702f6790da324879c75a5.tar.bz2
mpv-fa929eb0d0449bc88aa702f6790da324879c75a5.tar.xz
win32: pthread: avoid using TLS, simplify pthread_t
Don't use __thread, which requires heavy runtime in some cases (such as MinGW-w64, at least under some configurations, forcing you to link to its pthread runtime DLL). The pthread_t struct was needed over a simple thread ID, because pthread_join() needed to access some sort of context from pthread_t. Further, pthread_exit() and pthread_detach() need the context of the current thread, for which we relied on TLS. Replace these uses by a global thread array. This includes all threads created by the thread wrapper. Hopefully the number of threads created by mpv is low (say, below 20), and threads are not that often created or destroyed. So just keeping them in an array with linear search lookup should be reasonable.
-rw-r--r--osdep/win32/include/pthread.h11
-rw-r--r--osdep/win32/pthread.c126
2 files changed, 89 insertions, 48 deletions
diff --git a/osdep/win32/include/pthread.h b/osdep/win32/include/pthread.h
index 271cae0f0a..5157b8e342 100644
--- a/osdep/win32/include/pthread.h
+++ b/osdep/win32/include/pthread.h
@@ -28,7 +28,6 @@
#define pthread_mutex_unlock m_pthread_mutex_unlock
#define pthread_cond_timedwait m_pthread_cond_timedwait
#define pthread_cond_wait m_pthread_cond_wait
-#define pthread_self m_pthread_self
#define pthread_exit m_pthread_exit
#define pthread_join m_pthread_join
#define pthread_detach m_pthread_detach
@@ -80,15 +79,11 @@ int pthread_cond_timedwait(pthread_cond_t *restrict cond,
int pthread_cond_wait(pthread_cond_t *restrict cond,
pthread_mutex_t *restrict mutex);
-// Unusual, but allowed by POSIX.
-typedef struct {
- DWORD id;
- struct m_thread_info *info;
-} pthread_t;
+#define pthread_t DWORD
-#define pthread_equal(a, b) ((a).id == (b).id)
+#define pthread_equal(a, b) ((a) == (b))
+#define pthread_self() (GetCurrentThreadId())
-pthread_t pthread_self(void);
void pthread_exit(void *retval);
int pthread_join(pthread_t thread, void **retval);
int pthread_detach(pthread_t thread);
diff --git a/osdep/win32/pthread.c b/osdep/win32/pthread.c
index 57a9d85810..465ac94568 100644
--- a/osdep/win32/pthread.c
+++ b/osdep/win32/pthread.c
@@ -20,6 +20,7 @@
#include <stdint.h>
#include <errno.h>
#include <sys/time.h>
+#include <assert.h>
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
{
@@ -113,46 +114,76 @@ int pthread_cond_wait(pthread_cond_t *restrict cond,
return cond_wait(cond, mutex, INFINITE);
}
+static pthread_mutex_t pthread_table_lock = PTHREAD_MUTEX_INITIALIZER;
+static struct m_thread_info *pthread_table;
+size_t pthread_table_num;
+
struct m_thread_info {
+ DWORD id;
HANDLE handle;
void *(*user_fn)(void *);
void *user_arg;
void *res;
};
-// Assuming __thread maps to __declspec(thread)
-static __thread struct m_thread_info *self;
+static struct m_thread_info *find_thread_info(DWORD id)
+{
+ for (int n = 0; n < pthread_table_num; n++) {
+ if (id == pthread_table[n].id)
+ return &pthread_table[n];
+ }
+ return NULL;
+}
-pthread_t pthread_self(void)
+static void remove_thread_info(struct m_thread_info *info)
{
- return (pthread_t){GetCurrentThreadId(), self};
+ assert(pthread_table_num);
+ assert(info >= &pthread_table[0] && info < &pthread_table[pthread_table_num]);
+
+ pthread_table[info - pthread_table] = pthread_table[pthread_table_num - 1];
+ pthread_table_num -= 1;
+
+ // Avoid upsetting leak detectors.
+ if (pthread_table_num == 0) {
+ free(pthread_table);
+ pthread_table = NULL;
+ }
}
void pthread_exit(void *retval)
{
- if (!self)
- abort(); // not started with pthread_create
- self->res = retval;
- if (!self->handle) {
- // detached case
- free(self);
- self = NULL;
- }
+ pthread_mutex_lock(&pthread_table_lock);
+ struct m_thread_info *info = find_thread_info(pthread_self());
+ assert(info); // not started with pthread_create, or pthread_join() race
+ info->res = retval;
+ if (!info->handle)
+ remove_thread_info(info); // detached case
+ pthread_mutex_unlock(&pthread_table_lock);
+
ExitThread(0);
}
int pthread_join(pthread_t thread, void **retval)
{
- if (!thread.info)
- abort(); // not started with pthread_create
- HANDLE h = thread.info->handle;
- if (!h)
- abort(); // thread was detached
+ pthread_mutex_lock(&pthread_table_lock);
+ struct m_thread_info *info = find_thread_info(thread);
+ assert(info); // not started with pthread_create, or pthread_join() race
+ HANDLE h = info->handle;
+ assert(h); // thread was detached
+ pthread_mutex_unlock(&pthread_table_lock);
+
WaitForSingleObject(h, INFINITE);
+
+ pthread_mutex_lock(&pthread_table_lock);
+ info = find_thread_info(thread);
+ assert(info);
+ assert(info->handle == h);
CloseHandle(h);
if (retval)
- *retval = thread.info->res;
- free(thread.info);
+ *retval = info->res;
+ remove_thread_info(info);
+ pthread_mutex_unlock(&pthread_table_lock);
+
return 0;
}
@@ -160,19 +191,21 @@ int pthread_detach(pthread_t thread)
{
if (!pthread_equal(thread, pthread_self()))
abort(); // restriction of this wrapper
- if (!thread.info)
- abort(); // not started with pthread_create
- if (!thread.info->handle)
- abort(); // already deatched
- CloseHandle(thread.info->handle);
- thread.info->handle = NULL;
+
+ pthread_mutex_lock(&pthread_table_lock);
+ struct m_thread_info *info = find_thread_info(thread);
+ assert(info); // not started with pthread_create
+ assert(info->handle); // already detached
+ CloseHandle(info->handle);
+ info->handle = NULL;
+ pthread_mutex_unlock(&pthread_table_lock);
+
return 0;
}
static DWORD WINAPI run_thread(LPVOID lpParameter)
{
struct m_thread_info *info = lpParameter;
- self = info;
pthread_exit(info->user_fn(info->user_arg));
abort(); // not reached
}
@@ -180,20 +213,33 @@ static DWORD WINAPI run_thread(LPVOID lpParameter)
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg)
{
- struct m_thread_info *info = calloc(1, sizeof(*info));
- if (!info)
- return EAGAIN;
- info->user_fn = start_routine;
- info->user_arg = arg;
- HANDLE h = CreateThread(NULL, 0, run_thread, info, CREATE_SUSPENDED, NULL);
- if (!h) {
- free(info);
- return EAGAIN;
+ int res = 0;
+ pthread_mutex_lock(&pthread_table_lock);
+ void *nalloc =
+ realloc(pthread_table, (pthread_table_num + 1) * sizeof(pthread_table[0]));
+ if (!nalloc) {
+ res = EAGAIN;
+ goto done;
}
- info->handle = h;
- *thread = (pthread_t){GetThreadId(h), info};
- ResumeThread(h);
- return 0;
+ pthread_table = nalloc;
+ pthread_table_num += 1;
+ struct m_thread_info *info = &pthread_table[pthread_table_num - 1];
+ *info = (struct m_thread_info) {
+ .user_fn = start_routine,
+ .user_arg = arg,
+ };
+ info->handle = CreateThread(NULL, 0, run_thread, info, CREATE_SUSPENDED,
+ &info->id);
+ if (!info->handle) {
+ remove_thread_info(info);
+ res = EAGAIN;
+ goto done;
+ }
+ *thread = info->id;
+ ResumeThread(info->handle);
+done:
+ pthread_mutex_unlock(&pthread_table_lock);
+ return res;
}
void pthread_set_name_np(pthread_t thread, const char *name)
@@ -206,7 +252,7 @@ void pthread_set_name_np(pthread_t thread, const char *name)
if (!pSetThreadDescription)
return;
- HANDLE th = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, thread.id);
+ HANDLE th = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, thread);
if (!th)
return;
wchar_t wname[80];