Recent

Author Topic: Who catches the Linux signals?  (Read 1779 times)

jollytall

  • Sr. Member
  • ****
  • Posts: 376
Who catches the Linux signals?
« on: December 09, 2024, 09:33:24 am »
I have a large program and in order to gracefully quit should any signal received I set up a couple of signal catchers, like
Code: Pascal  [Select][+][-]
  1. fpsignal(SIGFPE, SignalHandler(@signal_callback_handler));

This takes care of any unexpected error. However at certain places I have divisions. It would be easy to check the divider before the division not to be zero, but in order to take also care should a very large number get divided by a very small number, I rather put it in a try-except:
Code: Pascal  [Select][+][-]
  1. try
  2.   a := b/c;
  3. except
  4.   a := 0;
  5.   end;

Now the question is should the signalcatcher catch the FPE in this case or the try-except takes care and it does not reach the signal catcher at all?

MarkMLl

  • Hero Member
  • *****
  • Posts: 8090
Re: Who catches the Linux signals?
« Reply #1 on: December 09, 2024, 09:38:25 am »
Keep the signal handler simple, and use it to set a flag which is checked every time a timer etc. runs.

Assume that the OS has absolutely no knowledge of the program's state when it sends a signal, and that the heap etc. might be in an inconsistent state.

