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

x
?
Solved

Comport timeout with overlapped operation during high CPU usage

Posted on 2006-04-27
6
Medium Priority
?
712 Views
Last Modified: 2013-12-03
First of all, this question is not the "ordinary" how to use WaitCommEvent and such. Instead I need help to understand the timing and optimal/correct usage and understanding of the threads and how to "tell Windows to boost the comport driver" to report a possible event even when the CPU load is very high??

I have a problem with an application that is beeing used to load an external device with a firmware through a comport.

I have a separate thread divided from the user interface thread that writes X amount of bytes to the external machine and then waits for a single byte to be returned from the machine, as a kind of confirm byte.

The problem is that I sometimes get a timeout from the "WaitCommEvent" and/or the aftercomming WaitForMultipleObjects (if WaitCommEvent returns ERROR_IO_PENDING, the WaitFor... is called with the event for the overlaped structure).

Even dough I know that data has been received I get this timeout during high CPU load.

So if I stress the CPU by moving the application window rapidly over the screen and never stop the mouse drag for the timeout period I stated in the "WaitFor" command, I will receive a timeout even dough I know that there has been a byte sent from the external machine to the PC.
In other words, my problem only shows up when the CPU is under stress.

At the moment it seems like the comport driver does not flag the RX_CHAR if the CPU usage is very high on the system. The RX_CHAR flag is "lit up" directly when the CPU load is lowered, however, the "WaitFor" functions is still counting the time during the high CPU load, so it causes my code to report for timeout in comport communication during high load of the system if the high CPU load lasts longer then the timeout value supplied to the "WaitCommEvent/WaitFor..." function.

Either my comprt handling design is bad or I have to do something to make the comport driver to run in "TimeCritical" priority and make sure it reports the RX_CHAR so that my overlapped.hEvent is triggered before the timeout period.

I have tried some things with the priority of the thread that calls WaitCommEvent/WaitForMultipleObjects, setting it to TimeCritical without any possitive change in the timeout results.

Below is my code (Delphi) that is used to wait for the confirmation byte from my external equipment connected to the PC.

I can be doing something wrong in this code that only causes trouble during high CPU load?

---

function TELSRS232.ReceiveByte(Timeout : DWord; out ReadByte : byte) : boolean;
var OLBlock: TOverlapped;
    Mask: DWORD;
    Success,IOTimeout : Boolean;
    BytesTrans,InpCnt,LastErr : Cardinal;
    Signaled, EventHandleCount: Integer;
    EventHandles: array[0..2] of THandle;
    AIOSignaled,ATerminateSignaled,AAbortSignaled : boolean;
begin
 result := false;
 ReadByte := $FF;
 if not Terminated then begin
  FillChar(OLBlock, SizeOf(TOverlapped), 0);
  OLBlock.hEvent := FIOEvent;
  EventHandles[0] := FIOEvent;
  EventHandles[1] := FTerminateEvent; // Terminate thread event...
  EventHandles[2] := FFlashAbortEvent; // Abort Flash event...
  ResetEvent(FIOEvent);
  Signaled := 0;
  SetCommMask(FComPort.Handle,EV_RXCHAR);
  SetThreadPriorityBoost(self.Handle,true); // <- This is only a test, I'm not sure it helps anything at all
  Success := WaitCommEvent(FComPort.Handle,ComMask,@OLBlock);
  LastErr := GetLastError;
  if (Success) or (LastErr = ERROR_IO_PENDING) then begin  
   // If WaitCommEvent returned Success = TRUE, then the overlapped.hEvent should still remain high..
   // making the next call to the WaitForMultipleObjects catch the same event again and return direct?
   Signaled := WaitForMultipleObjects(1, @EventHandles, False, Timeout); // Wait for any of 3 events
   AIOSignaled := (WaitForSingleObject(FIOEvent,0) = WAIT_OBJECT_0); // Check event
   ATerminateSignaled := (WaitForSingleObject(FTerminateEvent,0) = WAIT_OBJECT_0); // Check event
   AAbortSignaled := (WaitForSingleObject(FFlashAbortEvent,0) = WAIT_OBJECT_0); // Check event
   if (Signaled = WAIT_OBJECT_0) then begin
    Success := (ComMask = EV_RXCHAR);
    SetCommMask(FComPort.Handle,0); // reset the event
    IOTimeout := false;
   end else begin
    if (Signaled = WAIT_TIMEOUT) then begin
     SetCommMask(FComPort.Handle, 0); // reset the event
     IOTimeout := true;
    end else IOTimeout := false;
   end;
  end else begin
   // Here we come if WaitCommEvent failed with anything else then ERROR_IO_PENDING
   WriteLogText(SysErrorMessage(LastErr));
   AIOSignaled := (WaitForSingleObject(FIOEvent,0) = WAIT_OBJECT_0);
   ATerminateSignaled := (WaitForSingleObject(FTerminateEvent,0) = WAIT_OBJECT_0);
   AAbortSignaled := (WaitForSingleObject(FFlashAbortEvent,0) = WAIT_OBJECT_0);
   IOTimeout := true;
  end;
  SetThreadPriorityBoost(Handle,false); // <- This is only a test, I'm not sure it helps anything at all
  if not IOTimeout then begin
   if Success then begin // There is a byte to read
    InpCnt := FComPort.InputCount;
    if (InpCnt > 0) then begin
     result := (FComPort.Read(ReadByte,1) >= 1);
     if result then inc(FReceivedCnt,1);
    end;
   end;
   if ATerminateSignaled then WriteLogText('TERMINATE EVENT TRIGGERED');
   if AAbortSignaled then WriteLogText('ABORT EVENT TRIGGERED');
  end else begin
   inc(FTimeoutCnt,1);
   WriteLogText('I/O TIMEOUT');
   if AIOSignaled then WriteLogText('I/O (RX or FRAME ERROR) TRIGGERED');
   if ATerminateSignaled then WriteLogText('TERMINATE EVENT TRIGGERED');
   if AAbortSignaled then WriteLogText('ABORT EVENT TRIGGERED');
  end;
 end;
