Implementing delays while thread is running

  • Follow


Some time ago I came up with a simple way to implement delays by using a 
loop as follows:

 type
  TDelayObject = class(TObject)
    Timer1: TTimer;
  private
    procedure Timer1Timer(Sender: TObject);
  public
    Timeout: Integer;
  end;

 var
  DelayObject: TDelayObject;

 procedure     Delay( const Msec: Integer );
 var           fTimerEvent: TNotifyEvent;
 {$J+}
 const         DelayInProgress: Boolean = False;
 {$J-}
 begin
  If DelayInProgress = True then
    exit;
  DelayInProgress := True;
  DelayObject := TDelayObject.Create;
  DelayObject.Timeout := mSec;
  DelayObject.Timer1 := TTimer.Create(nil);
  DelayObject.Timer1.Interval := 100;
  fTimerEvent := DelayObject.Timer1Timer;
  DelayObject.Timer1.OnTimer := fTimerEvent;
  while DelayObject.Timeout > 0 do
    Application.ProcessMessages;
  DelayObject.Timer1.Free;
  DelayObject.Timer1 := nil;
  DelayObject.Free;
  DelayObject := nil;
  DelayInProgress := False;
 end;

 procedure TDelayObject.Timer1Timer(Sender: TObject);
 begin
  if fmDelay <> nil then
    fmDelay.Edit1.Text := FloatToStr( Timeout/1000 );
  If DelayObject.TimeOut > 0 then
    DelayObject.TimeOut := DelayObject.TimeOut - 100;
 end;

This has worked, or at least seemed to work, in previous applications where 
I was using a Serial Port component, which had threads and required delays 
in the main application to allow for certain operations to occur and 
results returned on the serial port, which was actually a virtual port 
using the usbser.sys driver.

Now, however, I am trying to implement a direct "Generic" USB connection 
using the Microchip mchpusb.sys driver which allows direct writing and 
reading on the USB pipes for a custom USB device (which uses the same 
hardware but different PIC code). It has worked reasonably well but I had 
problems where I needed to use delays. The Delay function above worked well 
most of the time but at other times it seemed to "short-circuit", with no 
appreciable delay, and I finally discovered that this was only while a 
thread was running. The thread loops on a call to ReadUSB and transfers any 
characters to a buffer for later processing. It is implemented as follows:

 type
  TUSBThread = class(TThread)
  private
    procedure UpdateMemo;
    procedure SetNil(Sender: TObject);
  protected
    constructor Create(CreateSuspended: Boolean);
    procedure Execute; override;
  end;

 var  thUSBread: TUSBThread;
     fThreadTerminate: TNotifyEvent;

 constructor  TUSBThread.Create(CreateSuspended: Boolean);
 begin
     inherited Create(CreateSuspended);
     FreeOnTerminate := True;
 end;

 procedure TUSBthread.UpdateMemo;
 begin
  if fmOrtUSB.Memo1 <> nil then
    fmOrtUSB.Memo1.Lines.Add(ThreadStr);
 end;

 procedure TUSBThread.SetNil(Sender: TObject);
 begin
  thUSBread := nil;
  if fmOrtUSB.Memo1 <> nil then
    fmOrtUSB.Memo1.Lines.Add('Thread terminated and nil');
 end;

 procedure TUSBthread.Execute;
 var       i: Integer;
          Data: Byte;
 begin
  ThreadStr := 'Thread Running';
  fmOrtUSB.USBThreadRunning := True;
  Synchronize(UpdateMemo);
    while fmOrtUSB.Tmo > 0 do begin  //1
      If Terminated then
        exit;
      Inc(LoopCount);
      // This function has a timeout of 100 mSec
      if(_MPUSBRead(myInPipe,receive_buf,64,RecvLength,100)<>0) then begin 
//2
        // USB Read Successful
        for i := 0 to RecvLength-3 do begin //3
          Inc(TotalData);
          Data := receive_buf[i+2];
          fmOrtUSB.WriteBuffer(Data,fmOrtUSB.USBCommBuffer);
          Inc(DataCount); end;//-3
        fmOrtUSB.Tmo := TMO_VAL;
        end //-2
      else  // USB Read timed out
        Dec(fmOrtUSB.Tmo);
      end; //-1
  ThreadStr := 'Thread Timeout';
  Synchronize(UpdateMemo);
  fmOrtUSB.USBThreadRunning := False;
 end;

When I send a character to the USB device I typically await a response so I 
make sure the thread is running. If there is nothing received in 25 loops 
at 100 mSec, the thread terminates and is made free and nil.

 procedure TfmOrtUSB.USBSendChar( ch: char );
 var  SentDataLength:DWORD ;
     SendLength: Integer;
 begin
  Tmo := TMO_VAL;    // I use 25 which is 2.5 seconds with 100 mSec USBRead 
Timeout
  if thUSBread = nil then begin
    thUSBread := TUSBThread.Create(True);
    fThreadTerminate := thUSBread.SetNil;
    thUSBread.OnTerminate := fThreadTerminate;
    thUSBread.Priority := MyPriority; end;    // I'm using tpTimeCritical = 
highest available
  send_buf[0]:= $80;
  send_buf[1]:= 3;
  send_buf[2]:=BYTE(ch);
  SendLength := 3;
  thUSBread.Resume;
  if(_MPUSBWrite(myOutPipe,send_buf,SendLength,SentDataLength,100)<>0) then 
