Lazarus

Programming => General => Topic started by: pcurtis on September 28, 2021, 08:53:54 am

Title: [SOLVED] Command timeout
Post by: pcurtis on September 28, 2021, 08:53:54 am
What would be the best way to implement a command timeout?

I want to repeatedly call a function and if its result doesn't change after 2 seconds then fail?

Thanks in advance.

[Edit to fix subject "timepit" > "timeout" :-]
Title: Re: Command timeout
Post by: Zvoni on September 28, 2021, 09:11:35 am
A Timer comparing "old" Result with "Current" Result?
Title: Re: Command timeout
Post by: MarkMLl on September 28, 2021, 09:47:36 am
Is the function under your full control?

If so why not simply write it with an internal iteration or time limit?

If not you might need to encapsulate it in a thread which can be killed... you might be getting into OS-specific areas here.

Or are you talking about something like a dialog(ue) box with a timeout?

MarkMLl
Title: Re: Command timeout
Post by: BobDog on September 29, 2021, 01:01:45 am

A quick way in windows
Code: Pascal  [Select][+][-]
  1.  program timeout;
  2.  
  3. function msgbox(p:pointer;msg:pchar;title:pchar;i1:int32;i2:int32;WaitTime:int32):int32; external 'user32.dll' name 'MessageBoxTimeoutA';
  4.  
  5.  
  6. begin
  7. msgbox(nil,'Wait 2 seconds','Hello',0,0,2000);
  8. writeln('Press return to end . . .');
  9. readln;
  10. end.
Title: Re: Command timeout
Post by: Leledumbo on September 29, 2021, 11:51:22 am
Just TTimer with Interval of 2 seconds with OnTimer checking the return value of the function, preferably held by a shared variable. This is needed to counter the possibility of misbehaving, i.e. the function result has changed, but it's too late to set TTimer.Enabled to false, as the OnTimer event is already fired. Do remember that TTimer has its own thread, so it's run independently from the main thread, where your repeated function call is likely to be. There's still tiny tiny chance for the misbehaving, with such a sequential scenario (MT = main thread, TT = timer thread):
just be aware, as this is not a fixable condition. From concurrency perspective, above scenario is perfectly valid, i.e. it has to fail.
Title: Re: Command timeout
Post by: Zvoni on September 29, 2021, 12:44:45 pm
Leledumbo,
i'd actually turn it around: Run the Function in TT every 2 seconds, If oldvalue=newvalue then Fail:=True

EDIT: My approach only makes sense, if the call-interval for the function is 2 seconds.
OP doesn't write what the call-interval for the function is (100 times / second?)

So a basic algorithm would be:

Init Function and retrieve first Result and Start Timer with Timespan being 2 seconds
In a loop (somewhere):
Call Function and compare new Result against old result
If New Result<>Old Result then
Begin
Stop Timer,
set Old Result:=New Result
Start Timer again (to start the 2 seconds anew)
End
Loop end

In OnTimer (which fires every 2 seconds)
If New Result=Old Result Then Stop Timer and Nuke Computer (or whatever)

EDIT2: Just had an idea (throwing it to the experts here):
Would it be possible to encase the comparison/stopping/starting of Timer in a critical Section?
Title: Re: Command timeout
Post by: MarkMLl on September 29, 2021, 01:41:42 pm
Would it be possible to encase the comparison/stopping/starting of Timer in a critical Section?

Probably, but since you've written an explicit enclosing loop an easier way would be simply to pause (call APM 100 times) and then look at Now() before looping. I consider simplicity to be a major advantage in this sort of situation, since a solution which is substantially more "clever" than somebody who poses a question would arrive at himself is likely to cause enormous problemsfor eve^rybody when he comes to debug and maintain it.

However none of this helps very much if OP's problem is actually that he's going into somebody else's function which has no timeout and wants to generate a signal of some sort to force it to return.

MarkMLl
Title: Re: Command timeout
Post by: Zvoni on September 29, 2021, 01:47:09 pm
Mark,

