Board index » delphi » Thread problem in serial communication component

Thread problem in serial communication component

Hi,

I hope someone can help me out, since I've spent a lot of time already
solving the problem.

I am using a TComPort component that uses a TComThread instance for
monitoring the comport status and acting according to this status. This
TComThread is created in the constructor of the TComPort object.

The problem occurs when I try to stop the thread and right after that
closing the handle to the com port. Sometimes I get a blue screen with a
exception 0E message.

This is what happens:

1 Open the com port with TComPort.Open. In this procedure the thread is
created. The constructor of the thread sets the StopEvent to non-signalled.
With Resume the thread starts running. StopEvent is used to stop the thread.

2 When the TComPort object is destroyed, TComPort.Close is called in the
destructor of TComPort. This procedure frees the TComThread object by
calling free. Next, in the destructor of TComThread, Stop is called. Stop
sets the StopEvent to signalled.

3 Because StopEvent is signalled, I assume that Execute is terminated,
because StopEvent is the event on which the repeat-until loop is ended.
(However, I've got the impression that it doesn't).

4 Then, getting back to the Close procedure of TComPort, DestroyHandle is
called in which the handle to the comport is Closed. This is exactly the
point where I get the blue screen.

If I manually free the thread and then after some time close the com port,
DestroyHandle doesn't give a blue screen.

What could be wrong here? I think it has something to do with the Execute
procedure which is still running when the handle to the comport is close. Is
this  possible ?

Thanks a lot for spending time on this problem!

Roy

*********************
* Thread Class
*********************

TComThread = class(TThread)
private
  Owner: TComPort;
  Mask: DWORD;
  StopEvent: THandle;
protected
  procedure Execute; override;
  procedure DoEvents;
  procedure Stop;
public
  constructor Create(AOwner: TComPort);
  destructor Destroy; override;
end;

constructor TComThread.Create(AOwner: TComPort);
var
  AMask: Integer;
begin
  inherited Create(True);
  StopEvent := CreateEvent(nil, True, False, nil);
  Owner := AOwner;

  AMask := 0;
  if evRxChar in Owner.FEvents then
    AMask := AMask or EV_RXCHAR;
  if evRxFlag in Owner.FEvents then
    AMask := AMask or EV_RXFLAG;
  SetCommMask(Owner.ComHandle, AMask);
  Resume;
end;

procedure TComThread.Execute;
var
  EventHandles: Array[0..1] of THandle;
  Overlapped: TOverlapped;
  dwSignaled, BytesTrans: DWORD;
begin
  FillChar(Overlapped, SizeOf(Overlapped), 0);
  Overlapped.hEvent := CreateEvent(nil, True, True, nil);
  EventHandles[0] := StopEvent;
  EventHandles[1] := Overlapped.hEvent;
  repeat
    WaitCommEvent(Owner.ComHandle, Mask, @Overlapped);
    dwSignaled := WaitForMultipleObjects(2, @EventHandles, False, INFINITE);
    case dwSignaled of
      WAIT_OBJECT_0:
        Break;
      WAIT_OBJECT_0 + 1:
        if GetOverlappedResult(Owner.ComHandle, Overlapped,
          BytesTrans, False) then Synchronize(DoEvents);
      else
        Break;
    end;
  until False;
  Owner.PurgeIn;
  Owner.PurgeOut;
  CloseHandle(Overlapped.hEvent);
  CloseHandle(StopEvent);
end;

procedure TComThread.Stop;
begin
  SetEvent(StopEvent);
end;

destructor TComThread.Destroy;
begin
  Stop;
  inherited Destroy;
end;

*********************
* TComPort Class
*********************

constructor TComPort.Create(AOwner: TComponent);
begin
  inherited Create(AOwner);

  .... Default property settings

  ComHandle := INVALID_HANDLE_VALUE;
end;

destructor TComPort.Destroy;
begin
  Close;
  inherited Destroy;
end;