begin
    Tmo := TMO_VAL;
    thUSBread.Resume; end;
 end;

Admittedly this may not be the best way to do this, but it seems to work 
except for the problem of the delays, which are necessary. And the timeout 
and termination of the thread is a way that I can sense if the connection 
has failed (such as removal of the USB cable) and alert the user of a 
problem.

I only just now discovered the problem with the delay function, and I have 
some ideas to try. But perhaps there is a better way to perform delays. And 
I still do not understand why the delay function does not seem to work. It 
is as if the DelayInProgress Boolean gets set and not reset. And I tried 
commenting out those parts and I got an AV where the DelayObject was nil on 
the Timer tick.

Time for some sleep and a fresh start tomorrow.

Thanks,

Paul



0
Reply Paul 1/4/2010 11:05:33 AM

"Paul E. Schoen" <paul@peschoen.com> wrote in message
news:E%j0n.1470$ZB2.1069@newsfe13.iad...

> Some time ago I came up with a simple way to implement delays by using a
> loop as follows:
>
>  type
>   TDelayObject = class(TObject)
>     Timer1: TTimer;
>   private
>     procedure Timer1Timer(Sender: TObject);
>   public
>     Timeout: Integer;
>   end;
>
>  var
>   DelayObject: TDelayObject;
>
>  procedure     Delay( const Msec: Integer );
>  var           fTimerEvent: TNotifyEvent;
>  {$J+}
>  const         DelayInProgress: Boolean = False;
>  {$J-}
>  begin
>   If DelayInProgress = True then
>     exit;

So you want to be _really_ sure that DelayInProgress is really True.
But what if, through some unforeseeable fluke, DelayInProgress is
True but (DelayInProgress=True) isn't? Clearly, if you're going to
check it, you want ((DelayInProgress=True)=True). Or perhaps
((DelayInprogress=True)<>False). Or what if it's True but also False?
((DelayInProgress=True)=True) and ((DelayInProgress=True)<>False).
Or should that be
((DelayInProgress=True)=True) OR ((DelayInProgress=True)<>False)?
But you should have checked the first time around, too:
((DelayInProgress=True)=True) and (DelayInProgress<>False)=True).
And ((DelayInProgress=True)<>False) and ((DelayInProgress<>False)<>False).

And (((DelayInProgress=True)=True)=True), too.

Or you can simply believe the Boolean expression (yes, a variable is
an expression) you started with, and write:

if (DelayInProgress) then...

Really.


>   DelayInProgress := True;
>   DelayObject := TDelayObject.Create;
>   DelayObject.Timeout := mSec;
>   DelayObject.Timer1 := TTimer.Create(nil);

You're missing a few try-finally's here. (Three, to be exact.)
Also, this whole timer thing should be encapsulated inside the
TDelayObject. And Timer1 can be just as private as Timer1Timer.
And DelayInProgress is just a macro for Assigned(DelayObject).


>   DelayObject.Timer1.Interval := 100;
>   fTimerEvent := DelayObject.Timer1Timer;
>   DelayObject.Timer1.OnTimer := fTimerEvent;
>   while DelayObject.Timeout > 0 do
>     Application.ProcessMessages;

That's 100% CPU useage for nothing. Try HandleMessage instead.
The help knows why, but the key point is this: if you're waiting
for something _that arrives by Windows message_, HandleMessage is
better. The only advantage of ProcessMessages is that it will return
if there are no messages at all, while in that case HandleMessage might
hang until a message happens to arrive. Some people might now argue
that that never happens, but it does and anyway, it's wrong to depend
on it. Usually though, the change you're waiting for will be heralded
by a message and it does no harm to wait on that.


>   DelayObject.Timer1.Free;
>   DelayObject.Timer1 := nil;
>   DelayObject.Free;
>   DelayObject := nil;
>   DelayInProgress := False;
>  end;
>
>  procedure TDelayObject.Timer1Timer(Sender: TObject);
>  begin
>   if fmDelay <> nil then
>     fmDelay.Edit1.Text := FloatToStr( Timeout/1000 );
>   If DelayObject.TimeOut > 0 then
>     DelayObject.TimeOut := DelayObject.TimeOut - 100;

You're depending on the timer ticks to actually arrive every 100ms,
on the dot. It's safer to compute a timestamp and see if you've passed
it. Change that 100 to the Interval, set it to 1 and think what happens.


>  end;
>
> This has worked, or at least seemed to work, in previous applications
> where I was using a Serial Port component, which had threads and
> required delays in the main application to allow for certain operations
> to occur and results returned on the serial port, which was actually a
> virtual port using the usbser.sys driver.

I won't say that it couldn't possibly work, but is there any guarantee
about the timing? Is there some delay that is 'always enough'? And is the
real work never done any sooner than that? Windows has synchronisation
objects that might work better, in the sense that you can (reliably) sense
whether the event has already occurred, or even wait synchronously until it
has (bot that you want that here). The component would need to lay the
groundwork for such things, and from the sound of it, it probably doesn't.


> [...] It is as if the DelayInProgress Boolean gets set and not reset.

Looking at the code, that seems possible only if an exception occurs
somewhere. Put in those try-finally's.

