We help IT Professionals succeed at work.

We've partnered with Certified Experts, Carl Webster and Richard Faulkner, to bring you two Citrix podcasts. Learn about 2020 trends and get answers to your biggest Citrix questions!Listen Now

x

Problem with WaitForSingleObject

Actia
Actia asked
on
Medium Priority
1,263 Views
Last Modified: 2010-04-05
Hello,

Please have a look at the code below.
What it should be doing is:

1. User clicks the SpawnButton
2. ('Calculation began...') info is displayed
3. Thread starts the execution- it calculates if the entered number is prime
4. Once it's finished it displays the result and SetEvent(hEvent)
5. Main Thread waits until Thread finishes the calculation. It uses WaitForSingleObject(hEvent, INFINITE).

the problem is that 'WaitForSingleObject(hEvent, INFINITE)' never returns the value. It looks like this function
just never finds out that SetEvent(hEvent) was ever called- and it should because it IS CALLED.

What do I do wrong?

Jack


unit PrimeForm;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TPrimeFrm = class(TForm)
    SpawnButton: TButton;
    NumEdit: TEdit;
    ResultsMemo: TMemo;
    procedure SpawnButtonClick(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  PrimeFrm: TPrimeFrm;

implementation

uses PrimeThread;

{$R *.dfm}

procedure TPrimeFrm.SpawnButtonClick(Sender: TObject);
var
  NewThread: TPrimeThrd;

begin
  ResultsMemo.Lines.Add('Calculation began...');

  NewThread := TPrimeThrd.Create(True);
  NewThread.FreeOnTerminate := True;
  try
    NewThread.TestNumber := StrToInt(Trim(NumEdit.Text));
    ResetEvent(hEvent);
    NewThread.Resume;  
  except
    on EConvertError do
    begin
      NewThread.Free;
      ShowMessage('That is not a valid number!');
    end;
  end;


  case WaitForSingleObject(hEvent, INFINITE) of   //***PROBLEM
    WAIT_TIMEOUT : ResultsMemo.Lines.Add('It takes too long');
    WAIT_OBJECT_0 : ResultsMemo.Lines.Add('OK');
    WAIT_FAILED : ResultsMemo.Lines.Add('Failed');
  end;

  //***it NEVER returns the value, looks like SetEvent(hEvent) was never executed- but it was in UpdateResults!

end;

end.


xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

unit PrimeThread;

interface

uses
  Classes, Windows;

type
  TPrimeThrd = class(TThread)
  private
    FTestNumber: integer;
    FResultString: string;
  protected
    function IsPrime: boolean;
    procedure Execute; override;
    procedure UpdateResults;
  public
    property TestNumber: integer write FTestNumber;
  end;

var
  hEvent : THandle;
implementation

uses SysUtils, Dialogs, PrimeForm;

function TPrimeThrd.IsPrime: boolean;
var iter: integer;
begin
  result := true;
  if FTestNumber < 0 then
  begin
    result := false;
    exit;
  end;
  if FTestNumber <= 2 then
    exit;
  for iter := 2 to FTestNumber - 1 do
  begin
    if (FTestNumber mod iter) = 0 then
    begin
      result := false;
      {exit;}
    end;
  end;
end;

procedure TPrimeThrd.Execute;
begin
  if IsPrime then
    FResultString := IntToStr(FTestNumber) + ' is prime.'
  else
    FResultString := IntToStr(FTestNumber) + ' is not prime.';
  Synchronize(UpdateResults);

end;

procedure TPrimeThrd.UpdateResults;
begin
  PrimeFrm.ResultsMemo.Lines.Add(FResultString);
  SetEvent(hEvent);  
end;

initialization
  hEvent := CreateEvent(nil, True, false, PChar('MCS_WFSO_ExampleEvent'));

finalization
  CloseHandle(hEvent);

end.
Comment
Watch Question

This cannot work because the WaitForSingleObject blocks the main thread until the signal occurs.
Synchronize in the TPrimeThrd blocks because the main thread is blocked and cannot handle messages. Synchonization relies on messages.

All in all you are completely off-track.
If you block the main thread then there is no use for a secondary thread becasue the main thread could do it instead.
What you should do is create a Delphi event in TPrimeFrm (something like OnThreadUpdate) and the implementation is in TPrimeThrd.UpdateResults.

Author

Commented:
>>This cannot work because the WaitForSingleObject blocks the main thread until the signal occurs.
Synchronize in the TPrimeThrd blocks because the main thread is blocked and cannot handle messages. Synchonization relies on messages.

Right...!


>>All in all you are completely off-track.
If you block the main thread then there is no use for a secondary thread becasue the main thread could do it instead.

I know- I wrote this software just to learn about Threads.

What I've done to make it work is:

procedure TPrimeThrd.Execute;
begin
  if IsPrime then
    FResultString := IntToStr(FTestNumber) + ' is prime.'
  else
    FResultString := IntToStr(FTestNumber) + ' is not prime.';

  SetEvent(hEvent);                //*** put this line here
  Synchronize(UpdateResults); //instead of inside Synchronize

end;

and now it works fine, but maybe I can do it even better?

Jack




Commented:
Maybe you should take a look at http://www.midnightbeach.com/jon/pubs/MsgWaits/MsgWaits.html
There is few quite nice thread implementations that might be helpful in your case.

Not the solution you were looking for? Getting a personalized solution is easy.

Ask the Experts
The basic idea to block the main thread is simply wrong.
Your change is not really correct either. Now the main thread outputs first (or at least can output first) so for a real world example the result from the thread is not yet available the instant the event is received.

Author

Commented:
>The basic idea to block the main thread is simply wrong.
Your change is not really correct either. Now the main thread outputs first (or at least can output first) so for a real world example the result from the thread is not yet available the instant the event is received.

Right... maybe I will explain what I exactly want to achieve. My application is supposed to send messages to external electronic unit. It is 2 way communication like this one:

1. My application: send a command
2. My application WAIT for max 3 seconds to get a reply (in the code I provided I replaced electronic unit with prime number calculation)
3. After getting the reply -> send next command
3.1 Wrong or not reply -> raise an exception and do not send next command

So... are you saying that my approach is simple wrong? If so- which one is better?

Jack
I would set up a complete class for that.
The API would be a SendCommand method and a OnCommandCompleted event.
The event would have a reason parameter for the completion like success, failure, timeout and of course any data returned.
Sending the command and waiting with timeout is done in a thread which is kept locally in the class.

You may need to implement a command queue for the thread to work on. That is SendCommand places the command in a queue to work on and starts the worker thread if it is not already running.
SendCommand then returns to the main thread which can call SendCommand before the command has been completed. So the new command is placed in the queue to serialize the commands.
The thread works until the queue is empty and then terminates. The next Sendcommand will start a new worker thread.

I assume that you write the command with WriteFileEx overlapped and then wait with WaitForSingleObject 8with 3 sec timeout) for the completion of the call inside the thread.
This is what the thread does. Take a command out of the queue, do the write, wait for its completion, synchronize the OnCommandCompleted event. This is repeated until the command queue is empty.
You will have to use a mutex to guard the access to the queue so you get no synchronization problems between the main thread enqueueing commands and the thread dequeueing them.
Access more of Experts Exchange with a free account
Thanks for using Experts Exchange.

Create a free account to continue.

Limited access with a free account allows you to:

  • View three pieces of content (articles, solutions, posts, and videos)
  • Ask the experts questions (counted toward content limit)
  • Customize your dashboard and profile

*This site is protected by reCAPTCHA and the Google Privacy Policy and Terms of Service apply.

OR

Please enter a first name

Please enter a last name

8+ characters (letters, numbers, and a symbol)

By clicking, you agree to the Terms of Use and Privacy Policy.