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: (3 Total Ratings)
5.00 out of 5
Total Votes: 22
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.

Tomasz Kunicki at 5/23/2013 1:10:13 PM -
This is very serious. When closing service application we can't release hanging threads and synchronizing objects on network operations (waiting for them would take too long) so this code is now hanging our application EVERY TIME!!!

Anyway its serious BUG not a feature!

Tomasz Kunicki at 5/23/2013 1:15:33 PM -
To make things clear. It's not race condition.
The bug is cause by stupid idea to indefinitely wait for all synchronization objects (cached in SysUtils) to be freed.

This is another proof that DELPHI is not a professional product It's just for writing small single threaded applications.

Server Response from: ETNACODE01