Recent

Author Topic: At power down ?  (Read 1810 times)

dbannon

  • Hero Member
  • *****
  • Posts: 3556
    • tomboy-ng, a rewrite of the classic Tomboy
At power down ?
« on: September 06, 2025, 09:20:48 am »
I'd really like some help here.

Attached is a demo of a problem with LCL apps running when the Linux box powers down.  It creates 6 extra, non-modal windows keeping track of them in an array. When the main window closes down, it iterates over that array, closing each. (And, possibly, doing something specific because it's app is closing).

However, logging this as it happens reveals two separate actions -

  • Initially, one or more slave windows usually, somehow, get a close down signal. That is seen in the log in .FormCloseQuery() and .FormClose().
  • Seven seconds later, a SIGTERM (ie #15) is received by the main window and each one,      including any 'closed' in the above step, are nicely closed and freed.


It appears that the slave Windows 'closed' in stage 1 remain responsive, is perhaps set to not visible (or are disconnected from the XServer (or Wayland)  in much the same was as xkill works). So, it is still usable but has told the world its not.

I can not find any way to determine where this first, bogus 'close' command comes from. Nor any way to tell the difference between it and eg a user initiated close.

And the app holds up the OS shutdown by that seven seconds. If the slave window's CloseAction is set to caFree the window would be gone before a proper shutdown can be performed. Extract from log file (hh:nn:ss.zzz) -
Code: [Select]
16:22:34.652  Starting Log
16:22:42.740  TMyForm.FormCloseQuery Form 1
16:22:42.740  TMyForm.FormCloseQuery Visible=True
16:22:42.740  TMyForm.FormClose Form 1
16:22:49.759  Signal received #15
16:22:49.860  TMyForm.FormCloseQuery Form 0
16:22:49.860  TMyForm.FormCloseQuery Visible=True
16:22:49.860  TMyForm.FormClose Form 0
16:22:49.860  TMyForm.FormDestroy Form 0
16:22:49.964  TMyForm.FormCloseQuery Form 1
16:22:49.964  TMyForm.FormCloseQuery Visible=False
16:22:49.964  TMyForm.FormClose Form 1
16:22:49.964  TMyForm.FormDestroy Form 1
16:22:50.071  TMyForm.FormCloseQuery Form 2
....
16:22:50.410  The FormMain is closed
16:22:50.410  Signal received #1
16:22:50.410  Igoring second signal #1

Note that the app is first affected by the shutdown at 42.740, one of the slave windows is hidden. Then 7 seconds later, the MainForm gets the message via my HandleSig() method ! And in this case, Form 1 is now hidden (and would be gone if CloseAction=caFree).

How and why does the slave window get this close signal before FormMain ?

If I send the app a SIGTERM with the kill command, no problems, it all works as it should. The issue is at shutdown where, I believe, System V or SystemD will send a SIGTERM in exactly the same way !

The attached project, should be compiled, then run (perhaps outside the IDE) and the computer told to restart. Find the log in $HOME and some demo text, written as each Window shuts down, in $HOME/junk/.

Any insight would be greatly appreciated !

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

dbannon

  • Hero Member
  • *****
  • Posts: 3556
    • tomboy-ng, a rewrite of the classic Tomboy
Re: At power down ?
« Reply #1 on: September 11, 2025, 11:48:42 am »
OK,  I am so desperate  I respond to my own post !

Just to point out the problem seems to be in LCL, a "pure" FPC program works as expected. Thus -
Code: Pascal  [Select][+][-]
  1. program catchterm;
  2.  
  3. uses SysUtils, BaseUnix;
  4.  
  5. var   ExitNow : boolean;
  6.       MesgTime : integer;
  7.       RecSig : integer;
  8.  
  9. procedure WriteLog(Stuff : string; Init : boolean = false);
  10. var T : TextFile;
  11. begin
  12.     AssignFile(T, 'catchterm.log');
  13.     if Init then begin
  14.         if FileExists('catchterm.log') then DeleteFile('catchterm.log');
  15.         rewrite(T);
  16.     end else
  17.         append(T);
  18.     writeln(T, FormatDateTime('hh:nn:ss.zzz ', now()) + '  ' + Stuff);
  19.     flush(T);
  20.     closeFile(T);
  21. end;
  22.  
  23. // suggestion that absolutly miminal code allowed in a signal handler in C  ?
  24. procedure HandleSig(aSignal: LongInt); cdecl;
  25. begin
  26.     if aSignal <> SigTERM then exit;    // just ignore SigHUP, we act on SigTERM
  27.     if RecSig = 0 then begin
  28.         RecSig := aSignal;
  29.         writelog('SigTerm received');
  30.         ExitNow := True;            // we exit gracefully ??
  31.     end;                            // the shutdown does NOT commence until after handler exits
  32. end;
  33.  
  34. procedure DoExitStuff();   // do not write to screen from here !
  35. var i : integer = 0;
  36. begin
  37.     if RecSig > 0 then begin
  38.         while i < 50 do begin
  39.             sleep(100);
  40.             inc(i);
  41.             if i mod 10 = 0 then
  42.                 WriteLog('I''m still here doing shutdown stuff ' + inttostr(i div 10));
  43.         end;
  44.     end;
  45. end;
  46.  
  47. begin
  48.     ExitNow := False;
  49.     MesgTime := 0;
  50.     writelog('App started ', true);
  51.     FpSignal(SigTERM, @HandleSig);          // FpSignal lies, sets errorno but works as expected.
  52.     FpSignal(SigHUP,  @HandleSig);
  53.     while not ExitNow do begin              // just sit here and try to look busy
  54.         inc(MesgTime);
  55.         if MesgTime mod 500 = 0 then
  56.             WriteLog('I am still here');
  57.         sleep(10);
  58.     end;
  59.     DoExitStuff();  // here because we are exiting gracefully.
  60.     writelog('OK, we are done here.');
  61. end.


If you restart while this is running, you get this log -
Code: [Select]
19:17:32.357   App started
19:17:37.616   I am still here
19:17:42.855   I am still here
19:17:48.600   I am still here
19:17:53.907   I am still here
19:17:59.469   I am still here
19:18:04.662   I am still here
19:18:09.872   I am still here
19:18:15.079   I am still here
19:18:20.292   I am still here
19:18:21.321   SigTerm received
19:18:22.333   I'm still here doing shutdown stuff 1
19:18:23.337   I'm still here doing shutdown stuff 2
19:18:24.340   I'm still here doing shutdown stuff 3
19:18:25.346   I'm still here doing shutdown stuff 4
19:18:26.352   I'm still here doing shutdown stuff 5
19:18:26.353   OK, we are done here.
My experiments indicate an app can delay up to 90 seconds like this but apparently it depends on the OS. But not an LCL app.

So, why does LCL somehow decide to close down one or more child windows and then kill off the main one while its still trying to do its job ?

Davo

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
Re: At power down ?
« Reply #2 on: September 11, 2025, 01:35:02 pm »
Did you know about Application.OnQueryEndSession? Not form....That is where you should handle power down.
This is only interrupted/fails by hard powerdown or power outage and these cases can not be managed in code.

There are humungous amounts of examples, also on the forum.
« Last Edit: September 11, 2025, 01:49:31 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

dbannon

  • Hero Member
  • *****
  • Posts: 3556
    • tomboy-ng, a rewrite of the classic Tomboy
Re: At power down ?
« Reply #3 on: September 11, 2025, 02:07:15 pm »
Did you know about Application.OnQueryEndSession?

Nope, never heard of it !  I will have a good read !

Thanks

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

dbannon

  • Hero Member
  • *****
  • Posts: 3556
    • tomboy-ng, a rewrite of the classic Tomboy
Re: At power down ?
« Reply #4 on: September 11, 2025, 02:57:34 pm »
There are humungous amounts of examples, also on the forum.

There are five threads about it in the forum, general conclusion was not very useful, nothing in the last five years.

No mention of it in Lazarus Examples.

The on line help mentions it as one property of TApplication.

Not sure I'd call that humungous amounts of examples. But the wiki did mention it exists as an entry in the IDE toolbar under Additional. So, drop a TApplicationProperties on the mainform, seems to be able to make the project's Application properties available. And put a call to 'close' in the appropriate event, sure enough, it closes my forms before the app is KILL-ed. Wow !

Sadly, it then locks up in some endless loop until the usual 90 second timeout but that  is,  am sure, because I am using it incorrectly. I'll look for more of those humungous examples. Maybe I need to do the form by form closing in the OnQueryEndSession event rather that using my existing close recipe ? Calling close does sound a bit re-entrant.

Important to note, this event is NOT fired with the expected SigTERM or SigHUP, you do need to do a real powerdown to see it happen. Interesting.

Anyway, with this little hint I have made more progress than in the last three days ! Light at the end of the tunnel.....

Davo
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
Re: At power down ?
« Reply #5 on: September 11, 2025, 04:19:44 pm »
I was referring to both Lazarus and Delphi.
I can write you an example if you want.
Come to think of it: OnCloseQuery in the main form can be used in the same fashion.
It is the only place where you can do some work *just* before shutdown, like closing sockets, rotating logs, thread clean-up etc..
That examples are old does not matter as long as they are good.
That it is little used by amateurs (not that I would call you that) is simply lack of knowledge.
That's what you get with screendrawers pretending to program.
The same scheme is used in many other programming languages. In Windows it is even part of the API as it is in UNIX, I believe. (but be careful with SIGKILL which does not obey clean shutdown, just like halt() )

This only works on a real restart or shutdown although formclosequery also works on a simple application shutdown.

Example that writes the time of last system shutdown in a caption:
Code: Pascal  [Select][+][-]
  1. unit query;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs;
  9.  
  10. type
  11.  
  12.   { TForm1 }
  13.  
  14.   TForm1 = class(TForm)
  15.     procedure FormCreate(Sender: TObject);
  16.   private
  17.  
  18.   public
  19.      procedure QueryEndSession(var cancel:Boolean);
  20.   end;
  21.  
  22. var
  23.   Form1: TForm1;
  24.  
  25. implementation
  26.  
  27. {$R *.lfm}
  28.  
  29. { TForm1 }
  30.  
  31. procedure TForm1.FormCreate(Sender: TObject);
  32. var
  33.   f:text;
  34.   s:string;
  35. begin
  36.   Application.OnQueryEndSession := @QueryEndSession;
  37.   if FileExists('laststart.txt') then
  38.   begin
  39.     System.assign(f,'laststart.txt');
  40.     reset(f);
  41.     readln(f,s);
  42.     form1.caption := s;
  43.     System.Close(f);
  44.   end;
  45. end;
  46.  
  47. procedure TForm1.QueryEndSession(var cancel: Boolean);
  48. var
  49.   f:text;
  50. begin
  51.  // here you make sure all clean-ups are done.
  52.  // threads, sockets, you name it.
  53.  // better not use GUI elements here...these block.
  54.   cancel  := true;
  55.   system.Assign(f,'laststart.txt');
  56.   rewrite(f);
  57.   writeln(f, DateTimeToStr(Now));
  58.   system.close(f);
  59.   cancel := false;
  60. end;
  61.  
  62. end.
You have to run the application at least twice and with a real system restart to see the result.
You can cancel during the restart, the file will be written.
« Last Edit: September 11, 2025, 05:58:11 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

dbannon

  • Hero Member
  • *****
  • Posts: 3556
    • tomboy-ng, a rewrite of the classic Tomboy
Re: At power down ?
« Reply #6 on: September 12, 2025, 06:47:23 am »
Yep, that works absolutely as I (and, importantly, my end users) expect.

In my test app, I dropped an Application component on the form, your model of using Application is neater and I tested my real app using that. In both cases, it works perfectly!

I have had a good look at how it works and I conclude that while FormCloseQuery() is called during a powerdown event, it is not able to delay the SigKILL (or maybe just power going off) for any more that 20mS or so.  On the other hand, TApplication.OnQueryEndSession can insist on its 90 second delay if necessary.

As this is an important behaviour, I'll make some notes on the wiki.

Thanks Thaddy, I am very glad to finally understand this issue.

Davo

Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
Re: At power down ?
« Reply #7 on: September 12, 2025, 07:34:03 am »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

dbannon

  • Hero Member
  • *****
  • Posts: 3556
    • tomboy-ng, a rewrite of the classic Tomboy
Re: At power down ?
« Reply #8 on: September 12, 2025, 11:33:38 am »
But mark the difference:
https://linuxhandbook.com/sigterm-vs-sigkill/

Oh,  should definitely never be confused, by either the programmer or the user/sysAmin. I taught my SysAdmin staff to use SigTERM if necessary but if that fails, think very carefully before using SigKILL. My (evolving) code redirects SigTERM to my own handler that calls FormClose. Thats fine if its from, eg, the kill command which send SigTerm by default. Its not adequate if its a powerdown. So far, it appears Application.OnQueryEndSession takes precedence over FPSignal(). So, my writeup (subject to revision) will state you need to handle at least four different scenarios -

* User initiated close with the Window Decoration close button which ends up in FormCloseQuery();
* User initiated close from a menu, button etc, call close and it ends up in FormCloseQuery();
* A PowerDown event, LCL apps needs to be caught in Application.OnQueryEndSession. In CLI, by a handler redirected to by FPSignal()
* App getting a SigTERM (but NOT at powerdown), caught in a handler redirected to by FPSignal().

I have not addressed HUP, fpc/LCL apps, out of the box, exit immediately on HUP same as TERM. That surprise me, there is a convention that HUP means a re-initialize but not exit, typically reread config etc.

And I prefer to use FPSignal() rather than  FPSigAction(), FPSignal() is marked as depreciated in source but probably by mistake. The C equivalent, signal() is depreciated because it depends on existence (or otherwise) of a macro. FPSignal() has no such dependency and is a useful wrapper round (an awkward) FPSigAction().

There could be a paper in this you know ....

Davo
 
Lazarus 3, Linux (and reluctantly Win10/11, OSX Monterey)
My Project - https://github.com/tomboy-notes/tomboy-ng and my github - https://github.com/davidbannon

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
Re: At power down ?
« Reply #9 on: September 12, 2025, 11:44:57 am »
To add  to the confusion the kill command calls SIGTERM unless SIGKILL is explicitly specified ;)
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

LeP

  • Jr. Member
  • **
  • Posts: 57
Re: At power down ?
« Reply #10 on: September 12, 2025, 12:03:10 pm »
For those using Windows (not entirely relevant to the needs discussed here, but relevant to the topic),
I intercept the QueryEndSession message and request a time extension to perform all necessary operations, including asynchronous ones.
At the end, in the destoy procedure I release the lock and Windows shutdown or logoff the session.
This is because in Windows, the QueryEndSession message must be completed within 5 seconds, after which the operating system terminates the application.

Code: Pascal  [Select][+][-]
  1. function ShutdownBlockReasonCreate(hWnd: HWND; pwszReason: PWCHAR): BOOL; stdcall; external 'USER32.dll' name 'ShutdownBlockReasonCreate';
  2. function ShutdownBlockReasonDestroy(hWnd: HWND): BOOL; stdcall; external 'USER32.dll' name 'ShutdownBlockReasonDestroy';
  3.  
  4. procedure TForm1.FormDestroy(Sender: TObject);
  5. begin
  6.   ShutdownBlockReasonDestroy(handle);
  7. end;
  8.  
  9. procedure TForm1.QEndSession(var Msg: TMessage);
  10. begin
  11.   ExitCode := 0;
  12.   Msg.Result := 1;
  13.   ShutdownBlockReasonCreate(Handle,  'Delayed');
  14.   Close;
  15. end;
« Last Edit: September 12, 2025, 12:05:35 pm by LeP »

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
Re: At power down ?
« Reply #11 on: September 12, 2025, 02:01:38 pm »
You are using the form's handle and that seems wrong to me.
On Windows, pass hInstance. (The application windows handle)
The reason is that the form may be destroyed while there are still running tasks associated with the application.
The global hInstance variable is associated with WinMain and is the correct handle in this particular case.

Otherwise it is a useful addition to the discussion.

One extra remark: ShutdownBlockReasonCreate() must not be called from within QueryEndSession itself, it seems you must call it before QueryEndSession is entered. I tested and put it simply in Mainform.Create;
« Last Edit: September 12, 2025, 02:20:48 pm by Thaddy »
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

LeP

  • Jr. Member
  • **
  • Posts: 57
Re: At power down ?
« Reply #12 on: September 12, 2025, 02:51:09 pm »
You are using the form's handle and that seems wrong to me.
On Windows, pass hInstance. (The application windows handle)
The global hInstance variable is associated with WinMain and is the correct handle in this particular case.

May be you are right, but I always used in this way (since times of XP) and it works. I'll do some test in the future with HINSTANCE.

The reason is that the form may be destroyed while there are still running tasks associated with the application.

It's not possible, the Main Form is always the last resource opened. I always shutdown all things waiting for all, for this I used extended procedure for shutdown.

One extra remark: ShutdownBlockReasonCreate() must not be called from within QueryEndSession itself, it seems you must call it before QueryEndSession is entered.

Yes, it's possible but there is not meaning. If the application doesn't catch QueryEndSession it means that something was wrong so it's indifferent about extended or not the "shutdown".
This procedure is connected specifically to OS operations so I prefer to do like should do.

Additional remarks may be helpful for Windows users: in the QueryEndSession procedure it is recommended not to perform any cleanup operations.

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
Re: At power down ?
« Reply #13 on: September 12, 2025, 03:15:41 pm »
[It's not possible, the Main Form is always the last resource opened. I always shutdown all things waiting for all, for this I used extended procedure for shutdown.
It is possible. Loads of my server applications only have a form to make some settings or to shutdown. All services/tasks run independent of the form.
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

Thaddy

  • Hero Member
  • *****
  • Posts: 18306
  • Here stood a man who saw the Elbe and jumped it.
Re: At power down ?
« Reply #14 on: September 12, 2025, 03:17:20 pm »
Additional remarks may be helpful for Windows users: in the QueryEndSession procedure it is recommended not to perform any cleanup operations.
That I do not understand. Do you have more info on that? From MS itself, not from some forum/?
Due to censorship, I changed this to "Nelly the Elephant". Keeps the message clear.

 

TinyPortal © 2005-2018