Skip to content

Commit fa929eb

Browse files
author
wm4
committed
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.
1 parent a3f0bf9 commit fa929eb

File tree

2 files changed

+89
-48
lines changed

2 files changed

+89
-48
lines changed

osdep/win32/include/pthread.h

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
#define pthread_mutex_unlock m_pthread_mutex_unlock
2929
#define pthread_cond_timedwait m_pthread_cond_timedwait
3030
#define pthread_cond_wait m_pthread_cond_wait
31-
#define pthread_self m_pthread_self
3231
#define pthread_exit m_pthread_exit
3332
#define pthread_join m_pthread_join
3433
#define pthread_detach m_pthread_detach
@@ -80,15 +79,11 @@ int pthread_cond_timedwait(pthread_cond_t *restrict cond,
8079
int pthread_cond_wait(pthread_cond_t *restrict cond,
8180
pthread_mutex_t *restrict mutex);
8281

83-
// Unusual, but allowed by POSIX.
84-
typedef struct {
85-
DWORD id;
86-
struct m_thread_info *info;
87-
} pthread_t;
82+
#define pthread_t DWORD
8883

89-
#define pthread_equal(a, b) ((a).id == (b).id)
84+
#define pthread_equal(a, b) ((a) == (b))
85+
#define pthread_self() (GetCurrentThreadId())
9086

91-
pthread_t pthread_self(void);
9287
void pthread_exit(void *retval);
9388
int pthread_join(pthread_t thread, void **retval);
9489
int pthread_detach(pthread_t thread);

osdep/win32/pthread.c

Lines changed: 86 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include <stdint.h>
2121
#include <errno.h>
2222
#include <sys/time.h>
23+
#include <assert.h>
2324

2425
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))
2526
{
@@ -113,87 +114,132 @@ int pthread_cond_wait(pthread_cond_t *restrict cond,
113114
return cond_wait(cond, mutex, INFINITE);
114115
}
115116

117+
static pthread_mutex_t pthread_table_lock = PTHREAD_MUTEX_INITIALIZER;
118+
static struct m_thread_info *pthread_table;
119+
size_t pthread_table_num;
120+
116121
struct m_thread_info {
122+
DWORD id;
117123
HANDLE handle;
118124
void *(*user_fn)(void *);
119125
void *user_arg;
120126
void *res;
121127
};
122128

123-
// Assuming __thread maps to __declspec(thread)
124-
static __thread struct m_thread_info *self;
129+
static struct m_thread_info *find_thread_info(DWORD id)
130+
{
131+
for (int n = 0; n < pthread_table_num; n++) {
132+
if (id == pthread_table[n].id)
133+
return &pthread_table[n];
134+
}
135+
return NULL;
136+
}
125137

126-
pthread_t pthread_self(void)
138+
static void remove_thread_info(struct m_thread_info *info)
127139
{
128-
return (pthread_t){GetCurrentThreadId(), self};
140+
assert(pthread_table_num);
141+
assert(info >= &pthread_table[0] && info < &pthread_table[pthread_table_num]);
142+
143+
pthread_table[info - pthread_table] = pthread_table[pthread_table_num - 1];
144+
pthread_table_num -= 1;
145+
146+
// Avoid upsetting leak detectors.
147+
if (pthread_table_num == 0) {
148+
free(pthread_table);
149+
pthread_table = NULL;
150+
}
129151
}
130152

131153
void pthread_exit(void *retval)
132154
{
133-
if (!self)
134-
abort(); // not started with pthread_create
135-
self->res = retval;
136-
if (!self->handle) {
137-
// detached case
138-
free(self);
139-
self = NULL;
140-
}
155+
pthread_mutex_lock(&pthread_table_lock);
156+
struct m_thread_info *info = find_thread_info(pthread_self());
157+
assert(info); // not started with pthread_create, or pthread_join() race
158+
info->res = retval;
159+
if (!info->handle)
160+
remove_thread_info(info); // detached case
161+
pthread_mutex_unlock(&pthread_table_lock);
162+
141163
ExitThread(0);
142164
}
143165

144166
int pthread_join(pthread_t thread, void **retval)
145167
{
146-
if (!thread.info)
147-
abort(); // not started with pthread_create
148-
HANDLE h = thread.info->handle;
149-
if (!h)
150-
abort(); // thread was detached
168+
pthread_mutex_lock(&pthread_table_lock);
169+
struct m_thread_info *info = find_thread_info(thread);
170+
assert(info); // not started with pthread_create, or pthread_join() race
171+
HANDLE h = info->handle;
172+
assert(h); // thread was detached
173+
pthread_mutex_unlock(&pthread_table_lock);
174+
151175
WaitForSingleObject(h, INFINITE);
176+
177+
pthread_mutex_lock(&pthread_table_lock);
178+
info = find_thread_info(thread);
179+
assert(info);
180+
assert(info->handle == h);
152181
CloseHandle(h);
153182
if (retval)
154-
*retval = thread.info->res;
155-
free(thread.info);
183+
*retval = info->res;
184+
remove_thread_info(info);
185+
pthread_mutex_unlock(&pthread_table_lock);
186+
156187
return 0;
157188
}
158189

159190
int pthread_detach(pthread_t thread)
160191
{
161192
if (!pthread_equal(thread, pthread_self()))
162193
abort(); // restriction of this wrapper
163-
if (!thread.info)
164-
abort(); // not started with pthread_create
165-
if (!thread.info->handle)
166-
abort(); // already deatched
167-
CloseHandle(thread.info->handle);
168-
thread.info->handle = NULL;
194+
195+
pthread_mutex_lock(&pthread_table_lock);
196+
struct m_thread_info *info = find_thread_info(thread);
197+
assert(info); // not started with pthread_create
198+
assert(info->handle); // already detached
199+
CloseHandle(info->handle);
200+
info->handle = NULL;
201+
pthread_mutex_unlock(&pthread_table_lock);
202+
169203
return 0;
170204
}
171205

172206
static DWORD WINAPI run_thread(LPVOID lpParameter)
173207
{
174208
struct m_thread_info *info = lpParameter;
175-
self = info;
176209
pthread_exit(info->user_fn(info->user_arg));
177210
abort(); // not reached
178211
}
179212

180213
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
181214
void *(*start_routine) (void *), void *arg)
182215
{
183-
struct m_thread_info *info = calloc(1, sizeof(*info));
184-
if (!info)
185-
return EAGAIN;
186-
info->user_fn = start_routine;
187-
info->user_arg = arg;
188-
HANDLE h = CreateThread(NULL, 0, run_thread, info, CREATE_SUSPENDED, NULL);
189-
if (!h) {
190-
free(info);
191-
return EAGAIN;
216+
int res = 0;
217+
pthread_mutex_lock(&pthread_table_lock);
218+
void *nalloc =
219+
realloc(pthread_table, (pthread_table_num + 1) * sizeof(pthread_table[0]));
220+
if (!nalloc) {
221+
res = EAGAIN;
222+
goto done;
192223
}
193-
info->handle = h;
194-
*thread = (pthread_t){GetThreadId(h), info};
195-
ResumeThread(h);
196-
return 0;
224+
pthread_table = nalloc;
225+
pthread_table_num += 1;
226+
struct m_thread_info *info = &pthread_table[pthread_table_num - 1];
227+
*info = (struct m_thread_info) {
228+
.user_fn = start_routine,
229+
.user_arg = arg,
230+
};
231+
info->handle = CreateThread(NULL, 0, run_thread, info, CREATE_SUSPENDED,
232+
&info->id);
233+
if (!info->handle) {
234+
remove_thread_info(info);
235+
res = EAGAIN;
236+
goto done;
237+
}
238+
*thread = info->id;
239+
ResumeThread(info->handle);
240+
done:
241+
pthread_mutex_unlock(&pthread_table_lock);
242+
return res;
197243
}
198244

199245
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)
206252
if (!pSetThreadDescription)
207253
return;
208254

209-
HANDLE th = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, thread.id);
255+
HANDLE th = OpenThread(THREAD_SET_LIMITED_INFORMATION, FALSE, thread);
210256
if (!th)
211257
return;
212258
wchar_t wname[80];

0 commit comments

Comments
 (0)