The Thread.Join method has three overloads: Join(), Join(Int32) and Join(TimeSpan). For each of these three overloads, there is the following statement in the Microsoft doc:
If the thread has already terminated when Join is called, the method returns immediately.
While this statement makes sense for the Join() overload, it doesn't specify which value is returned for the Join(Int32) and Join(TimeSpan) ones, so I tested the Int32 overload in two different environments:
Note that the Linux/Docker implementation is returning true (like the Windows one) if the thread is still running when Join is called and has terminated after the call. It only returns false if the thread has terminated before the call.
In my opinion Join should always return true whatever the platform, so what could explain this inconsistent behavior? Am I missing something or is it a .NET 5 bug?
UPDATE
As suggested by @txtechhelp, here is a .NET Fiddle with the exact code I'm testing.
If I run this code on Windows 10 (or in .NET Fiddle) I get the following result:
Starting..
Sleeping 1200..expect T1 end before join
In T1
Leaving T1
Join(100)..expect success
Join(100) success!
Done..
Then if I run this code using mcr.microsoft.com/dotnet/runtime:5.0 on Docker Desktop (v. 3.1.0) then I get the following result:
Starting..
Sleeping 1200..expect T1 end before join
In T1
Leaving T1
Join(100)..expect success
Join(100) failed
Done..
UPDATE 2
Actually after further testing I realized that the test above is only failing if I call the Join when the Docker application is unloading (i.e. after receiving the AssemblyLoadContext.Default.Unloading event, which is the signal sent by Docker to inform that it's going to shutdown the application).
So here is the exact test which is even failing on .NET Fiddle:
public class Program
{
public static void Main()
{
System.Runtime.Loader.AssemblyLoadContext.Default.Unloading += (arg) => { OnStopSignalReceived("application unloading"); };
}
public static void T1()
{
System.Console.WriteLine("In T1");
System.Threading.Thread.Sleep(1000);
System.Console.WriteLine("Leaving T1");
}
private static void OnStopSignalReceived(string stopSignalSource)
{
System.Threading.Thread t1 = new System.Threading.Thread(T1);
System.Console.WriteLine("Starting..");
t1.Start();
System.Console.WriteLine("Sleeping 1200..expect T1 end before join");
System.Threading.Thread.Sleep(1200);
System.Console.WriteLine("Join(100)..expect success");
if (t1.Join(100))
{
System.Console.WriteLine("Join(100) success!");
}
else
{
System.Console.WriteLine("Join(100) failed");
}
t1.Join();
System.Console.WriteLine("Done..");
}
}