At power down ? lazarus / 32-Linux ---------------------------------------------------------------------- dbannon · 2025-09-06 07:20 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 ---------------------------------------------------------------------- dbannon · 2025-09-11 09:48 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i program catchterm; uses SysUtils, BaseUnix; var   ExitNow : boolean;      MesgTime : integer;      RecSig : integer; procedure WriteLog(Stuff : string; Init : boolean = false);var T : TextFile;begin    AssignFile(T, 'catchterm.log');    if Init then begin        if FileExists('catchterm.log') then DeleteFile('catchterm.log');        rewrite(T);    end else        append(T);    writeln(T, FormatDateTime('hh:nn:ss.zzz ', now()) + '  ' + Stuff);    flush(T);  closeFile(T);end; // suggestion that absolutly miminal code allowed in a signal handler in C  ?procedure HandleSig(aSignal: LongInt); cdecl;begin    if aSignal <> SigTERM then exit;    // just ignore SigHUP, we act on SigTERM    if RecSig = 0 then begin        RecSig := aSignal;        writelog('SigTerm received');        ExitNow := True;          // we exit gracefully ??    end;                            // the shutdown does NOT commence until after handler exitsend; procedure DoExitStuff();   // do not write to screen from here !var i : integer = 0;begin    if RecSig > 0 then begin        while i < 50 do begin            sleep(100);            inc(i);          if i mod 10 = 0 then                WriteLog('I''m still here doing shutdown stuff ' + inttostr(i div 10));        end;    end;end; begin    ExitNow := False;    MesgTime := 0;    writelog('App started ', true);    FpSignal(SigTERM, @HandleSig);          // FpSignal lies, sets errorno but works as expected.    FpSignal(SigHUP,  @HandleSig);    while not ExitNow do begin              // just sit here and try to look busy        inc(MesgTime);        if MesgTime mod 500 = 0 then            WriteLog('I am still here');        sleep(10);    end;    DoExitStuff();  // here because we are exiting gracefully.    writelog('OK, we are done here.');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 ---------------------------------------------------------------------- Thaddy · 2025-09-11 11:35 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. ---------------------------------------------------------------------- dbannon · 2025-09-11 12:07 Quote from: Thaddy on September 11, 2025, 01:35:02 pmDid you know about Application.OnQueryEndSession? Nope, never heard of it !  I will have a good read ! Thanks Davo ---------------------------------------------------------------------- dbannon · 2025-09-11 12:57 Quote from: Thaddy on September 11, 2025, 01:35:02 pmThere 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 ---------------------------------------------------------------------- Thaddy · 2025-09-11 14:19 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i unit query; {$mode objfpc}{$H+} interface uses  Classes, SysUtils, Forms, Controls, Graphics, Dialogs; type   { TForm1 }   TForm1 = class(TForm)    procedure FormCreate(Sender: TObject);  private   public     procedure QueryEndSession(var cancel:Boolean);  end; var  Form1: TForm1; implementation {$R *.lfm} { TForm1 } procedure TForm1.FormCreate(Sender: TObject);var  f:text;  s:string;begin  Application.OnQueryEndSession := @QueryEndSession;  if FileExists('laststart.txt') then  begin    System.assign(f,'laststart.txt');    reset(f);    readln(f,s);    form1.caption := s;    System.Close(f);  end;end; procedure TForm1.QueryEndSession(var cancel: Boolean);var  f:text;begin // here you make sure all clean-ups are done. // threads, sockets, you name it. // better not use GUI elements here...these block.  cancel  := true;  system.Assign(f,'laststart.txt');  rewrite(f);  writeln(f, DateTimeToStr(Now));  system.close(f);  cancel := false;end; 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. ---------------------------------------------------------------------- dbannon · 2025-09-12 04:47 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 ---------------------------------------------------------------------- Thaddy · 2025-09-12 05:34 But mark the difference: https://linuxhandbook.com/sigterm-vs-sigkill/ ---------------------------------------------------------------------- dbannon · 2025-09-12 09:33 Quote from: Thaddy on September 12, 2025, 07:34:03 amBut 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 ---------------------------------------------------------------------- Thaddy · 2025-09-12 09:44 To add  to the confusion the kill command calls SIGTERM unless SIGKILL is explicitly specified ---------------------------------------------------------------------- LeP · 2025-09-12 10:03 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][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i function ShutdownBlockReasonCreate(hWnd: HWND; pwszReason: PWCHAR): BOOL; stdcall; external 'USER32.dll' name 'ShutdownBlockReasonCreate';function ShutdownBlockReasonDestroy(hWnd: HWND): BOOL; stdcall; external 'USER32.dll' name 'ShutdownBlockReasonDestroy'; procedure TForm1.FormDestroy(Sender: TObject);begin  ShutdownBlockReasonDestroy(handle);end; procedure TForm1.QEndSession(var Msg: TMessage);begin  ExitCode := 0;  Msg.Result := 1;  ShutdownBlockReasonCreate(Handle,  'Delayed');  Close;end; ---------------------------------------------------------------------- Thaddy · 2025-09-12 12:01 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; ---------------------------------------------------------------------- LeP · 2025-09-12 12:51 Quote from: Thaddy on September 12, 2025, 02:01:38 pmYou 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. Quote from: Thaddy on September 12, 2025, 02:01:38 pmThe 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. Quote from: Thaddy on September 12, 2025, 02:01:38 pmOne 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 · 2025-09-12 13:15 Quote from: LeP on September 12, 2025, 02:51:09 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. ---------------------------------------------------------------------- Thaddy · 2025-09-12 13:17 Quote from: LeP on September 12, 2025, 02:51:09 pmAdditional 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/? ---------------------------------------------------------------------- LeP · 2025-09-12 14:13 Quote from: Thaddy on September 12, 2025, 03:17:20 pmQuote from: LeP on September 12, 2025, 02:51:09 pmAdditional 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/? https://learn.microsoft.com/en-us/windows/win32/shutdown/wm- queryendsession QuoteWhen an application returns TRUE for this message, it receives the WM_ENDSESSION message, regardless of how the other applications respond to the WM_QUERYENDSESSION message. Each application should return TRUE or FALSE immediately upon receiving this message, and defer any cleanup operations until it receives the WM_ENDSESSION message. ---------------------------------------------------------------------- Thaddy · 2025-09-12 16:09 No, you totally misunderstand that: defer means it should call a clean-up, which by implication also means it can done in-place as I already thought. There's no point leaving the procedure unless for any clean-up call. E.g. calling free on a log is deferring, as are waitfor/free's on threads. Or calling free on classes that is also defer in this context. A better way to express it is like this: do not create something new inside the function, like a showmessage dialog, call the clean-up's and nothing more. ---------------------------------------------------------------------- LeP · 2025-09-12 19:50 Uhmm .... I'm not convince about you explanation. The sequence are clear and the words too. ONE SHOULD WAIT WM_ENDSESSION message before start a cleanup, but sure not in the WM_QUERYENDSESSION elaboration. In the WM_QUERYENDSESSION you must respond as quickly as you can and don't do cleanup. I never wait WM_ENDSESSION, 'cause it take preciuos time for cleanup process (I have sometimes more then 30 seconds). P.S.: if you don't respond in the WM_QUERYENDSESSION within 5 seconds, the OS will kill your application, I don't think you have enough time to make a cleanup inside. ---------------------------------------------------------------------- dbannon · 2025-09-13 03:16 Five seconds sounds like it should be enough for most cleanups but I am quite sure there will be exceptions. Every (well, most) time I close Windows on my Laptop, it wants to do updates that always take a lot longer than 5 seconds, so there must be a means to extent that.   WM_ENDSESSION message might well be that means ? I'm guessing that  WM_QUERYENDSESSION is captured by TApplication.QueryEndSession, looks like TApplication.OnEndSession captures WM_ENDSESSION to do  that extending ? LeP, do you use TApplication.OnEndSession or do you handle the calls your self ? Incidentally LeP, your input is mos certainly relevant, any docs we get together must be as cross platform as possible. Davo EDIT : I just tested the TApplication.QueryEndSession, successfully delaying shutdown for five minutes. You could really annoy your users ... In a command line FPC app, using FPSignal(), its 90 seconds. ---------------------------------------------------------------------- Thaddy · 2025-09-13 07:31 Although I have been using code like this for decades - in professional software with 100th of thousands of users - I have never run into a flaw. So I think the addition is useful, but still think the way I interpret it is enough explanation. (btw that code was written in Delphi, not Freepascal, although Freepascal compiles it nowadays) I might add that a user interface is secondary to running a program. Delphi and Lazarus programmers often forget that: It is the logic that performs, the GUI is ballast. ---------------------------------------------------------------------- LeP · 2025-09-13 09:12 Quote from: dbannon on September 13, 2025, 05:16:34 amLeP, do you use TApplication.OnEndSession or do you handle the calls your self ? This is the general code that I use since XP. Of course is on Windows. I do 99% Windows industrial applications. The GUI Interface is always present and it does only essential things like show something and interact with the user. Code: Pascal  [Select][+][-]window.onload = function(){var x1 = document.getElementById("main_content_section"); if (x1) { var x = document.getElementsByClassName("geshi");for (var i = 0; i type  TFMain = class(TForm)     ......     protected        procedure QEndSession(var Msg: TMessage); message WM_QUERYENDSESSION;   end; implementation     function ShutdownBlockReasonCreate(hWnd: HWND; pwszReason: PWCHAR): BOOL; stdcall; external 'USER32.dll' name 'ShutdownBlockReasonCreate';    function ShutdownBlockReasonDestroy(hWnd: HWND): BOOL; stdcall; external 'USER32.dll' name 'ShutdownBlockReasonDestroy';         procedure TFMain.FormDestroy(Sender: TObject);    begin      //In this place is where all cleanup is done (generally speaking of course)      ...............      ...............      ShutdownBlockReasonDestroy(handle);    end;         procedure TFMain.QEndSession(var Msg: TMessage);  //WM_QUERYENDSESSION    begin      ExitCode := 0;      Msg.Result := 1;      ShutdownBlockReasonCreate(Handle,  'Delayed'); //THIS TEXT IS SHOW ON THE WINDOWS SHUTDOWN SCREEN IF THE APPLICATION SLOW DOWN THE PROCESS.      Close;    end;  Quote from: Thaddy on September 13, 2025, 09:31:54 am(btw that code was written in Delphi, not Freepascal, although Freepascal compiles it nowadays) Yes, it was done in Delphi and it is used in Delphi. Quote from: Thaddy on September 13, 2025, 09:31:54 amI might add that a user interface is secondary to running a program. Delphi and Lazarus programmers often forget that: It is the logic that performs, the GUI is ballast. I'm in line with you, except that same applications (like mine)  needs GUI, so why not using some of their facilities ? In my applications I use lot of external hardware and lot of TThreads, so I don't forget what the logic should do. ---------------------------------------------------------------------- jamie · 2025-09-13 17:10 For Windows users https://learn.microsoft.com/en-us/windows/win32/power/wm- powerbroadcast However, you may need to add some additional work to your form or Application to capture it, like hook the message loop.  For that, I do have a helper unit that will easily assist you in capturing that message.  For others that are not on windows, I don't know. I suppose a file monitor in a thread watching a log file could help trigger an alert. Jamie ---------------------------------------------------------------------- dbannon · 2025-09-15 10:48 OK, here is my first cut at a Wiki page about the issues discussed in this thread. https://wiki.freepascal.org/Shutdown_and_Powerdown Much thanks to the contributors ! Please check out and flag any howlers you find. Jamie, the things built into TApplication do seem to handle a Powerdown quite well,including Windows. What I have not addressed, and might well leave to people more familiar with Windows than me (and thats almost anyone) is when someone uses, eg, the Task Manager to kill an app.  In linux thats easy, just direct the 'signal' to a method that does what is needed. In Windows that is either WM_QUIT or WM_CLOSE but how to use them ? LeP - I found no evidence of the 5 second limit you mention. But I did note that Windows gives you a dialog offering to kill a slow app after about (?) five seconds. Davo edit : typo, my keyboard is dying ! ---------------------------------------------------------------------- LeP · 2025-09-15 12:47 Quote from: dbannon on September 15, 2025, 12:48:23 pmLeP - I found no evidence of the 5 second limit you mention. I don't find the old reference about that (but I take a real applicaton locked and I press the power off button of a PC and this is the time that Windows wait for). I remember it was related to shutdown procedure that Windows use at that now I know was changed over the time. I have some win register keys where those times are exposed (there are other for services too), and they came from old defaults:  - HKCU\Control Panel\Desktop\    AutoEndtask    WaittoKillAppTimeout    HungAppTimeout I tried to delete those and the effects on shutdown are the same. On the new systems these keys are not exposed, you must insert manually if you want to alter the times. Every users can change the own times, depending on the applications. By the way, these values were the default in the old times, and not enough for my applications. But with the API proposed Windows acts like I want. I don't know if Windows, since those keys are not exposed anymore, acts in dinamic way for shutdown. I will try with different machines and different configuration and loads to view how the shutdown acts. But, since now what I have done is still working so I don't think to change it. Quote from: dbannon on September 15, 2025, 12:48:23 pmLeP - But I did note that Windows gives you a dialog offering to kill a slow app after about (?) five seconds. Yes, the screen propose the name of all applications that slow down the process and also the string that you use in the API I show in the posts. Quote from: dbannon on September 15, 2025, 12:48:23 pmIn Windows that is either WM_QUIT or WM_CLOSE but how to use them ? WM_CLOSE message is sent to a FORM (or same controls) that should be closed. You can choose to really destroy or not the Form and all childs. WM_QUIT is send to the Application, I don't know if you can intercept it normally or you must act in other way (I never catch WM_QUIT message). There are more other messages the are sent to an application during the close process (like WM_DESTROY, WM_NCDESTROY, etc ..), but those no needs to catch too.