Discussion:
Implementing delays while thread is running
(too old to reply)
Paul E. Schoen
2010-01-04 11:05:33 UTC
Permalink
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
Maarten Wiltink
2010-01-04 12:09:20 UTC
Permalink
Post by Paul E. Schoen
Some time ago I came up with a simple way to implement delays by using a
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.
Post by Paul E. Schoen
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).
Post by Paul E. Schoen
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.
Post by Paul E. Schoen
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.
Post by Paul E. Schoen
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.
Post by Paul E. Schoen
[...] 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
Paul E. Schoen
2010-01-05 12:41:52 UTC
Permalink
Post by Maarten Wiltink
Post by Paul E. Schoen
Some time ago I came up with a simple way to implement delays by using a
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)?
((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
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.
Post by Maarten Wiltink
Post by Paul E. Schoen
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.
Post by Maarten Wiltink
Post by Paul E. Schoen
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.
Post by Maarten Wiltink
Post by Paul E. Schoen
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.
Post by Maarten Wiltink
Post by Paul E. Schoen
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.
Post by Maarten Wiltink
Post by Paul E. Schoen
[...] 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.
Post by Maarten Wiltink
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.
Post by Maarten Wiltink
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
Maarten Wiltink
2010-01-05 13:57:41 UTC
Permalink
[...]
Post by Paul E. Schoen
Post by Maarten Wiltink
if (DelayInProgress) then...
Really.
[...]
Post by Paul E. Schoen
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.)


[...]
Post by Paul E. Schoen
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.


[...]
Post by Paul E. Schoen
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.
Post by Paul E. Schoen
[...] 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.


[...]
Post by Paul E. Schoen
Post by Maarten Wiltink
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
Arivald
2010-01-04 19:50:36 UTC
Permalink
Post by Paul E. Schoen
Some time ago I came up with a simple way to implement delays by using a
[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
Maarten Wiltink
2010-01-05 09:41:15 UTC
Permalink
"Arivald" <***@interia.pl> wrote in message news:hhtgva$3os$***@news.dialog.net.pl...
[...]
Post by Arivald
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
Rob Kennedy
2010-01-06 09:00:47 UTC
Permalink
Post by Maarten Wiltink
[...]
Post by Arivald
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
Arivald
2010-01-06 21:42:53 UTC
Permalink
Post by Rob Kennedy
Post by Maarten Wiltink
[...]
Post by Arivald
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...
Post by Rob Kennedy
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.
Post by Rob Kennedy
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
a***@aol.com
2010-01-06 08:49:28 UTC
Permalink
Couldn't you use a WaitableTimer & WaitForSingleObject, or
WaitForSingleObject with a timeout. Or are they not thread-safe ?

Alan Lloyd
Maarten Wiltink
2010-01-06 09:30:41 UTC
Permalink
Post by a***@aol.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
Arivald
2010-01-06 21:46:39 UTC
Permalink
Post by Maarten Wiltink
Post by a***@aol.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
Paul E. Schoen
2010-01-06 22:15:51 UTC
Permalink
Post by Maarten Wiltink
Post by a***@aol.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
a***@aol.com
2010-01-07 07:43:16 UTC
Permalink
On 6 Jan, 22:15, "Paul E. Schoen" <***@peschoen.com> wrote:
<snip>
Post by Paul E. Schoen
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
<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
Paul E. Schoen
2010-01-08 07:05:01 UTC
Permalink
Post by a***@aol.com
<snip>
Post by Paul E. Schoen
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
<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
Arivald
2010-01-08 07:39:08 UTC
Permalink
Post by Paul E. Schoen
Post by a***@aol.com
<snip>
Post by Paul E. Schoen
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
<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
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
Paul E. Schoen
2010-01-08 13:03:02 UTC
Permalink
Post by Arivald
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

Arivald
2010-01-08 07:30:12 UTC
Permalink
Post by a***@aol.com
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
Loading...