Recent

Author Topic: [SOLVED] Command timeout  (Read 3974 times)

pcurtis

  • Hero Member
  • *****
  • Posts: 951
[SOLVED] Command timeout
« 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" :-]
« Last Edit: September 29, 2021, 04:49:56 pm by pcurtis »
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

Zvoni

  • Hero Member
  • *****
  • Posts: 2319
Re: Command timeout
« Reply #1 on: September 28, 2021, 09:11:35 am »
A Timer comparing "old" Result with "Current" Result?
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Command timeout
« Reply #2 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
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

BobDog

  • Sr. Member
  • ****
  • Posts: 394
Re: Command timeout
« Reply #3 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.

Leledumbo

  • Hero Member
  • *****
  • Posts: 8746
  • Programming + Glam Metal + Tae Kwon Do = Me
Re: Command timeout
« Reply #4 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):
  • TT = trigger OnTimer; MT = function still returns the same value
  • TT = check function result, still the same value; MT = function still returns the same value
  • TT = any code after the check; MT = function returns a new value
  • TT = fail; MT = no idea what you want when this happens
just be aware, as this is not a fixable condition. From concurrency perspective, above scenario is perfectly valid, i.e. it has to fail.

Zvoni

  • Hero Member
  • *****
  • Posts: 2319
Re: Command timeout
« Reply #5 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?
« Last Edit: September 29, 2021, 01:06:19 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Command timeout
« Reply #6 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
« Last Edit: September 29, 2021, 04:24:15 pm by MarkMLl »
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Zvoni

  • Hero Member
  • *****
  • Posts: 2319
Re: Command timeout
« Reply #7 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.  
« Last Edit: September 29, 2021, 01:59:13 pm by Zvoni »
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

MarkMLl

  • Hero Member
  • *****
  • Posts: 6676
Re: Command timeout
« Reply #8 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
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Zvoni

  • Hero Member
  • *****
  • Posts: 2319
Re: Command timeout
« Reply #9 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
One System to rule them all, One Code to find them,
One IDE to bring them all, and to the Framework bind them,
in the Land of Redmond, where the Windows lie
---------------------------------------------------------------------
Code is like a joke: If you have to explain it, it's bad

alpine

  • Hero Member
  • *****
  • Posts: 1038
Re: Command timeout
« Reply #10 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.

"I'm sorry Dave, I'm afraid I can't do that."
—HAL 9000

pcurtis

  • Hero Member
  • *****
  • Posts: 951
Re: Command timeout
« Reply #11 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
Windows 10 20H2
Laz 2.2.0
FPC 3.2.2

 

TinyPortal © 2005-2018