@@ -102,6 +102,17 @@ async fn get_or_create_semaphore(locks: &FileSemaphores, filename: &str) -> Arc<
102102 . clone ( )
103103}
104104
105+ /// Remove semaphore entries that are no longer in use (strong_count == 1 means
106+ /// only the map itself holds a reference).
107+ async fn cleanup_semaphore ( locks : & FileSemaphores , filename : & str ) {
108+ let mut map = locks. write ( ) . await ;
109+ if let Some ( sem) = map. get ( filename) {
110+ if Arc :: strong_count ( sem) == 1 {
111+ map. remove ( filename) ;
112+ }
113+ }
114+ }
115+
105116struct ReceivedFile {
106117 original_filename : String ,
107118 cached_filename : String ,
@@ -566,6 +577,7 @@ async fn ax_post_file(
566577 let mut file0_filename: String = "" . to_string ( ) ;
567578 let mut upload_result: Option < ( StatusCode , Vec < u8 > ) > = None ;
568579 let mut buffered_file: Option < tempfile:: NamedTempFile > = None ;
580+ let mut locked_path: Option < String > = None ;
569581
570582 while let Some ( field) = match multipart. next_field ( ) . await {
571583 Ok ( field) => field,
@@ -694,6 +706,7 @@ async fn ax_post_file(
694706
695707 let hdr_content_type = headers. get ( "Content-Type-Upstream" ) ;
696708 let semaphore = get_or_create_semaphore ( & state. file_locks , & full_path) . await ;
709+ locked_path = Some ( full_path. clone ( ) ) ;
697710
698711 // Try to acquire permit - wait for up to 30 seconds
699712 let _permit = match tokio:: time:: timeout (
@@ -789,6 +802,11 @@ async fn ax_post_file(
789802 }
790803 }
791804
805+ // Clean up semaphore from the fast path (permit already dropped by leaving the loop)
806+ if let Some ( ref lp) = locked_path {
807+ cleanup_semaphore ( & state. file_locks , lp) . await ;
808+ }
809+
792810 // Handle buffered file0 case (file0 arrived before path)
793811 if upload_result. is_none ( ) {
794812 if let Some ( tmp) = buffered_file {
@@ -871,6 +889,9 @@ async fn ax_post_file(
871889
872890 // tmp (NamedTempFile) is dropped here, auto-deleting the temp file
873891
892+ drop ( _permit) ;
893+ cleanup_semaphore ( & state. file_locks , & full_path) . await ;
894+
874895 if result. is_empty ( ) {
875896 return ( StatusCode :: CONFLICT , Vec :: new ( ) ) ;
876897 }
@@ -958,6 +979,11 @@ async fn ax_get_file(
958979
959980 // IMPORTANT! Headers in cache must be stored in lowercase
960981 let received_file = driver_get_file ( filepath. clone ( ) ) ;
982+
983+ // Release the semaphore now that the file is resolved
984+ drop ( _permit) ;
985+ cleanup_semaphore ( & state. file_locks , & filepath) . await ;
986+
961987 if !received_file. valid {
962988 println ! (
963989 "{:?} 404 0 {} {} {} {}" ,
0 commit comments