Threads are hard to get right. Insanely hard. You need to be pessimistic
to the point of complete paranoia about what could happen in what order,
and guard against it all. Locks and exclusions and events (the Windows
synchronisation object kind, not Delphi's) on everything, and on the
other hand you have to leave room for things to still make progress.

Try to make your main UI stateless, not dependent on wait loops anywhere,
allowing at any moment to do only those things that are consistent with
the state of the entire application including the bits that run in their
own threads. This is hard, especially if you didn't build it that way
from the beginning. Putting everything behind actions can be a big help.
Make sure the OnUpdate handlers can't hang even momentarily. Never use
any delays, anywhere.

In the worker threads, go to the other extreme. Make sure the externally
visible state information is accurate, and then block them relentlessly
until something happens again.

Groetjes,
Maarten Wiltink


0
Reply Maarten 1/4/2010 12:09:20 PM


Paul E. Schoen pisze:
> Some time ago I came up with a simple way to implement delays by using a 
> loop as follows:
> 
[removed very bad code]


proper delay loop for main, GUI thread, assuming messages should still 
be processed without notciceable delay:

var
   waitTill: TDateTime;


waitTill := Now() + encodeTime(hours, minutes, seconds, miliseconds);

While Now() < waitTill do
begin
   Sleep(10); //give CPU time to other threads
   Application.ProcessMessages();
end;


proper delay loop for other threads:

   Sleep(miliseconds);


Note: TTimer will not work properly in thread! TTimer uses windows 
messages, and messahe loop should be accessed only from main, GUI thread.

-- 
Arivald

0
Reply Arivald 1/4/2010 7:50:36 PM

"Arivald" <NOSPAMarivald@interia.pl> wrote in message
news:hhtgva$3os$1@news.dialog.net.pl...
[...]
> Note: TTimer will not work properly in thread! TTimer uses windows
> messages, and messahe loop should be accessed only from main, GUI
> thread.

That's not strictly true (although it's a useful simplification for
beginners). There isn't just one message loop. Every thread can have
its own message loop, but only in the GUI thread do you get it for
free, and worker threads don't usually need one.

Groetjes,
Maarten Wiltink


0
Reply Maarten 1/5/2010 9:41:15 AM

"Maarten Wiltink" <maarten@kittensandcats.net> wrote in message 
news:4b41da75$0$22945$e4fe514c@news.xs4all.nl...
> "Paul E. Schoen" <paul@peschoen.com> wrote in message
> news:E%j0n.1470$ZB2.1069@newsfe13.iad...
>
>> Some time ago I came up with a simple way to implement delays by using a
>> loop as follows:
>>
>>  type
>>   TDelayObject = class(TObject)
>>     Timer1: TTimer;
>>   private
>>     procedure Timer1Timer(Sender: TObject);
>>   public
>>     Timeout: Integer;
>>   end;
>>
>>  var
>>   DelayObject: TDelayObject;
>>
>>  procedure     Delay( const Msec: Integer );
>>  var           fTimerEvent: TNotifyEvent;
>>  {$J+}
>>  const         DelayInProgress: Boolean = False;
>>  {$J-}
>>  begin
>>   If DelayInProgress = True then
>>     exit;
>
> So you want to be _really_ sure that DelayInProgress is really True.
> But what if, through some unforeseeable fluke, DelayInProgress is
> True but (DelayInProgress=True) isn't? Clearly, if you're going to
> check it, you want ((DelayInProgress=True)=True). Or perhaps
> ((DelayInprogress=True)<>False). Or what if it's True but also False?
> ((DelayInProgress=True)=True) and ((DelayInProgress=True)<>False).
> Or should that be
> ((DelayInProgress=True)=True) OR ((DelayInProgress=True)<>False)?
> But you should have checked the first time around, too:
> ((DelayInProgress=True)=True) and (DelayInProgress<>False)=True).
> And ((DelayInProgress=True)<>False) and 
> ((DelayInProgress<>False)<>False).
>
> And (((DelayInProgress=True)=True)=True), too.
>
> Or you can simply believe the Boolean expression (yes, a variable is
> an expression) you started with, and write:
>
> if (DelayInProgress) then...
>
> Really.

Yes, that's what I had originally. I changed it when I started debugging, 
and I was trying to put a breakpoint on the exit; but that did not seem to 
work because of compiler optimization, since it does nothing but jump to 
the final end; of the procedure. I realize that a Boolean variable or a 
Boolean expression of any complexity evaluates to a Boolean type. In "C" 
any non-zero value is True and a value of zero is False, but in Delphi the 
types must match absolutely or be specifically typecast. When I was fooling 
around with JavaScript I found that it has even more complexity in 
evaluation of expressions, but probably because its interpretor makes 
assumptions of variable types and they may be automatically typecast with 
seemingly strange results. I posted some examples of what seem to be 
contradictory or inexplicable results of certain comparisons that would 
more predictable in Delphi.

I do assume you are using these examples with tongue in cheek, although the 
resulting code MAY be different depending on the level of compiler 
optimization.


>>   DelayInProgress := True;
>>   DelayObject := TDelayObject.Create;
>>   DelayObject.Timeout := mSec;
>>   DelayObject.Timer1 := TTimer.Create(nil);
>
> You're missing a few try-finally's here. (Three, to be exact.)
> Also, this whole timer thing should be encapsulated inside the
> TDelayObject. And Timer1 can be just as private as Timer1Timer.
> And DelayInProgress is just a macro for Assigned(DelayObject).

I suppose that does make sense. The try..finally construct is also valuable 
advice. This was some old code that I got working a while ago but only 
recently I found that it has problems.


