Watch, Follow, &
Connect with Us
Public Report
Report From: Delphi-BCB/RTL/Delphi/Thread support    [ Add a report in this area ]  
Report #:  111795   Status: Open
Race-condition in TMonitor code causing application to hang on exit
Project:  Delphi Build #:  XE2, XE3
Version:    17.1 Submitted By:   Cosmin Prund
Report Type:  Basic functionality failure Date Reported:  1/8/2013 11:25:12 AM
Severity:    Serious / Highly visible problem Last Updated: 1/23/2013 4:50:54 AM
Platform:    All versions Internal Tracking #:   35304
Resolution: None (Resolution Comments) Resolved in Build: : None
Duplicate of:  None
Voting and Rating
Overall Rating: (1 Total Rating)
5.00 out of 5
Total Votes: 1
Description
There's a race condition in TMonitor code causing the application to hang when terminating.

Note:
I can confirm the issue is resolved by freeing SyncObj.
None the less I still consider this a serious bug. The penalty for not freeing a small object should be a small memory leak, not an application that hangs for ever.
Steps to Reproduce:
program Project29;
{$APPTYPE CONSOLE}

uses SysUtils, Windows, Classes;

// Thread declaration, because Delphi 2010 doesn't have an anonymous way of creating a small thread.
type
  TTestThread = class(TThread)
  public
    procedure Execute;override;
  end;

// Those constants control the test
const TestLenSeconds = 1;
      ThreadCount = 4;

var
  SyncObj: TObject; // ALL threads will use this object to sync, because I want them to clash!
  Handles: array [1..ThreadCount] of Cardinal;
  i: Integer;

// The thread-routine. Very simple: Loops until the specified number of seconds passes,
// inside the loop doesn an empty try-finally Enter-Exit using TMonitor. Technically
// nothing should go wrong in there!
procedure TTestThread.Execute;
var
  StopAt: TDateTime;
begin
  StopAt := Now + EncodeTime(0, TestLenSeconds div 60, TestLenSeconds mod 60, 0);
  while Now < StopAt do
  begin
    TMonitor.Enter(SyncObj);
    try
    finally
      TMonitor.Exit(SyncObj);
    end;
  end;
end;

// Main
begin
  try
    try
      // Preapre the object that we'll synchronize on
      SyncObj := TObject.Create;

      // Start ThreadCount threads threads
      for i:=1 to 4 do
        with TTestThread.Create(False) do
        begin
          Handles[i] := Handle;
        end;

      WriteLn('All ', ThreadCount, ' threads are started, please wait ', TestLenSeconds, ' seconds for the threads to finish.');
      // Wait for all the threads to finish
      WaitForMultipleObjects(ThreadCount, @Handles, True, INFINITE);

      // Wait until they're all done
      WriteLn('All threads are done.');

    except on E: Exception do Writeln(E.ClassName, ': ', E.Message);
    end;
  finally
    WriteLn('Press ENTER to Exit');
    ReadLn;
    WriteLn('ENTER received, application exiting.');
    WriteLn('If you get to read this, the application is now stuck and will never finish.');
  end;
end.
Workarounds
The problem is solved by calling SyncObj.Free after all threads are done.
Attachment
None
Comments

Leif Uneus at 1/8/2013 11:40:49 AM -
I can confirm this behavior in XE3 Version 17.0.4723.55752.

It gets stuck in SysUtils DoneMonitorSupport when calling CleanEventList

Paul Thornton at 1/8/2013 2:27:58 PM -
I am also experiencing the same issue in XE2. My application is very thread intensive and hangs in the same place when shutting down the app.

Uwe Raabe at 1/8/2013 4:23:54 PM -
The problem is solved by calling SyncObj.Free after all threads are done.

Cosmin Prund at 1/22/2013 2:24:01 AM -
I can confirm the issue is resolved by freeing SyncObj.

None the less I still consider this a serious bug. The penalty for not freeing a small object should be a small memory leak, not an application that hangs for ever.

Besides, I might have chosen to intentionally never free that object: We're talking about the shutdown of the application, all memory is about to be reclaimed by the operating system anyhow.

Leif Uneus at 1/31/2013 3:04:42 PM -
The QC 99324 "Process hangs on shutdown after using TMonitor to wait on an object that isn't freed" seems to be related.

Server Response from: ETNACODE01