end;

---
0
Comment
Question by:Probie
5 Comments
 
LVL 48

Expert Comment

by:AlexFM
ID: 16559621
  Signaled := WaitForMultipleObjects(1, @EventHandles, False, Timeout); // Wait for any of 3 events
   AIOSignaled := (WaitForSingleObject(FIOEvent,0) = WAIT_OBJECT_0); // Check event
   ATerminateSignaled := (WaitForSingleObject(FTerminateEvent,0) = WAIT_OBJECT_0); // Check event
   AAbortSignaled := (WaitForSingleObject(FFlashAbortEvent,0) = WAIT_OBJECT_0); // Check event

I am not sure whether this can help, but why do you need additional calls to WaitForSingleObject? WaitForMultipleObjects returns  WAIT_OBJECT_0 is first event in array is signaled,  WAIT_OBJECT_0 + 1 if second event is signaled etc. You need to make array of events and wait all of them in one WaitForMultipleObjects call.
0
 
LVL 1

Author Comment

by:Probie
ID: 16561110
True AlexFM, it's stupid.. and I see now that I feed WaitForMultipleObjects(1, @EventHandles, False, Timeout) with "1", telling it to only check the first of my three events. However none of these "bugs" affect the reason of this question.. and the problem is still the same even after changing this.
0
 
LVL 8

Expert Comment

by:mxjijo
ID: 16565929

I'm not sure whether there is a simple solution for this.
I can imagine this can happen if the application threads have higher priorities than threads in the driver.
Therefore I think increasing your application thread priority can make things even worse !!

May be its a good idea to reduce your thread's priority while waiting.
That way you can make sure that the driver threads has a bit upper hand.
Also it will work normally on normal CPU loads.

The other option is to play smart, change wait timeout according to the current cpu usage.

0
 
LVL 1

Author Comment

by:Probie
ID: 16567563
I found "a" solution to my own problem..

Instead of accepting a timeout when I get one and do a break, I catch the timeout and check the input buffer with "ClearCommError"
If "Comstat.cbInQue > 0", then I know that there has been a byte recieved and can catch it before the Overlapped.hEvent becomes set.

---

  SetCommMask(FComPort.Handle,EV_RXCHAR);
  Success := WaitCommEvent(FComPort.Handle,ComMask,@OLBlock);
  if not Success then begin
   LastErr := GetLastError;
   if (LastErr = ERROR_IO_PENDING) then begin
    Signaled := WaitForMultipleObjects(3,@EventHandles,False,Timeout);
    case Signaled of
     WAIT_OBJECT_0+0: begin
      AIOSignaled := true;
      Success := (ComMask = EV_RXCHAR);
     end;
     WAIT_OBJECT_0+1: ATerminateSignaled := true;
     WAIT_OBJECT_0+2: AAbortSignaled := true;
    else
     ClearCommError(FComPort.Handle,ComErrors,@ComStat);
     if (ComStat.cbInQue > 0) then begin
      Success := true;
     end else begin
      IOTimeout := true;
     end;
    end;
   end else begin
    WriteLogText(SysErrorMessage(LastErr));
   end;
  end else begin
   Success := (ComMask = EV_RXCHAR);
  end;
  SetCommMask(FComPort.Handle,0);

---
0
 

Accepted Solution

by:
GranMod earned 0 total points
ID: 16767054
PAQed with points refunded (500)

GranMod
Community Support Moderator
0

Featured Post

Fill in the form and get your FREE NFR key NOW!

Veeam is happy to provide a FREE NFR server license to certified engineers, trainers, and bloggers.  It allows for the non‑production use of Veeam Agent for Microsoft Windows. This license is valid for five workstations and two servers.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

As more and more people are shifting to the latest .Net frameworks, the windows presentation framework is gaining importance by the day. Many people are now turning to WPF controls to provide a rich user experience. I have been using WPF controls fo…
Whether you've completed a degree in computer sciences or you're a self-taught programmer, writing your first lines of code in the real world is always a challenge. Here are some of the most common pitfalls for new programmers.
This is Part 3 in a 3-part series on Experts Exchange to discuss error handling in VBA code written for Excel. Part 1 of this series discussed basic error handling code using VBA. http://www.experts-exchange.com/videos/1478/Excel-Error-Handlin…
Look below the covers at a subform control , and the form that is inside it. Explore properties and see how easy it is to aggregate, get statistics, and synchronize results for your data. A Microsoft Access subform is used to show relevant calcul…

581 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question