>>   DelayObject.Timer1.Interval := 100;
>>   fTimerEvent := DelayObject.Timer1Timer;
>>   DelayObject.Timer1.OnTimer := fTimerEvent;
>>   while DelayObject.Timeout > 0 do
>>     Application.ProcessMessages;
>
> That's 100% CPU useage for nothing. Try HandleMessage instead.
> The help knows why, but the key point is this: if you're waiting
> for something _that arrives by Windows message_, HandleMessage is
> better. The only advantage of ProcessMessages is that it will return
> if there are no messages at all, while in that case HandleMessage might
> hang until a message happens to arrive. Some people might now argue
> that that never happens, but it does and anyway, it's wrong to depend
> on it. Usually though, the change you're waiting for will be heralded
> by a message and it does no harm to wait on that.

In most cases there is some handshaking in the IO, so that when a requested 
action has been completed there is a message generated. But the USB device 
in some cases simply receives a character and performs a function which may 
take some finite amount of time but does not return an indicator of 
completion. Perhaps it would be best to implement that, and it would 
probably not be too difficult at this point in the project, where I am 
still developing prototypes. However, there are ten units which use the 
older virtual serial port implementation, and the problems I am now 
experiencing are with the generic USB functions.

The basic USB_Write function has a timeout which uses 
WaitForSingleObject( gOverlapped.hEvent, dwMilliseconds)) which I 
understand provides a very efficient timeout. So my loop in the 
Thread.execute procedure does not eat up too much CPU usage even though the 
thread priority is tpTimeCritical. The loop on ProcessMessages in the main 
application thread also does not seem to use CPU time excessively as long 
as the USB thread is not running. But there is a problem with 
responsiveness to mouse clicks under certain conditions.


>>   DelayObject.Timer1.Free;
>>   DelayObject.Timer1 := nil;
>>   DelayObject.Free;
>>   DelayObject := nil;
>>   DelayInProgress := False;
>>  end;
>>
>>  procedure TDelayObject.Timer1Timer(Sender: TObject);
>>  begin
>>   if fmDelay <> nil then
>>     fmDelay.Edit1.Text := FloatToStr( Timeout/1000 );
>>   If DelayObject.TimeOut > 0 then
>>     DelayObject.TimeOut := DelayObject.TimeOut - 100;
>
> You're depending on the timer ticks to actually arrive every 100ms,
> on the dot. It's safer to compute a timestamp and see if you've passed
> it. Change that 100 to the Interval, set it to 1 and think what happens.

The timestamp approach may be more accurate, but these delays are only 
approximations and accuracy is not critical. I may be thinking of the 
problem with using a timestamp in MSDOS, where the system timer wraps at 
midnight. The Delphi Date/Time probably does not have that problem.


>>  end;
>>
>> This has worked, or at least seemed to work, in previous applications
>> where I was using a Serial Port component, which had threads and
>> required delays in the main application to allow for certain operations
>> to occur and results returned on the serial port, which was actually a
>> virtual port using the usbser.sys driver.
>
> I won't say that it couldn't possibly work, but is there any guarantee
> about the timing? Is there some delay that is 'always enough'? And is the
> real work never done any sooner than that? Windows has synchronisation
> objects that might work better, in the sense that you can (reliably) 
> sense
> whether the event has already occurred, or even wait synchronously until 
> it
> has (bot that you want that here). The component would need to lay the
> groundwork for such things, and from the sound of it, it probably 
> doesn't.

I think it will be better if I implement the handshaking in the PIC code of 
the device. For this specific application, I am using it as a timer where I 
send control characters to set up its configuration. I use the change of 
state of I/O pins to start and stop a timer which has a resolution of about 
70 uSec and I am using a system timer at 200 mSec to send a control 
character to return the four bytes which correspond to the elapsed time and 
display it on a readout. I also send a control character to reset the 
device. This is complicated somewhat because I'm using one PIC to provide 
the USB connection to the computer and the GUI, and this PIC communicates 
with a second PIC over a digital isolator using a USART. There is no 
handshaking so perhaps it would be best to have the device return an ACK, 
or perhaps simply echo the control character back to the application.


>> [...] It is as if the DelayInProgress Boolean gets set and not reset.
>
> Looking at the code, that seems possible only if an exception occurs
> somewhere. Put in those try-finally's.

