Skip to content

Commit 2b25f94

Browse files
fix: Avoid deadlock on downsizing of threadpool
1 parent 2b52a16 commit 2b25f94

File tree

1 file changed

+16
-8
lines changed

1 file changed

+16
-8
lines changed

src/thread_pool.rs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -440,25 +440,29 @@ impl ThreadPool {
440440
}
441441
// The size decreased
442442
cmp::Ordering::Less => {
443+
let mut heartbeat_handle = None;
444+
443445
// Halt the heartbeat thread when scaling to zero.
444-
if let Some(control) = state.managed_threads.heartbeat.take() {
446+
if new_size == 0 && let Some(control) = state.managed_threads.heartbeat.take() {
445447
control.halt.store(true, Ordering::Relaxed);
446-
let _ = control.handle.join();
448+
heartbeat_handle = Some(control.handle);
447449
}
448450

449451
// Pull the workers we intend to halt out of the thread manager.
450452
let terminating_workers = state.managed_threads.workers.split_off(new_size);
451453

452-
drop(state);
453-
454454
// Terminate the workers.
455455
for worker in &terminating_workers {
456456
// Tell the worker to halt.
457457
worker.control.halt.store(true, Ordering::Relaxed);
458458
}
459459

460460
// Wake any sleeping workers to ensure they will eventually see the termination notice.
461-
// self.job_is_ready.notify_all();
461+
for seat in &state.seats {
462+
seat.data.sleep_controller.wake();
463+
}
464+
465+
drop(state);
462466

463467
let own_lease = Worker::map_current(|worker| worker.lease.index);
464468

@@ -470,6 +474,10 @@ impl ThreadPool {
470474
let _ = worker.control.handle.join();
471475
}
472476
}
477+
478+
if let Some(handle) = heartbeat_handle {
479+
let _ = handle.join();
480+
}
473481
}
474482
}
475483

@@ -906,7 +914,7 @@ impl Worker {
906914
/// Cooperatively yields execution to the threadpool, allowing it to execute
907915
/// some work.
908916
///
909-
/// Tis function may execute either local or shared work: work already
917+
/// This function may execute either local or shared work: work already
910918
/// queued on the worker, or work off-loaded by a different worker. If there
911919
/// is no work on the pool, this will lock the thread-pool mutex, so it
912920
/// should not be called within a hot loop. Consider using
@@ -1134,7 +1142,7 @@ impl Worker {
11341142
// the queue must point to `stack_job`, implying that
11351143
// `stack_job` cannot have been executed yet.
11361144
let a = unsafe { stack_job.unwrap() };
1137-
// Execute the closure directly and return the results. This is
1145+
// Execute the closure directly and return the results. This
11381146
// allows the compiler to inline and optimize `a`.
11391147
let result_a = a(self);
11401148
return (result_a, result_b);
@@ -1553,7 +1561,7 @@ fn heartbeat_loop(thread_pool: &'static ThreadPool, halt: Arc<AtomicBool>) {
15531561
queued_to_heartbeat = (seat_index + 1) % num_seats;
15541562
}
15551563

1556-
// Count every occupied slot, even if we didn't sent them a heartbeat.
1564+
// Count every occupied slot, even if we didn't send them a heartbeat.
15571565
num_occupied += 1;
15581566
}
15591567
}

0 commit comments

Comments
 (0)