According to the "What's New in Delphi 2009," there is a new debugger features called "wait chain traversal." It specifically says "A Wait Chain Traversal feature has been added to help you resolve thread contention or deadlock issues. The feature relies on a facility added to the Windows Vista operating system that provides information to the debugger about the wait status of your application's threads in the form of a wait chain."
Delphi 2009 was released when Windows Vista was the current operating system. From my experience, most of the features introduced in Vista are also available in Windows 7. However, I do not see this feature anywhere in my Delphi 2009 through Delphi XE installations (all on Windows 7).
I am looking for this feature on the debugger's Threads pane.
Am I looking for wait chain traversal in the correct place?
Is it a features truly only available in Windows Vista, and not in Windows 7?
David M provided a nice and clear answer, but I still don't get the Wait Chain column in the Threads pane. Here is some code.
Main Form:
unit Main;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, SyncObjs, RanThread;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Button1: TButton;
Button2: TButton;
Label1: TLabel;
procedure Button2Click(Sender: TObject);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
procedure ThreadDone(Sender: TObject);
end;
var
Form1: TForm1;
RanGenThread: TRandomizer;
implementation
uses LoadThread;
{$R *.dfm}
{ TForm1 }
procedure TForm1.ThreadDone(Sender: TObject);
begin
RanGenThread.Free;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ListBox1.Sorted := True;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Thread: TLoader;
begin
ListBox1.Items.Clear;
ListBox1.Sorted := False;
RanGenThread := TRandomizer.Create(True);
RanGenThread.ArraySize := 1000;
Thread := TLoader.Create(True);
with Thread do
begin
RanGenThread.WaitThread := Thread;
FreeOnTerminate := True;
OnTerminate := ThreadDone;
WaitForThread := RanGenThread;
//Use Start in Delphi 2010 or later, where Resume is deprecated
Resume;
end;
RanGenThread.Resume;
end;
initialization
Randomize;
end.
TRandomizer:
unit RanThread;
interface
uses
Classes, Math, SyncObjs;
type
TRandomizer = class(TThread)
private
{ Private declarations }
FArraySize: Integer;
protected
procedure Execute; override;
public
WaitThread: TThread;
RandNumbers: array of Integer;
property ArraySize: Integer read FArraySize write FArraySize;
end;
implementation
uses Main;
procedure TRandomizer.Execute;
var
i: Integer;
LowNum, HighNum: Integer;
RandNum: Integer;
begin
if FArraySize = 0 then
begin
Exit;
end;
SetLength(RandNumbers, FArraySize);
LowNum := Low(RandNumbers);
HighNum := High(RandNumbers);
//initialize the array
for i := LowNum to HighNum do
RandNumbers[i] := -1;
// generate the random order
for i := LowNum to HighNum do
while True do
begin
RandNum := RandomRange(LowNum, HighNum + 1);
if RandNumbers[RandNum] = -1 then
begin
RandNumbers[RandNum] := i + 1;
break;
end; // if
end; // while
WaitThread.WaitFor;
end;
end.
TLoader:
unit LoadThread;
interface
uses
Classes, SyncObjs, Dialogs, SysUtils, RanThread;
type
TLoader = class(TThread)
private
FWaitForThread: TRandomizer;
procedure UpdateList;
{ Private declarations }
protected
procedure Execute; override;
public
property WaitForThread: TRandomizer
read FWaitForThread write FWaitForThread;
end;
implementation
uses Main;
procedure TLoader.UpdateList;
var
i: Integer;
begin
for i := Low(FWaitForThread.RandNumbers) to
High(FWaitForThread.RandNumbers) do
Form1.ListBox1.Items.Add(IntToStr(FWaitForThread.RandNumbers[i]));
end;
procedure TLoader.Execute;
begin
if WaitForThread <> nil then
begin
FWaitForThread.WaitFor;
Synchronize(UpDateList)
end;
end;
end.
According to the wait chain traversal document that David M linked to, WTC is available for the following synchronization objects:
My code waits on a thread, but it is a TThread, and not directly a native thread. This evening I will modify my code example to deadlock on waiting for a Mutex, and see if that results in the Wait Chain column appearing in the Thread pane.
OK. Finally found time for the next test. Created an application that took ownership of a Mutex upon startup. Created a worker thread that used OpenMutex to get a handle to that Mutex and then called WaitForsingleObject(handle, INFINITE). Still no Wait Chain column in the Threads pane.
Yes. Write a program with two threads that deadlock. You will see, on the Threads pane, the right-hand column (labeled "Wait Chain") will say something like "Waiting for lock held by thread 12345." I'm not completely certain of the exact phrase since I don't have any deadlocks in my current program right now :) If you don't see it, scroll right. If the column simply doesn't exist, that's very strange - please say so in a comment.
No. Wait chain traversal was introduced in Vista (and Server 2008) and is also available on Windows 7. You can see this by reading the MSDN documentation on wait chain traversal and looking at some of the core functions, which all have comments in the table at the bottom saying "Minimum supported client: Windows Vista". It would say if it was removed in a later version of Windows - the way it's phrased is exactly the way it is for any number of other functions. Also, a colleague using Windows 7 (I have Vista) has this feature.
Edit: I tried your example code in Delphi 2010 (I don't have 2009 I'm afraid) on my Vista machine. I clicked Button1 and Button2, and then switched to the Threads view without pausing the program. The Threads pane had a 'Wait Chain' column, which contained text for the two non-main-thread threads 'Blocked waiting for Thread 11968' or '14416'.
Here's a screenshot:
So that's what you should get in your IDE if this feature is working.
When you try this, do you get the other columns but not the Wait Chain column? If so I admit I'm a bit puzzled, and I think the next step might be to contact Embarcadero or at least post on one of their forums, probably this one. I did look up what permissions are required for the wait chain functions to work, but it seems like you don't even need SE_DEBUG_NAME
if you're working with a process owned by the current user. (I'm not completely certain that's a valid interpretation of the documentation, but...)
I don't suppose your IDE is running under odd privileges or in XP compatibility mode or anything? Edit 4: This is the answer. See Cary's comment below, the shortcut was running in XP compatibility mode.
Edit 2: I thought I'd do a sanity check and look up what their help file says. I can't find the 2009 help online, but the 2010 help says 'Wait Chain: (For Windows Vista and 7 only)'. Cool. Then I looked at the XE help, and it says 'Wait Chain: (For Windows Vista only)'. Very strange.
I suspect this is a documentation bug and it is supposed to work on Windows 7, because I also came across this Delphi 2009 hotfix for wait chain traversal running on Windows 7. Sounds pretty supported to me! There is no way, surely, they'd remove a feature like this in the newest version of their product for the newest version of Windows... is there? Even if they did you're using a version it's documented to work in.
Edit 3: I asked my Windows 7-using colleague to try your example code too. This is what he got:
At this point I admit I'm a bit puzzled. All I can say is that works using RAD Studio 2010 on Windows 7 and, as far as I can tell, should work for you. If it's not I have no more suggestions beyond the few vague ideas above at the moment!
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With