> Threads are hard to get right. Insanely hard. You need to be pessimistic
> to the point of complete paranoia about what could happen in what order,
> and guard against it all. Locks and exclusions and events (the Windows
> synchronisation object kind, not Delphi's) on everything, and on the
> other hand you have to leave room for things to still make progress.

I found a Que book by Todd Miller and David Powell, called "Using Delphi 
3", which seems to have a good section of about 40 pages on threads. I also 
searched through the Delphi 4 help and it seems that perhaps I can use a 
WaitForSingleObject or MsgWaitForMultipleObjects. And it seems that I can 
use CreateWaitableTimer as the object, but then that requires an 
lpSecurityDescriptor which in turn has a set of items such as SIDs and 
ACLs. It seems awfully complicated just to cause a delay in the main 
thread's execution while other threads do their work.

It seems that I should be able to use one of those wait functions on an 
arbitrary object and just let the timeout occur. The object would not even 
need to have a signal state or do anything other than just exist. Once I 
get it figured out there should be a way to encapsulate the function neatly 
in a new Delay(mSec) function.

>
> Try to make your main UI stateless, not dependent on wait loops anywhere,
> allowing at any moment to do only those things that are consistent with
> the state of the entire application including the bits that run in their
> own threads. This is hard, especially if you didn't build it that way
> from the beginning. Putting everything behind actions can be a big help.
> Make sure the OnUpdate handlers can't hang even momentarily. Never use
> any delays, anywhere.

I think I understand what you are suggesting. I could make a state machine 
with the various functions I wish to perform be called in a particular 
sequence from a system timer which can implement delays as set up in a 
table or structure. For instance I could have a sequence of:

Function1    Delay1
Function2    Delay2
Function3    Delay3

And when there is an event (such as a button click) that must perform these 
functions, I could do something like:

State1:
Timer1.Enabled := False;
Timer1.Interval := Delay1;
Timer1.OnTimer := Function1;
Function1.OnTerminate := State2;   // At the end of the function, set up 
the next state
Timer1.Enabled := True;            // Wait Delay1, perform Function1, enter 
State2

Each state would have its own delay and would then execute the 
corresponding function, which then sets up the next state. I have an idea 
how to do it, but the devil's in the details.


> In the worker threads, go to the other extreme. Make sure the externally
> visible state information is accurate, and then block them relentlessly
> until something happens again.

Thanks for your help. I can see why Windows is not the ideal environment 
for real-time applications. But most of the low-level stuff is being done 
outside the GUI by the PIC, and I only need to handle the timing of the USB 
Reads and Writes.

Paul 


0
Reply Paul 1/5/2010 12:41:08 PM

"Paul E. Schoen" <paul@peschoen.com> wrote in message
news:srG0n.1677$ap2.1080@newsfe18.iad...
> "Maarten Wiltink" <maarten@kittensandcats.net> wrote in message
> news:4b41da75$0$22945$e4fe514c@news.xs4all.nl...

[...]
>> if (DelayInProgress) then...
>>
>> Really.
[...]
> I do assume you are using these examples with tongue in cheek,
> although the resulting code MAY be different depending on the
> level of compiler optimization.

Very much so (on both). But although the generated code may differ,
on the language level you never need more than the simple variable
reference. And as long as you don't cast pi to a Boolean, it will
work correctly. (If you do cast pi into a Boolean, you will get no
sympathy from me.)


[...]
> The basic USB_Write function has a timeout which uses
> WaitForSingleObject( gOverlapped.hEvent, dwMilliseconds)) which I
> understand provides a very efficient timeout. So my loop in the
> Thread.execute procedure does not eat up too much CPU usage even
> though the thread priority is tpTimeCritical. The loop on
> ProcessMessages in the main application thread also does not seem
> to use CPU time excessively as long as the USB thread is not running.
> But there is a problem with responsiveness to mouse clicks under
> certain conditions.

Sorry, but a tight loop on ProcessMessages can't help but eat all
the cycles you throw at it. You may not notice so much on a multicore
processor, but it remains a spin loop.


[...]
> The timestamp approach may be more accurate, but these delays are
> only approximations and accuracy is not critical. I may be thinking
> of the problem with using a timestamp in MSDOS, where the system
> timer wraps at midnight. The Delphi Date/Time probably does not have
> that problem.

With a little unsigned arithmetic and judicious ignoring of overflows,
the deadline approach (as opposed to the timeout approach) can be
made to work also. Which is quite irrelevant now. I agree that if
accuracy is not critical, counting down a timeout is fine.


> [...] it seems that I can
> use CreateWaitableTimer as the object, but then that requires an
> lpSecurityDescriptor which in turn has a set of items such as SIDs
> and ACLs. It seems awfully complicated just to cause a delay in the
> main thread's execution while other threads do their work.

That security descriptor can probably be filled for the most part with
NULLs. But in the main thread, a TTimer works just as well as
CreateWaitableTimer. Except that it (alone of all threads) should never
sleep in the first place. And your worker threads can just Sleep or
wait on events.


[...]
>> Try to make your main UI stateless, not dependent on wait loops
>> anywhere, allowing at any moment to do only those things that are
>> consistent with the state of the entire application including the
>> bits that run in their own threads. This is hard, especially if you
>> didn't build it that way from the beginning. Putting everything behind
>> actions can be a big help. Make sure the OnUpdate handlers can't hang
>> even momentarily. Never use any delays, anywhere.
>

> I think I understand what you are suggesting. I could make a state
> machine with the various functions I wish to perform be called in a
> particular sequence from a system timer which can implement delays as
> set up in a table or structure.

A state machine is the right idea, but again, there should be no delays,
no waiting. The program is _in_ a certain state, and when it's tickled
it progresses to the next state. But you should let your message loop
run - that's where your notification comes in. You're still thinking
in terms of polling; you should think of interrupts or events instead.

Groetjes,
Maarten Wiltink


0
Reply Maarten 1/5/2010 1:57:41 PM

Couldn't you use a WaitableTimer & WaitForSingleObject, or
WaitForSingleObject with a timeout. Or are they not thread-safe ?

Alan Lloyd
0
Reply alanglloyd 1/6/2010 8:49:28 AM

Maarten Wiltink wrote:
> "Arivald" <NOSPAMarivald@interia.pl> wrote in message
> news:hhtgva$3os$1@news.dialog.net.pl...
> [...]
>> Note: TTimer will not work properly in thread! TTimer uses windows
>> messages, and messahe loop should be accessed only from main, GUI
>> thread.
> 
> That's not strictly true (although it's a useful simplification for
> beginners). There isn't just one message loop. Every thread can have
> its own message loop, but only in the GUI thread do you get it for
> free, and worker threads don't usually need one.