hmm, yeah, that's an idea.
I'd probably go with a GetTickCount64, saving the TickCount as my new starting point if OldValue<>NewValue. Then it's basic math

EDIT:
So it would be basically something like this (untested)
Careful: It's a blocking code (nothing else gets executed) and it could end up in an endless loop (in the event, that the Function really returns a different value with each call/within 2 seconds)
Code: Pascal  [Select][+][-]
  1. Procedure MyFail;
  2. Var
  3.    StartTime:QWord;
  4.    MyResultOld:Integer;
  5.    MyResultNew:Integer;
  6. Begin
  7.    StartTime:=GetTickCount64;
  8.    MyResultOld:=MyFunction(MyParameters);
  9.    Repeat
  10.      MyResultNew:=MyFunction(MyParameters);
  11.      If MyResultOld<>MyResultNew Then
  12.         Begin
  13.            StartTime:=GetTickCount64;
  14.            MyResultOld:=MyResultNew;
  15.         End;
  16.    Until GetTickCount64-StartTime>=2000;
  17. End;
  18.  
Title: Re: Command timeout
Post by: MarkMLl on September 29, 2021, 02:15:28 pm
I'd probably go with a GetTickCount64, saving the TickCount as my new starting point if OldValue<>NewValue. Then it's basic math

I tend to overuse Now(), despite being very unhappy with some of its implementation details (forces an indeterminate amount of floating point maths into a loop, and having progressively less precision as we move away from the epoch).

However unlike integer-based solutions it does have the advantage of being effectively immune to the risk of rollover in the comparison.

MarkMLl
Title: Re: Command timeout
Post by: Zvoni on September 29, 2021, 02:23:38 pm
I tend to overuse Now(), despite being very unhappy with some of its implementation details (forces an indeterminate amount of floating point maths into a loop, and having progressively less precision as we move away from the epoch).

However unlike integer-based solutions it does have the advantage of being effectively immune to the risk of rollover in the comparison.

MarkMLl

Agreed.
However, it's up to OP now. We've shown some ways to skin that cat
Title: Re: Command timeout
Post by: alpine on September 29, 2021, 04:11:50 pm
There is too little information from the OP.

What would be the best way to implement a command timeout?
Command to what? External device or the operating system?

I want to repeatedly call a function and if its result doesn't change after 2 seconds then fail?
Call the function with what frequency?
Do the call frequency affects the result, i.e. is the function 'drives' something (on each call) or just fetches a value?

Is it a GUI application?
Is it a multi-threaded application?
How quick the change in the result should be detected?

If it is a GUI application, some delays can be tolerated, with an external process driven, the reaction time can be of high importance.

If the quick reaction is not so important, I'd go for something like:
Code: Pascal  [Select][+][-]
  1. var
  2.   I, OldV, NewV: integer;
  3. begin
  4.   OldV := MyFunction();
  5.   for I := 1 to 20 do
  6.   begin
  7.     Sleep(100);
  8.     NewV := MyFunction();
  9.     if OldV <> NewV then
  10.       Break;
  11.   end;
  12.   if NewV = OldV then
  13.     ; // Timeout!
  14. end;
This will keep the thread mostly inactive, giving more processor time for the ongoing command/process.
While the previous Zvoni suggestions are perfectly valid, calling GetLocalTime (Now) or GetTickCount64 in a very tight loop can degrade MT and to slow down execution of concurrent process.

It all depends on the particular case, of course.

Title: Re: Command timeout
Post by: pcurtis on September 29, 2021, 04:49:04 pm
I solved like this

Code: Pascal  [Select][+][-]
  1. function TForm1.HasChanged(aOldTitle : String; aHandle : HWND) : Boolean;
  2. var
  3.   iTemp : Integer;
  4. begin
  5.   Result := False;
  6.   for iTemp := 1 to 20 do
  7.     begin
  8.       if aOldTitle <> GetTitle(aHandle) then
  9.         begin
  10.           Result := True;
  11.           Break;
  12.         end;
  13.       Sleep(50);
  14.     end;
  15. end;
  16.  

Same as code from y.ivanov
TinyPortal © 2005-2018