Skip to content

Commit 28139bf

Browse files
committed
unix,win: fix threadpool race condition
90891b4 introduced a race condition when accessing `slow_io_work_running` – it is being increased and later decreased as part of the worker thread loop, but was accessed with different mutexes during these operations. This fixes the race condition by making sure both accesses are protected through the global `mutex` of `threadpool.c`. This fixes a number of flaky Node.js tests. Refs: libuv#1845 Refs: nodejs/reliability#18 Refs: nodejs/node#23089 Refs: nodejs/node#23067 Refs: nodejs/node#23066 Refs: nodejs/node#23219
1 parent 6781db5 commit 28139bf

File tree

1 file changed

+12
-6
lines changed

1 file changed

+12
-6
lines changed

src/threadpool.c

+12-6
Original file line numberDiff line numberDiff line change
@@ -62,10 +62,10 @@ static void worker(void* arg) {
6262
uv_sem_post((uv_sem_t*) arg);
6363
arg = NULL;
6464

65+
uv_mutex_lock(&mutex);
6566
for (;;) {
66-
uv_mutex_lock(&mutex);
67+
/* `mutex` should always be locked at this point. */
6768

68-
wait_for_work:
6969
/* Keep waiting while either no work is present or only slow I/O
7070
and we're at the threshold for that. */
7171
while (QUEUE_EMPTY(&wq) ||
@@ -93,13 +93,13 @@ static void worker(void* arg) {
9393
other work in the queue is done. */
9494
if (slow_io_work_running >= slow_work_thread_threshold()) {
9595
QUEUE_INSERT_TAIL(&wq, q);
96-
goto wait_for_work;
96+
continue;
9797
}
9898

9999
/* If we encountered a request to run slow I/O work but there is none
100100
to run, that means it's cancelled => Start over. */
101101
if (QUEUE_EMPTY(&slow_io_pending_wq))
102-
goto wait_for_work;
102+
continue;
103103

104104
is_slow_work = 1;
105105
slow_io_work_running++;
@@ -122,13 +122,19 @@ static void worker(void* arg) {
122122
w->work(w);
123123

124124
uv_mutex_lock(&w->loop->wq_mutex);
125-
if (is_slow_work)
126-
slow_io_work_running--;
127125
w->work = NULL; /* Signal uv_cancel() that the work req is done
128126
executing. */
129127
QUEUE_INSERT_TAIL(&w->loop->wq, &w->wq);
130128
uv_async_send(&w->loop->wq_async);
131129
uv_mutex_unlock(&w->loop->wq_mutex);
130+
131+
/* Lock `mutex` since that is expected at the start of the next
132+
* iteration. */
133+
uv_mutex_lock(&mutex);
134+
if (is_slow_work) {
135+
/* `slow_io_work_running` is protected by `mutex`. */
136+
slow_io_work_running--;
137+
}
132138
}
133139
}
134140

0 commit comments

Comments
 (0)