Commit 541879c4ac for freeswitch.com

commit 541879c4ac7000c608eb547ada3e02d5d80f035b
Author: Andrey Volk <andywolk@gmail.com>
Date:   Mon Sep 28 21:09:45 2026 +0300

    [Build-System] Windows: Fix a download hang on a missed completion signal and keep retrying when an incomplete file cannot be deleted (#3196)

diff --git a/w32/downloadpackage.task b/w32/downloadpackage.task
index 6132131ac2..013e3b70bd 100644
--- a/w32/downloadpackage.task
+++ b/w32/downloadpackage.task
@@ -41,6 +41,7 @@ using System.Diagnostics;
             public long lastActivityTicks;
             public Exception error;
             public bool cancelled;
+            public bool completed;
         }

         protected ManualResetEvent TaskCanceled { get; private set; }
@@ -213,7 +214,16 @@ using System.Diagnostics;
                 {
                     Log.LogMessage(MessageImportance.High,
                         "Deleting incomplete file " + output + " for package \"" + package + "\".");
-                    File.Delete(output);
+                    try
+                    {
+                        File.Delete(output);
+                    }
+                    catch (Exception e)
+                    {
+                        // A scanner may still hold the file; the next attempt
+                        // overwrites it anyway, so do not let this end the retries.
+                        Log.LogWarning("Could not delete " + output + ": " + e.Message);
+                    }
                 }

                 if (TaskCanceled.WaitOne(0))
@@ -242,12 +252,19 @@ using System.Diagnostics;
             {
                 client.DownloadFileAsync(uri, output, syncObject);

-                while (!Monitor.Wait(syncObject, 1000))
+                // Wait on the completed flag, not on Monitor.Wait's result: a pulse
+                // that lands while a timed-out Wait is reacquiring the lock is lost.
+                while (!syncObject.completed)
                 {
+                    if (Monitor.Wait(syncObject, 1000) || syncObject.completed)
+                    {
+                        continue;
+                    }
+
                     if (TaskCanceled.WaitOne(0))
                     {
                         client.CancelAsync();
-                        Monitor.Wait(syncObject);
+                        WaitCompleted(syncObject);
                         return "canceled";
                     }

@@ -257,7 +274,7 @@ using System.Diagnostics;
                     if (idle > TimeSpan.FromSeconds(stallTimeoutSeconds).Ticks)
                     {
                         client.CancelAsync();
-                        Monitor.Wait(syncObject);
+                        WaitCompleted(syncObject);
                         return "no data for " + stallTimeoutSeconds + " s";
                     }
                 }
@@ -282,6 +299,15 @@ using System.Diagnostics;
             return null;
         }

+        // Caller holds the lock on state.
+        private static void WaitCompleted(State state)
+        {
+            while (!state.completed)
+            {
+                Monitor.Wait(state);
+            }
+        }
+
         private static string Describe(Exception error)
         {
             var web = error as WebException;
@@ -353,6 +379,7 @@ using System.Diagnostics;
             {
                 state.error = e.Error;
                 state.cancelled = e.Cancelled;
+                state.completed = true;
                 //releases blocked thread
                 Monitor.Pulse(state);
             }