The reason TTimer mustn't be used outside the main thread has nothing to 
do with using window messages. Rather, it's due to its use of 
AllocateHWnd, which calls MakeObjectInstance, which uses a global linked 
list that doesn't have any protection for multithreaded access.

Since any other thread must implement its own message loop anyway, you 
may as well just call SetTimer and handle the wm_Timer message yourself. 
Actually, you don't even have to handle it. Just provide a callback and 
a null window handle, and DispatchMessage will handle it for you. (Just 
remember to keep track of the timer ID that SetTimer returns so you can 
call KillTimer later.)

-- 
Rob
0
Reply Rob 1/6/2010 9:00:47 AM

<alanglloyd@aol.com> wrote in message
news:01789366-c79d-4381-aed0-6a16a8e52e87@j5g2000yqm.googlegroups.com...

> Couldn't you use a WaitableTimer & WaitForSingleObject, or
> WaitForSingleObject with a timeout. Or are they not thread-safe ?

I imagine they are _made_ to be thread-safe.

But I'd check MSDN all the same.

Groetjes,
Maarten Wiltink


0
Reply Maarten 1/6/2010 9:30:41 AM

Rob Kennedy pisze:
> Maarten Wiltink wrote:
>> "Arivald" <NOSPAMarivald@interia.pl> wrote in message
>> news:hhtgva$3os$1@news.dialog.net.pl...
>> [...]
>>> Note: TTimer will not work properly in thread! TTimer uses windows
>>> messages, and messahe loop should be accessed only from main, GUI
>>> thread.
>>
>> That's not strictly true (although it's a useful simplification for
>> beginners). There isn't just one message loop. Every thread can have
>> its own message loop, but only in the GUI thread do you get it for
>> free, and worker threads don't usually need one.
> 
> The reason TTimer mustn't be used outside the main thread has nothing to 
> do with using window messages. Rather, it's due to its use of 
> AllocateHWnd, which calls MakeObjectInstance, which uses a global linked 
> list that doesn't have any protection for multithreaded access.

Yes, i short-circuit. I do not know exactly why TTimer will fail... I 
just know no one VCL operation should be done from Thread. VCL itself is 
not thread-safe. It is why TThread.Synchronize() exists.
And TTimer is a part of VCL.

I short-circuit VCL <=> Message loop, because by default VCL message 
loops are always and only in main thread...

> Since any other thread must implement its own message loop anyway, you 

Yes, but only if thread needs message loop.
I have lot of threads without message loops.

> may as well just call SetTimer and handle the wm_Timer message yourself. 
> Actually, you don't even have to handle it. Just provide a callback and 
> a null window handle, and DispatchMessage will handle it for you. (Just 
> remember to keep track of the timer ID that SetTimer returns so you can 
> call KillTimer later.)

Yes, it is better way to use Windows timer, without using TTimer 
component. But, frankly, I just use Sleep() in thread if I need to 
wait... I see no gain in using timers in threads...

-- 
Arivald

0
Reply Arivald 1/6/2010 9:42:53 PM

Maarten Wiltink pisze:
> <alanglloyd@aol.com> wrote in message
> news:01789366-c79d-4381-aed0-6a16a8e52e87@j5g2000yqm.googlegroups.com...
> 
>> Couldn't you use a WaitableTimer & WaitForSingleObject, or
>> WaitForSingleObject with a timeout. Or are they not thread-safe ?
> 
> I imagine they are _made_ to be thread-safe.
> 

WaitForXXX functions are thread safe, and many waitable objects as well.

But code which use this functions and objects may be not thread safe... 
Programmer needs to know how it works, and know how it may fail. Best is 
to prepare foe worst-case scenario, assume everything may fail.

-- 
Arivald
0
Reply Arivald 1/6/2010 9:46:39 PM

"Maarten Wiltink" <maarten@kittensandcats.net> wrote in message 
news:4b445848$0$22920$e4fe514c@news.xs4all.nl...
> <alanglloyd@aol.com> wrote in message
> news:01789366-c79d-4381-aed0-6a16a8e52e87@j5g2000yqm.googlegroups.com...
>
>> Couldn't you use a WaitableTimer & WaitForSingleObject, or
>> WaitForSingleObject with a timeout. Or are they not thread-safe ?
>
> I imagine they are _made_ to be thread-safe.
>
> But I'd check MSDN all the same.

I found that the WaitableTimer and WaitForSingleObject to be too complex. 
And I found a way to accomplish the delays I need in a fairly simple 
manner:

        Tmo1 := fmUSB.Tmo;
        while fmUSB.USBThreadRunning and (fmUSB.Tmo = Tmo1) do
          Application.ProcessMessages;

This is not a very accurate delay, but in some ways it is ideal. The thread 
that is running contains the USB_Read function which has a timeout of 100 
mSec. When I start the thread I set fmUSB.Tmo to a value of 25, and upon 
successful completion of the USB_Read function the timeout is restored. So 
it only decrements when the read times out. In the cases where I am waiting 
for a sequence of characters to be returned, I want to wait until all have 
been received, and then the Tmo will decrement. When no return characters 
are expected, this should give a delay of about 100 mSec. This test is in a 
fairly tight loop, so when Tmo decrements the loop almost immediately loops 
back so that each loop is just about 100 mSec.

