@@ -463,6 +463,12 @@ pub async fn ensure_docker_client_if_needed() -> std::result::Result<Option<Path
463
463
. filter ( |u| !u. is_empty ( ) )
464
464
. unwrap_or_else ( || DEFAULT_DOCKER_CLIENT_URL . to_string ( ) ) ;
465
465
466
+ // If the docker client already exists, short-circuit without taking the lock
467
+ let target_path = get_workspace_root ( ) . join ( "resources/docker/docker-linux-amd64" ) ;
468
+ if target_path. exists ( ) {
469
+ return Ok ( Some ( target_path) ) ;
470
+ }
471
+
466
472
// Prepare lock file next to the docker binary directory
467
473
let lock_file_path = get_workspace_root ( ) . join ( "resources/docker/.docker.lock" ) ;
468
474
if let Some ( dir) = lock_file_path. parent ( ) {
@@ -475,9 +481,21 @@ pub async fn ensure_docker_client_if_needed() -> std::result::Result<Option<Path
475
481
. truncate ( false )
476
482
. open ( & lock_file_path) ?;
477
483
478
- // Acquire an exclusive cross-process lock
479
- use fs2:: FileExt ;
480
- lock_file. lock_exclusive ( ) ?;
484
+ // Acquire an exclusive cross-process lock with timeout to avoid deadlocks
485
+ let start = Instant :: now ( ) ;
486
+ let timeout = Duration :: from_secs ( 30 ) ;
487
+ loop {
488
+ // Prefer a non-blocking try-lock; retry until timeout
489
+ if fs2:: FileExt :: try_lock_exclusive ( & lock_file) . is_ok ( ) {
490
+ break ;
491
+ }
492
+ if start. elapsed ( ) >= timeout {
493
+ return Err ( std:: io:: Error :: other (
494
+ "Failed to acquire docker client lock within timeout" ,
495
+ ) ) ;
496
+ }
497
+ std:: thread:: sleep ( std:: time:: Duration :: from_millis ( 50 ) ) ;
498
+ }
481
499
482
500
// Ensure we always release the lock
483
501
let res = async {
0 commit comments