Skip to content

Commit 5d87322

Browse files
committed
fix semaphore memory leak
Signed-off-by: Denys Fedoryshchenko <denys.f@collabora.com>
1 parent ff6d8e1 commit 5d87322

1 file changed

Lines changed: 26 additions & 0 deletions

File tree

src/main.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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+
105116
struct 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

Comments
 (0)