@@ -7,6 +7,7 @@ use std::{
77 process:: { ExitStatus , Stdio } ,
88} ;
99
10+ use fs4:: fs_std:: FileExt ;
1011use semver:: { Version , VersionReq } ;
1112use serde:: { Deserialize , Serialize } ;
1213use tokio:: { fs:: remove_dir_all, process:: Command } ;
@@ -374,9 +375,21 @@ async fn download_package_manager(
374375 tracing:: debug!( "Rename package dir to {}" , bin_name) ;
375376 tokio:: fs:: rename ( & target_dir_tmp. join ( "package" ) , & target_dir_tmp. join ( & bin_name) ) . await ?;
376377
377- // check bin_file again, for the concurrent download cases
378+ // Use a file-based lock to ensure atomicity of remove + rename operations
379+ // This prevents DirectoryNotEmpty exceptions when multiple processes/threads
380+ // try to install the same package manager version concurrently.
381+ // Create lock file with a unique name based on the target directory
382+ let lock_path = parent_dir. join ( format ! ( "{version}.lock" ) ) ;
383+ tracing:: debug!( "Acquiring lock file: {:?}" , lock_path) ;
384+ let _lock_file = File :: create ( & lock_path) ?;
385+ // Acquire exclusive lock (blocks until available)
386+ _lock_file. lock_exclusive ( ) ?;
387+ tracing:: debug!( "Lock acquired: {:?}" , lock_path) ;
388+
389+ // Check again after acquiring the lock, in case another thread completed
390+ // the installation while we were downloading
378391 if is_exists_file ( & bin_file) ? {
379- tracing:: debug!( "bin_file already exists, skip rename" ) ;
392+ tracing:: debug!( "bin_file already exists after lock acquisition , skip rename" ) ;
380393 return Ok ( install_dir) ;
381394 }
382395
@@ -395,12 +408,13 @@ async fn download_package_manager(
395408/// Remove the directory and all its contents.
396409/// Ignore the error if the directory is not found.
397410async fn remove_dir_all_force ( path : impl AsRef < Path > ) -> Result < ( ) , std:: io:: Error > {
398- match remove_dir_all ( path) . await {
411+ match remove_dir_all ( path. as_ref ( ) ) . await {
399412 Ok ( ( ) ) => Ok ( ( ) ) ,
400413 Err ( e) => {
401414 if e. kind ( ) == std:: io:: ErrorKind :: NotFound {
402415 Ok ( ( ) )
403416 } else {
417+ tracing:: error!( "remove_dir_all_force path: {:?} error: {e:?}" , path. as_ref( ) ) ;
404418 Err ( e)
405419 }
406420 }
0 commit comments