I also tried making a scheduler which performed the tasks I need in a 
sequence with a specified delay after each has run, but it was rather ugly, 
and would require a lot of rewrite to the program code. I found that there 
were really only a few places where a delay is needed, so this method 
should do the job. My application is also set up to use a serial port 
instead of the direct USB method, and in that case I will not have the USB 
thread running. But I will instead have a communications thread which 
handles the send and receive of commands and data, and I may be able to use 
it in the same way. But my original Delay function seemed to work fine in 
that mode.

Thanks for the ideas and discussion.

Paul 


0
Reply Paul 1/6/2010 10:15:51 PM

On 6 Jan, 22:15, "Paul E. Schoen" <p...@peschoen.com> wrote:
<snip>
> I found that the WaitableTimer and WaitForSingleObject to be too complex.
> And I found a way to accomplish the delays I need in a fairly simple
> manner:
<snip>

MSDN says of the Security Descriptor in CreateWaitableTimer (&
others) . . .

"lpTimerAttributes
[in] Pointer to a SECURITY_ATTRIBUTES structure that specifies a
security descriptor for the new timer object and determines whether
child processes can inherit the returned handle.
If lpTimerAttributes is NULL, the timer object gets a default security
descriptor and the handle cannot be inherited. "

So you could implement a delay with . . .

var
  WaitTimerHnd : THandle;
const
  Delay = 2000; //milliSec
begin
  WaitTimerHnd := CreateWaitableTimer(nil, false, '');
  WaitForSingleObject(WaitTimerHnd, Delay);
  CloseHandle(WaitTimerHnd);

BTW Delphi3 Windows.pas has an incorrect declaration for CWT() in that
it returns a BOOL not a handle.

Alan Lloyd
0
Reply alanglloyd 1/7/2010 7:43:16 AM

<alanglloyd@aol.com> wrote in message 
news:987a7a23-0515-4abf-aa4f-7fb94b251394@o28g2000yqh.googlegroups.com...
> On 6 Jan, 22:15, "Paul E. Schoen" <p...@peschoen.com> wrote:
> <snip>
>> I found that the WaitableTimer and WaitForSingleObject to be too 
>> complex.
>> And I found a way to accomplish the delays I need in a fairly simple
>> manner:
> <snip>
>
> MSDN says of the Security Descriptor in CreateWaitableTimer (&
> others) . . .
>
> "lpTimerAttributes
> [in] Pointer to a SECURITY_ATTRIBUTES structure that specifies a
> security descriptor for the new timer object and determines whether
> child processes can inherit the returned handle.
> If lpTimerAttributes is NULL, the timer object gets a default security
> descriptor and the handle cannot be inherited. "
>
> So you could implement a delay with . . .
>
> var
>  WaitTimerHnd : THandle;
> const
>  Delay = 2000; //milliSec
> begin
>  WaitTimerHnd := CreateWaitableTimer(nil, false, '');
>  WaitForSingleObject(WaitTimerHnd, Delay);
>  CloseHandle(WaitTimerHnd);
>
> BTW Delphi3 Windows.pas has an incorrect declaration for CWT() in that
> it returns a BOOL not a handle.

Thanks. I made that into a procedure and it seemed to work fine as a short 
delay in the main thread. But when I used it in a longer delay of 3000 mSec 
I found that it essentially causes the main VCL thread to sleep and it also 
causes the USB thread to timeout. I used my method and it seemed to be OK:

  fmDebug.AddDebugItem( 'fmUSB Show ');
  Count := 30;
  While Count > 0 do begin
    Tmo1 := fmUSB.Tmo;
    Dec(Count);
    while fmUSB.USBThreadRunning and (fmUSB.Tmo = Tmo1) do
      Application.ProcessMessages;
  end;
  fmDebug.AddDebugItem( 'DelayMsec(3000) ');

The actual delay was 4.5 seconds as determined by the timestamps on the 
Debug memo, which uses its own system timer. Using the WaitableTimer shows 
a zero delay because the system timer is suspended.

Quite possibly a separate thread could be launched with a 
WaitForSingleObject with the desired delay, although it would still need to 
have a loop in the main VCL thread with a ProcessMessages, so that the 
execution of the main thread will be suspended but other activities may 
continue.

Now I have a problem such that it appears that the USB device is not always 
responding to each command. I issue a Ctrl-R and then enter a timeout loop 
waiting for a four-byte count to be returned. But sometimes after a highly 
variable time there is nothing returned. The USB_SendChar function shows 
success. So perhaps I need to implement a form of handshaking to be sure 
the device has received and acted upon the command. In this case, skipping 
a reading is inconsequential, but if I issue a command to set up a certain 
configuration I need to know it has been done.

But that's not a Delphi issue. I need to go into the PIC code which is in C 
for the USB module and assembly for the data acquisition module. For a 
discussion of that please see:
http://www.microchip.com/forums/tm.aspx?m=465297&mpage=1&key=&#465397

Paul 


0
Reply Paul 1/8/2010 7:05:01 AM

alanglloyd@aol.com pisze:
> So you could implement a delay with . . .
> 
> var
>   WaitTimerHnd : THandle;
> const
>   Delay = 2000; //milliSec
> begin
>   WaitTimerHnd := CreateWaitableTimer(nil, false, '');
>   WaitForSingleObject(WaitTimerHnd, Delay);
>   CloseHandle(WaitTimerHnd);
> 

