Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 1191
  • Last Modified:

Problem with WaitForSingleObject

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.
0
Actia
Asked:
Actia
  • 3
  • 2
2 Solutions
 
robert_marquardtCommented:
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.
0
 
ActiaAuthor 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




0
 
jpedefCommented:
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.
0
What does it mean to be "Always On"?

Is your cloud always on? With an Always On cloud you won't have to worry about downtime for maintenance or software application code updates, ensuring that your bottom line isn't affected.

 
robert_marquardtCommented:
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.
0
 
ActiaAuthor 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
0
 
robert_marquardtCommented:
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.
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 3
  • 2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now