[Mono-bugs] [Bug 47232][Maj] New - Finalizers never called in multi-threaded applications

bugzilla-daemon@rocky.ximian.com bugzilla-daemon@rocky.ximian.com
Fri, 1 Aug 2003 05:50:59 -0400 (EDT)


Please do not reply to this email- if you want to comment on the bug, go to the
URL shown below and enter your comments there.

Changed by giuseppe.greco@agamura.com.

http://bugzilla.ximian.com/show_bug.cgi?id=47232

--- shadow/47232	Fri Aug  1 05:50:59 2003
+++ shadow/47232.tmp.15221	Fri Aug  1 05:50:59 2003
@@ -0,0 +1,137 @@
+Bug#: 47232
+Product: Mono/Runtime
+Version: unspecified
+OS: Red Hat 9.0
+OS Details: 
+Status: NEW   
+Resolution: 
+Severity: 
+Priority: Major
+Component: misc
+AssignedTo: mono-bugs@ximian.com                            
+ReportedBy: giuseppe.greco@agamura.com               
+QAContact: mono-bugs@ximian.com
+TargetMilestone: ---
+URL: 
+Summary: Finalizers never called in multi-threaded applications
+
+Description of Problem:
+
+If a class owns a worker thread, the finalizer is never called,
+and the application just exits brutally.
+
+If you compile the same code after commenting out the code for
+creating the working thread, then the finalizer is called and
+the application exits gracefully.
+
+
+Steps to reproduce the problem:
+
+1. Write a small application like this:
+
+using System;
+using System.Threading;
+                                                                          
+           
+public class MyFinalizer : IDisposable
+{
+  Thread thread;
+  bool isDisposed = false;
+                                                                          
+           
+  public MyFinalizer()
+  {
+    Console.WriteLine("Constructor");
+                                                                          
+           
+    thread = new Thread(new ThreadStart(DoSomething));
+    thread.Start();
+    thread.IsBackground = true;
+  }
+                                                                          
+           
+  private void DoSomething()
+  {
+    while (!isDisposed) {
+      Thread.Sleep(5000);
+    }
+  }
+                                                                          
+           
+  ~MyFinalizer()
+  {
+    Console.WriteLine("Destructor");
+    Dispose(false);
+  }
+
+  public void Dispose()
+  {
+    Dispose(true);
+    GC.SuppressFinalize(this);
+  }
+                                                                          
+           
+  protected virtual void Dispose(bool disposing)
+  {
+    if (!isDisposed) {
+      Console.WriteLine("Free unmanaged resources.");
+                                                                          
+           
+      //
+      // wait until the working thread completes
+      // its job...
+      //
+      thread.Join();
+
+      if (disposing) {
+        Console.WriteLine("Finalize will be suppressed.");
+        Console.WriteLine("Free resource manually.");
+      }
+                                                                          
+           
+      isDisposed = true;
+    }
+  }
+                                                                          
+           
+  public static void Main()
+  {
+    MyFinalizer f = new MyFinalizer();
+    Console.WriteLine("Bye!");
+  }
+}
+ 
+2. Compile it:
+
+mcs -t:exe -out:finalizer.exe *.cs
+ 
+3. Execute it:
+
+mono ./finalizer.exe 
+
+
+Actual Results:
+
+The destructor (or finalizer) is never called and the
+working thread is interrupted brutally.
+
+
+Expected Results:
+
+The destructor should be called, which in turn calls
+the Dispose() method. Dispose() allows the application
+to exit gracefully by waiting until the working thread
+completes its job - Thread.Join().
+How often does this happen? 
+
+
+Additional Information:
+
+If you comment out the following lines
+
+    // thread = new Thread(new ThreadStart(DoSomething));
+    // thread.Start();
+    // thread.IsBackground = true;
+
+then the finalizer is called and the dispose pattern works
+fine.