procedure TComPort.CreateHandle;
begin
  ComHandle := CreateFile(
    PChar(ComString),
    GENERIC_READ or GENERIC_WRITE,
    0,
    nil,
    OPEN_EXISTING,
    FILE_FLAG_OVERLAPPED,
    0);

  if not ValidHandle then
    raise EComHandle.Create('TComPort.CreateHandle: Unable to open com
port');
end;

procedure TComPort.DestroyHandle;
begin
  if ValidHandle then
    CloseHandle(ComHandle);
end;

function TComPort.ValidHandle: Boolean;
begin
  if ComHandle = INVALID_HANDLE_VALUE then
    Result := False
  else
    Result := True;
end;

procedure TComPort.Open;
begin
  Close;
  CreateHandle;
  SetupState;
  EventThread := TComThread.Create(Self);
  FConnected := True;
  if Assigned(FOnOpen) then
    FOnOpen(Self);
end;

procedure TComPort.Close;
begin
  if FConnected then
  begin
    EventThread.Free;
    EventThread := nil
    DestroyHandle;
    FConnected := False;
    if Assigned(FOnClose) then
      FOnClose(Self);
  end;
end;

 

Re:Thread problem in serial communication component


JRS:  In article <bfGO4.13777$FF6.319340@zonnet-reader-1> of Sat, 29 Apr
2000 20:58:21 seen in news:comp.lang.pascal.delphi.misc, Roy Rutten

Quote
<royrut...@zonnet.nl.NOSPAM> wrote:

>function TComPort.ValidHandle: Boolean;
>begin
>  if ComHandle = INVALID_HANDLE_VALUE then
>    Result := False
>  else
>    Result := True;
>end;

function TComPort.ValidHandle: Boolean;
begin
  Result := ComHandle <> INVALID_HANDLE_VALUE ;
end;

This will not, however, fix your problem.

--
? John Stockton, Surrey, UK.  j...@merlyn.demon.co.uk   Turnpike v4.00   MIME. ?
 <URL: http://www.merlyn.demon.co.uk/> TP/BP/Delphi/&c., FAQqy topics & links;
 <URL: ftp://garbo.uwasa.fi/pc/link/tsfaqp.zip> Timo Salmi's Turbo Pascal FAQ;
 <URL: http://www.merlyn.demon.co.uk/clpb-faq.txt> Pedt Scragg: c.l.p.b. mFAQ.

Re:Thread problem in serial communication component


In article <bfGO4.13777$FF6.319340@zonnet-reader-1>, "Roy Rutten"

Quote
<royrut...@zonnet.nl.NOSPAM> writes:
>I hope someone can help me out, since I've spent a lot of time already
>solving the problem.

>I am using a TComPort component that uses a TComThread instance for
>monitoring the comport status and acting according to this status. This
>TComThread is created in the constructor of the TComPort object.

>The problem occurs when I try to stop the thread and right after that
>closing the handle to the com port. Sometimes I get a blue screen with a
>exception 0E message.

I'm not sure I am as expert as others but I had a somewhat similar bit of code
(also called TCommPort <g>) and I held off freeing the commport until the
thread had finished. I did this with two actions (might not be most elegant -
any comments please) :-

1 I specify "FreeOnTerminate := true;" as the first code
inTStatusThread.Execute.

2 I set TStatusThread.OnTerminate to TCommPort.OnThreadFinished, which just
sets :-

  FThreadFinished := true; // flag to indicate thread has finished

3 in TCommPort.Destroy I code :-

begin
  FStatusThread.Terminate;
  repeat
    Application.ProcessMessages;
  until FThreadFinished;  // wait until thread is finished
  inherited Destroy;
end;

I've never had any problems at this end of the action - Now while we're on comm
port status monitoring would anyone like to comment as an answer to my post of
"Is ResetEvent Needed ?" <g>

Alan Lloyd
alangll...@aol.com

Re:Thread problem in serial communication component


Quote
Roy Rutten wrote:

> Hi,

> I hope someone can help me out, since I've spent a lot of time already
> solving the problem.

A couple of thoughts.

Quote
> 3 Because StopEvent is signalled, I assume that Execute is terminated,
> because StopEvent is the event on which the repeat-until loop is ended.
> (However, I've got the impression that it doesn't).

This is an invalid assumption. You can assume that the thread is working
its way towards termination. You can't be sure that it has (to all
extents and purposes) terminated.

To get around this, I would normally advocate using WaitFor. However,
you also use synchronize elsewhere in your app. I would suggest not
using synchronize, and instead using a critical section and message
passing to achieve the same effect.

Quote
>     WaitCommEvent(Owner.ComHandle, Mask, @Overlapped);
>     dwSignaled := WaitForMultipleObjects(2, @EventHandles, False, INFINITE);

Looks to be OK ... since this will unblock whenever the stop event is
signalled, however, in general, check that you always release critical
sections and signal events before closing handles (you probably do
anyway).

If you send me enough source to replicate the problem, I can probably
solve it without too much difficulty.

MH.

--
Martin Harvey. mar...@pergolesi.demon.co.uk
     http://www.pergolesi.demon.co.uk

Re:Thread problem in serial communication component


As a followup to my previous post, I hasten to add that

http://www.pergolesi.demon.co.uk/prog/threads/ToC.html

has some info on how you can get rid of the Synchronize call in your
thread, thus allowing you to use WaitFor.

MH.

--
Martin Harvey. mar...@pergolesi.demon.co.uk
     http://www.pergolesi.demon.co.uk

Re:Thread problem in serial communication component


Thanks for the help, I appreciate it very much!

I'll start trying the solutions right away.

Thanks again,

Roy

Other Threads