MarkMLl
MT+86 & Turbo Pascal v1 on CCP/M-86, multitasking with LAN & graphics in 128Kb.
Logitech, TopSpeed & FTL Modula-2 on bare metal (Z80, '286 protected mode).
Pet hate: people who boast about the size and sophistication of their computer.
GitHub repositories: https://github.com/MarkMLl?tab=repositories

Thaddy

  • Hero Member
  • *****
  • Posts: 16343
  • Censorship about opinions does not belong here.
Re: Who catches the Linux signals?
« Reply #2 on: December 09, 2024, 10:15:00 am »
Code: Pascal  [Select][+][-]
  1. try
  2.   a := b/c;
  3. except
  4.   on E:EdivByZero do
  5.     a := 0
  6.   else raise;
  7. end;
This allows the "expected" exception to be eaten, but the unexpected exceptions are re-raised so you can debug them.
I am not a fan of eating exceptions, but at least done like so it is less harmful.
There is nothing wrong with being blunt. At a minimum it is also honest.

Zvoni

  • Hero Member
  • *****
  • Posts: 2792
Re: Who catches the Linux signals?
« Reply #3 on: December 09, 2024, 10:19:21 am »
Code: Pascal  [Select][+][-]
  1. try
  2.   a := b/c;
  3. except
  4.   on E:EdivByZero do
  5.     a := 0
  6.   else raise;
  7. end;
This allows the "expected" exception to be eaten, but the unexpected exceptions are re-raised so you can debug them.
I am not a fan of eating exceptions, but at least done like so it is less harmful.
TS doesn't have a Problem with DivByZero (he did write, he can check if c<>0).
His Problem is more how to catch, if b is really big, and c is really small, causing a to "overflow"
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

jollytall

  • Sr. Member
  • ****
  • Posts: 376
Re: Who catches the Linux signals?
« Reply #4 on: December 09, 2024, 10:30:51 am »
To be precise the real question is whether a try-except structure catches first the error and (unless re-raised as Thaddy suggests) then it never reaches the signal catcher OR the signalcatcher is somewhere deep in the operating system and even if the error is in a try-except, the signalcatcher catches it first. In this latter case it is also a question what should or should not the signalcatcher do in order that the error reaches or not the except in the try-except structure. I am interested in the actual way it is written.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11984
  • FPC developer.
Re: Who catches the Linux signals?
« Reply #5 on: December 09, 2024, 11:01:47 am »
To be precise the real question is whether a try-except structure catches first the error and (unless re-raised as Thaddy suggests) then it never reaches the signal catcher OR the signalcatcher is somewhere deep in the operating system and even if the error is in a try-except, the signalcatcher catches it first. In this latter case it is also a question what should or should not the signalcatcher do in order that the error reaches or not the except in the try-except structure. I am interested in the actual way it is written.

There are three mechanisms at play.

  • Default, the RTL converts runtime conditions into Runtime errors, leading to an abort
  • If you add unit sysutils, such conditions are converted to exceptions, i.e. sysutils installs a signal handler for it
  • However, the generation of FPU conditions is usually determined by some FPU exception mask, and quite often this is disabled because Linux libraries like e.g. GTK assume FPU exceptions are off. Enabling it triggers exceptions in LCL/GTK code.

In short: if you want to do this in a Linux GUI program, it probably not going to happen. Windows has the same problem btw (3rd party DLLs not handling enabling float exceptions well), but it is less pervasive, and mostly outside of the core system dlls.

jollytall

  • Sr. Member
  • ****
  • Posts: 376
Re: Who catches the Linux signals?
« Reply #6 on: December 09, 2024, 12:16:30 pm »
So, I made a thorough test. The following things were mentioened: GUI or not GUI, Is there a try..except structure or not, SysUtils is added to the uses or not, fpSignal is defined or not. It gave me 16 combinations. I used the following two programs (with commenting out the parts not needed for a test:
Code: Pascal  [Select][+][-]
  1. program projectt;
  2.  
  3. uses
  4.   BaseUnix
  5.   , SysUtils
  6.   ;
  7. procedure signal_callback_handler(signum : integer); cdecl;
  8.   begin
  9.   Writeln('Signal caught ', SigNum);
  10.   end;
  11.  
  12. var
  13.   a, b, c : double;
  14.  
  15. begin
  16. fpsignal(SIGFPE, SignalHandler(@signal_callback_handler));
  17. a := 1;
  18. b := 0;
  19. try
  20.   c := a / b;
  21. except
  22.   Writeln('Except caught');
  23.   end;
  24. Writeln(c);
  25. Readln;
  26. end.
and
Code: Pascal  [Select][+][-]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.     BaseUnix, Classes,
  9.     SysUtils,
  10.     Forms, Controls, Graphics, Dialogs, StdCtrls;
  11.  
  12. type
  13.  
  14.   { TForm1 }
  15.  
  16.   TForm1 = class( TForm)
  17.     Button1 : TButton;
  18.     LabelFinish : TLabel;
  19.     LabelC : TLabel;
  20.     LabelExcept : TLabel;
  21.     LabelSignal : TLabel;
  22.     procedure Button1Click( Sender : TObject);
  23.   end;
  24.  
  25. var
  26.   Form1 : TForm1;
  27.  
  28. implementation
  29.  
  30. {$R *.lfm}
  31.  
  32. procedure signal_callback_handler(signum : integer); cdecl;
  33.   begin
  34.   Form1.LabelSignal.Caption := 'Signal caught';
  35.   end;
  36.  
  37.  
  38. { TForm1 }
  39.  
  40. procedure TForm1. Button1Click( Sender : TObject);
  41.   var
  42.     a, b, c : double;
  43.   begin
  44.   fpsignal(SIGFPE, SignalHandler(@signal_callback_handler));
  45.   a := 1;
  46.   b := 0;
  47.   try
  48.     c := a / b;
  49.   except
  50.     LabelExcept.Caption := 'Except caught';
  51.     end;
  52.   if C = 1 then
  53.     LabelC.Caption := 'LabelC reached';
  54.   LabelFinish.Caption := 'LabelFinish reached';
  55.   end;
  56. end.
  57.  

I got the following results:
Code: [Select]
GUI Try     SysU fps Result
No No No No Runtime error 208
No No No Yes Infinite loop with SigNum 8
No No Yes No Unhandled exception Div0
No No Yes Yes Infinite loop with SigNum 8
No Yes No No Except catches properly
No Yes No Yes Infinite loop with SigNum 8
No Yes Yes No Except catches properly
No Yes Yes Yes Infinite loop with SigNum 8
Yes No No No Finishes, No Signal, No Except, No Error
Yes No No Yes Finishes, No Signal, No Except, No Error
Yes No Yes No Finishes, No Signal, No Except, No Error
Yes No Yes Yes Finishes, No Signal, No Except, No Error
Yes Yes No No Finishes, No Signal, No Except, No Error
Yes Yes No Yes Finishes, No Signal, No Except, No Error
Yes Yes Yes No Finishes, No Signal, No Except, No Error
Yes Yes Yes Yes Finishes, No Signal, No Except, No Error

- So if it is a GUI application, nothing matters. I find it very strange, however as my program is command line, it was not part of the original question. Still, does it mean that in Linux a GUI can never detect anyway a Div0 (or other serious) error? (8 cases covered)
- From the remaining 8 command line versions, if the fpSignal is set then regardless of the other two settings, it always ended up in an infinite loop. Again, I do not understand. At what point does it turn back in code and starts running again (i.e. a loop)? (4 more cases covered)
- If fpSignal is not set, but the try..except is set, then it is captured by the Except. The difference between SysUtils added or not is whether
Code: Pascal  [Select][+][-]
  1. On E:Exception Do
can be used or not. This is as expected. (2 cases covered).
- If not even try..except is switched on then the program crashes. If SysUtil is not added the with a RunTime error 8 (I guess it is the OS Code), while if SysUtils is added then it catches it, coverts it to text and then crashes with unhandled Div0 error. Again this is as expected. (the last 2 cases covered).

So after all it seems that GUI overwrites everything, then it a command line program fpSignal overwrites everything, and SysUtils and try..except is only used otherwise. What bothers me now are the GUI and the infinite loop of fpSignal.

jollytall

  • Sr. Member
  • ****
  • Posts: 376
Re: Who catches the Linux signals?
« Reply #7 on: December 09, 2024, 12:25:55 pm »
Ooops... The real disaster came after. I got a disk full error. See my other post regarding this.

Warfley

  • Hero Member
  • *****
  • Posts: 1849
Re: Who catches the Linux signals?
« Reply #8 on: December 09, 2024, 01:20:47 pm »
When SysUtils is used somehwere in the project, it registers the default error signal handlers to convert signals into exceptions. So in any project of non trivial size SysUtil will probably be used and thereby Exceptions are used

Note Signals are EXTREMELY restrictive, be very careful with them. Without knowing your code, I would already bet that you did things which are not allowed. From the POSIX Standard:
Quote
the behavior is undefined if:
  • The signal handler refers to any object other than errno with static or thread storage duration that is not a lock-free atomic object, and not a non-modifiable object (for example, string literals, objects that were defined with a const-qualified type, and objects in memory that is mapped read-only), other than by assigning a value to an object declared as volatile sig_atomic_t, unless the previous modification (if any) to the object happens before the signal handler is called and the return from the signal handler happens before the next modification (if any) to the object.
  • The signal handler calls any function defined in this standard other than one of the functions listed in 2.4 Signal Concepts.

marcov

  • Administrator
  • Hero Member
  • *
  • Posts: 11984
  • FPC developer.
Re: Who catches the Linux signals?
« Reply #9 on: December 09, 2024, 01:47:13 pm »
- So if it is a GUI application, nothing matters. I find it very strange, however as my program is command line, it was not part of the original question. Still, does it mean that in Linux a GUI can never detect anyway a Div0 (or other serious) error? (8 cases covered)

See it this way, roughly there is a chance that for any random  C library  that you add that it could not be safe to enable FPU exceptions. The more and the larger libraries you use, the bigger the chance. But for GTK it is a known issue, and LCL disables the exceptions on startup.

Overflow/underflow are more likely that division by zero, but there are no guarantees. The problem is in non FPC/Lazarus code.

Thaddy

  • Hero Member
  • *****
  • Posts: 16343
  • Censorship about opinions does not belong here.
Re: Who catches the Linux signals?
« Reply #10 on: December 09, 2024, 04:52:16 pm »
Code: Pascal  [Select][+][-]
  1. program projectt;
  2.  
  3. uses
  4.   cthreads, // <--------
  5.   BaseUnix
  6.   , SysUtils
  7.   ;
There is nothing wrong with being blunt. At a minimum it is also honest.

jollytall

  • Sr. Member
  • ****
  • Posts: 376
Re: Who catches the Linux signals?
« Reply #11 on: December 09, 2024, 05:55:05 pm »
Sorry Thaddy,
What would that help. Just to save time and do not try all the 16 versions again.
Anyway I thought that the command line version is definitely single thread and probably also the GUI. So, why should cthreads change anything?

Thaddy

  • Hero Member
  • *****
  • Posts: 16343
  • Censorship about opinions does not belong here.
Re: Who catches the Linux signals?
« Reply #12 on: December 09, 2024, 07:08:02 pm »
Handling signals on Unixes usually need to take into account threads and that is not a joke but a fact, even for the simplest.
There is nothing wrong with being blunt. At a minimum it is also honest.

PascalDragon

  • Hero Member
  • *****
  • Posts: 5802
  • Compiler Developer
Re: Who catches the Linux signals?
« Reply #13 on: December 09, 2024, 09:11:29 pm »
I have a large program and in order to gracefully quit should any signal received I set up a couple of signal catchers, like
Code: Pascal  [Select][+][-]
  1. fpsignal(SIGFPE, SignalHandler(@signal_callback_handler));

If you capture signals yourself you are essentially circumventing FPC's exception handling, cause it relies on the signal handlers as well. If at all then use fpSigAction which will return the old handler (the one set by the RTL) and you should then call that when you have done your own custom handling.

Handling signals on Unixes usually need to take into account threads and that is not a joke but a fact, even for the simplest.

Incorrect. If no threads are used in the application then no threads need to be taken into account for signals either.

LV

  • Full Member
  • ***
  • Posts: 189
Re: Who catches the Linux signals?
« Reply #14 on: December 09, 2024, 09:47:15 pm »
This takes care of any unexpected error. However at certain places I have divisions. It would be easy to check the divider before the division not to be zero, but in order to take also care should a very large number get divided by a very small number, I rather put it in a try-except

App GTK2 on Linux Deb64.

It is indeed strange that the Try..Except block does not handle division by zero errors. Perhaps using an If..Else statement would be a workaround.

 

TinyPortal © 2005-2018