Skip to content

Commit a00a69d

Browse files
authored
Fix deadlock in test_pthread_dlopen_many (emscripten-core#18518)
Avoid locking the DSO list during dlsync. Instead only lock during dlopen (when adding new DSEs to the list). This avoid problems where static constructors were run during dlopen but other threads were blocked on dlsync while holding other locks (such as the malloc lock or the stdout lock).
1 parent e6cdb66 commit a00a69d

1 file changed

Lines changed: 25 additions & 28 deletions

File tree

system/lib/libc/dynlink.c

Lines changed: 25 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -50,15 +50,19 @@ static struct dso * _Atomic head, * _Atomic tail;
5050

5151
#ifdef _REENTRANT
5252
static thread_local struct dso* thread_local_tail;
53-
static pthread_rwlock_t lock;
53+
static pthread_mutex_t write_lock = PTHREAD_MUTEX_INITIALIZER;
5454

55-
static void dlsync_locked() {
55+
static void dlsync() {
5656
if (!thread_local_tail) {
5757
thread_local_tail = head;
5858
}
59+
if (!thread_local_tail->next) {
60+
return;
61+
}
62+
dbg("dlsync: catching up %p %p", thread_local_tail, tail);
5963
while (thread_local_tail->next) {
6064
struct dso* p = thread_local_tail->next;
61-
dbg("dlsync_locked: %s mem_addr=%p "
65+
dbg("dlsync: %s mem_addr=%p "
6266
"mem_size=%zu table_addr=%p table_size=%zu",
6367
p->name,
6468
p->mem_addr,
@@ -76,6 +80,7 @@ static void dlsync_locked() {
7680
}
7781
thread_local_tail = p;
7882
}
83+
dbg("dlsync: done");
7984
}
8085

8186
// This function is called from emscripten_yield which itself is called whenever
@@ -89,38 +94,31 @@ void _emscripten_thread_sync_code() {
8994
if (skip_dlsync) {
9095
return;
9196
}
92-
skip_dlsync = true;
9397
ensure_init();
98+
if (!thread_local_tail) {
99+
thread_local_tail = head;
100+
}
94101
if (thread_local_tail != tail) {
95-
dbg("emscripten_thread_sync_code: catching up %p %p", thread_local_tail, tail);
96-
pthread_rwlock_rdlock(&lock);
97-
dlsync_locked();
98-
pthread_rwlock_unlock(&lock);
99-
dbg("emscripten_thread_sync_code: done");
102+
skip_dlsync = true;
103+
dlsync();
104+
skip_dlsync = false;
100105
}
101-
skip_dlsync = false;
102-
}
103-
104-
static void do_read_lock() {
105-
skip_dlsync = true;
106-
pthread_rwlock_rdlock(&lock);
107106
}
108107

109108
static void do_write_lock() {
110109
// Once we have the lock we want to avoid automatic code sync as that would
111110
// result in a deadlock.
112111
skip_dlsync = true;
113-
pthread_rwlock_wrlock(&lock);
112+
pthread_mutex_lock(&write_lock);
114113
}
115114

116-
static void do_unlock() {
117-
pthread_rwlock_unlock(&lock);
115+
static void do_write_unlock() {
116+
pthread_mutex_unlock(&write_lock);
118117
skip_dlsync = false;
119118
}
120119
#else
121-
#define do_unlock()
122-
#define do_read_lock()
123120
#define do_write_lock()
121+
#define do_write_unlock()
124122
#endif
125123

126124
static void error(const char* fmt, ...) {
@@ -190,14 +188,14 @@ static void dlopen_js_onsuccess(struct dso* dso, struct async_data* data) {
190188
dso->mem_addr,
191189
dso->mem_size);
192190
load_library_done(dso);
193-
do_unlock();
191+
do_write_lock();
194192
data->onsuccess(data->user_data, dso);
195193
free(data);
196194
}
197195

198196
static void dlopen_js_onerror(struct dso* dso, struct async_data* data) {
199197
dbg("dlopen_js_onerror: dso=%p", dso);
200-
do_unlock();
198+
do_write_unlock();
201199
data->onerror(data->user_data);
202200
free(dso);
203201
free(data);
@@ -219,7 +217,7 @@ static void ensure_init() {
219217
load_library_done(p);
220218
assert(head);
221219
}
222-
do_unlock();
220+
do_write_unlock();
223221
}
224222

225223
void* dlopen(const char* file, int flags) {
@@ -235,7 +233,7 @@ void* dlopen(const char* file, int flags) {
235233
do_write_lock();
236234
#ifdef _REENTRANT
237235
// Make sure we are in sync before loading any new DSOs.
238-
dlsync_locked();
236+
dlsync();
239237
#endif
240238

241239
/* Search for the name to see if it's already loaded */
@@ -260,8 +258,9 @@ void* dlopen(const char* file, int flags) {
260258
dbg("dlopen_js: success: %p", p);
261259
load_library_done(p);
262260
end:
263-
do_unlock();
261+
do_write_unlock();
264262
pthread_setcancelstate(cs, 0);
263+
dbg("dlopen: %s done", file);
265264
return p;
266265
}
267266

@@ -275,7 +274,7 @@ void emscripten_dlopen(const char* filename, int flags, void* user_data,
275274
do_write_lock();
276275
struct dso* p = load_library_start(filename, flags);
277276
if (!p) {
278-
do_unlock();
277+
do_write_unlock();
279278
onerror(user_data);
280279
return;
281280
}
@@ -297,9 +296,7 @@ void* __dlsym(void* restrict p, const char* restrict s, void* restrict ra) {
297296
return 0;
298297
}
299298
void* res;
300-
do_read_lock();
301299
res = _dlsym_js(p, s);
302-
do_unlock();
303300
dbg("__dlsym done dso:%p res:%p", p, res);
304301
return res;
305302
}

0 commit comments

Comments
 (0)