Why do no jyst use "Sleep(Delay)" ? It works like Your code, but is much 
shorter to write, and do not need extra variable...

-- 
Arivald
0
Reply Arivald 1/8/2010 7:30:12 AM

Paul E. Schoen pisze:
> <alanglloyd@aol.com> wrote in message 
> news:987a7a23-0515-4abf-aa4f-7fb94b251394@o28g2000yqh.googlegroups.com...
>> On 6 Jan, 22:15, "Paul E. Schoen" <p...@peschoen.com> wrote:
>> <snip>
>>> I found that the WaitableTimer and WaitForSingleObject to be too 
>>> complex.
>>> And I found a way to accomplish the delays I need in a fairly simple
>>> manner:
>> <snip>
>>
>> MSDN says of the Security Descriptor in CreateWaitableTimer (&
>> others) . . .
>>
>> "lpTimerAttributes
>> [in] Pointer to a SECURITY_ATTRIBUTES structure that specifies a
>> security descriptor for the new timer object and determines whether
>> child processes can inherit the returned handle.
>> If lpTimerAttributes is NULL, the timer object gets a default security
>> descriptor and the handle cannot be inherited. "
>>
>> So you could implement a delay with . . .
>>
>> var
>>  WaitTimerHnd : THandle;
>> const
>>  Delay = 2000; //milliSec
>> begin
>>  WaitTimerHnd := CreateWaitableTimer(nil, false, '');
>>  WaitForSingleObject(WaitTimerHnd, Delay);
>>  CloseHandle(WaitTimerHnd);
>>
>> BTW Delphi3 Windows.pas has an incorrect declaration for CWT() in that
>> it returns a BOOL not a handle.
> 
> Thanks. I made that into a procedure and it seemed to work fine as a short 
> delay in the main thread. But when I used it in a longer delay of 3000 mSec 
> I found that it essentially causes the main VCL thread to sleep and it also 
> causes the USB thread to timeout. I used my method and it seemed to be OK:
> 
>   fmDebug.AddDebugItem( 'fmUSB Show ');
>   Count := 30;
>   While Count > 0 do begin
>     Tmo1 := fmUSB.Tmo;
>     Dec(Count);
>     while fmUSB.USBThreadRunning and (fmUSB.Tmo = Tmo1) do
>       Application.ProcessMessages;
>   end;
>   fmDebug.AddDebugItem( 'DelayMsec(3000) ');
> 
> The actual delay was 4.5 seconds as determined by the timestamps on the 
> Debug memo, which uses its own system timer. Using the WaitableTimer shows 
> a zero delay because the system timer is suspended.
> 

try to use this code, i give You before

var
   waitTill: TDateTime;

begin
   fmDebug.AddDebugItem( 'fmUSB Show ');

//3000 msec -> 3 sec
   waitTill := Now() + EncodeTime(0, 0, 3, 0);

   while (Now() < waitTill) //time not elapsed yet
   and fmUSB.USBThreadRunning
   do
   begin
     Sleep(10); //give CPU time to other threads
     Application.ProcessMessages();
   end;

   fmDebug.AddDebugItem( 'DelayMsec(3000) ');
end;

it guarantee low CPU usage while waiting, and use real time clock.

-- 
Arivald

0
Reply Arivald 1/8/2010 7:39:08 AM

"Arivald" <NOSPAMarivald@interia.pl> wrote in message 
news:hi6nju$45b$1@news.dialog.net.pl...
>
> try to use this code, i give You before
>
> var
>   waitTill: TDateTime;
>
> begin
>   fmDebug.AddDebugItem( 'fmUSB Show ');
>
> //3000 msec -> 3 sec
>   waitTill := Now() + EncodeTime(0, 0, 3, 0);
>
>   while (Now() < waitTill) //time not elapsed yet
>   and fmUSB.USBThreadRunning
>   do
>   begin
>     Sleep(10); //give CPU time to other threads
>     Application.ProcessMessages();
>   end;
>
>   fmDebug.AddDebugItem( 'DelayMsec(3000) ');
> end;
>
> it guarantee low CPU usage while waiting, and use real time clock.

OK. That is much better. I made a procedure:

 procedure TfmTimerMain.DelayMsec( mSec: Integer );
 var
   waitTill: TDateTime;
   tSec, tMsec: Integer;
 begin
   tSec := mSec DIV 1000;
   tMsec := mSec MOD 1000;
   waitTill := Now() + EncodeTime(0, 0, tSec, tMsec);

   while (Now() <= waitTill) //time not elapsed yet
   do
   begin
     Sleep(10); //give CPU time to other threads
     Application.ProcessMessages();
   end;
 end;

The debug timer showed a delay of 2800 mSec for DelayMsec(3000). For 
DelayMsec(200) I measured an actual delay of 300 mSec. And for 
DelayMsec(100) I got 100 or 200 mSec. But I don't know if my Debug timer is 
very accurate.

I tried removing the sleep(10) with little or no change in performance. But 
having it might provide better performance for processes outside the 
application.

Thanks!

Paul


0
Reply Paul 1/8/2010 1:03:02 PM

16 Replies
246 Views

(page loaded in 0.246 seconds)

Similiar Articles:


















7/17/2012 4:01:18 AM


Reply: