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=񱧵
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: Running SAS on Ubuntu via wine - comp.soft-sys.sasLinux assembly using C library functions - comp.unix ... ... Implementing delays while thread is running - comp.lang ... Your Distro; Ubuntu Linux; Exception handling in ... Value at Risk: Security level code required - comp.soft-sys.matlab ...Implementing delays while thread is running - comp.lang.pascal ... In "C" any non-zero value is True and a ... But although the generated code may differ, on the language ... How to implement exception handling in linux assembly? - comp.lang ...} } Just don't use MFC exception handling macros and don't forget ... GnuPG v1.4.9 (GNU/Linux) Comment: Processed by ... Implementing delays while thread is running - comp ... How to tell if an exception has currently been thrown and is being ...Implementing delays while thread is running - comp.lang.pascal ..... at the code, that seems possible only if an exception ... But most of the low-level stuff is being ... Wait or Delay - comp.lang.rexxDelay in C on linux or OSS - comp.lang.c If you're trying to implement a delay and ... matlab... ao); start(ai); data = getdata(ai); wait ... delays while thread is running ... System hang-up using RT Workshop external mode in RT-Linux - comp ...... Matlab/Simulink for communication with a target running ... However, I build in a thread which kills the target ... Matlab/Simulink: "ExtGetTargetPkt() call failed while ... Tries to save the file, instead of executing it - comp.lang.php ...Every once in a while, when I execute one of my PHP ... com/myscript.php), instead of the script running, I ge... ... I read the thread, I understand >>>>> that this is ... clock microseconds with resolution in milliseconds - comp.lang.tcl ...Is there another way to implement some kind of microsecond delay (must not be ... microseconds] + $usec}] >> while ... Since the program is only running on windows ... Missing reference - comp.databases.ms-accessBoth machines are running Access 2000 on Windows XP ... mfG --> stefan <-- [ Taken from the thread at ... I just didn't implement such a method. Tom Wickerath ... Problem with pthreads C++ wrapper class on Linux - comp ...... and override the function call operator to implement your // Thread ... The reason is that while the resources/entities ... Program-wrapper to avoid multiple running instances ... Application hangs - comp.sys.hp.hpuxIt hangs after running for sometime (2-3 hours ... the application code calls some non-thread safe functions. To implement ... While I won't rule-out a problem with the ... Watchdog thread syncronisation with semphores? - comp.unix ...my design is as follows: 1 'comms' thread for each ... this before, but this is embedded XScale CPU running QNX ... void PMS_PIG_Socket_Report::Run(void) { while(1 ... fork() race in SIGCHLD handler - comp.unix.programmer... delay.tv_nsec = 0; while (nanosleep(&delay, &residual ... the signal handler are in the same thread of ... One can block signals while a handler is running (with sigaction ... Dynamically populate text field based on dropdown box value in ...How do we go about implementing this? I am using > Coldfusion ... web server will be large and therefore take a while to ... Previous by thread: deleting temporary internet files ... Thread-safe Queue on Win32 - comp.programming.threads... LeaveCriticalSection(&cs); > > Pop: > EnterCriticalSection(&cs); > while ... pool of threads save one blocked on rapidly- filling queue, and only one thread is running ... implement time delay in c - Stack OverflowI need to implement a time delay in C. for example I want to do ... between a signal handler and the main thread. I ... char **argv) { int running = 3; while( running ... Walkthrough: Implementing a Form That Uses a Background Operation... amount of time, the main UI thread will not be interrupted by this delay ... this example, see How to: Implement a ... While the calculation is running in the background, you will ... 7/17/2012 4:01:18 AM
|