www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - scope(exit) and Ctrl-C

reply Wanderer <d-wanderer hitbts.com> writes:
I wonder why `scope(exit)` code is not executed when the program 
is terminated with Ctrl-C.

For example:

```
import std.stdio;
import core.thread;

void main()
{
     scope (exit)
     {
         writeln("Cleanup");
     }
     writeln("Waiting...");
     Thread.sleep(10.seconds);
     writeln("Done waiting...");
}
```

If I wait 10 seconds, I get "Cleanup" output.
But if I use Ctrl-C to terminate the program before 10 seconds 
elapse, there's no "Cleanup" output.

Is it intentional?
Is there any method to cleanup on Ctrl-C?
Dec 01
next sibling parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 12/01/2017 04:41 PM, Wanderer wrote:
 I wonder why `scope(exit)` code is not executed when the program is 
 terminated with Ctrl-C.
 
 For example:
 
 ```
 import std.stdio;
 import core.thread;
 
 void main()
 {
      scope (exit)
      {
          writeln("Cleanup");
      }
      writeln("Waiting...");
      Thread.sleep(10.seconds);
      writeln("Done waiting...");
 }
 ```
 
 If I wait 10 seconds, I get "Cleanup" output.
 But if I use Ctrl-C to terminate the program before 10 seconds elapse, 
 there's no "Cleanup" output.
 
 Is it intentional?
 Is there any method to cleanup on Ctrl-C?
Combined your code with this solution: http://forum.dlang.org/post/isawmurvxjyldcwddsfj forum.dlang.org import core.thread; import std.stdio; import core.sys.posix.signal; // Note: This one is thread-local; make shared or __gshared if needed bool doQuit; extern(C) void handler(int num) nothrow nogc system { printf("Caught signal %d\n",num); doQuit = true; } void main(string[] args) { signal(SIGINT, &handler); scope (exit) { writeln("Cleanup"); } writeln("Waiting..."); foreach (_; 0 .. 100) { Thread.sleep(100.msecs); if (doQuit) { break; } } writeln("Done waiting..."); } Ali
Dec 01
prev sibling next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 2 December 2017 at 00:41:19 UTC, Wanderer wrote:
 I wonder why `scope(exit)` code is not executed when the 
 program is terminated with Ctrl-C.
It depends on what your operating system is. On win32, I believe it does run. On Linux (and I think Mac) you need to set a signal handler to catch the ctrl c. But this is intentional - there is no generic, reliable, cross-platform way of handling it natively. So you need to know the system and code it yourself. Not super hard but does take a bit of effort in your code.
Dec 01
next sibling parent Wanderer <d-wanderer hitbts.com> writes:
On Saturday, 2 December 2017 at 01:26:14 UTC, Adam D. Ruppe wrote:
 On Saturday, 2 December 2017 at 00:41:19 UTC, Wanderer wrote:
 I wonder why `scope(exit)` code is not executed when the 
 program is terminated with Ctrl-C.
It depends on what your operating system is. On win32, I believe it does run. On Linux (and I think Mac) you need to set a signal handler to catch the ctrl c. But this is intentional - there is no generic, reliable, cross-platform way of handling it natively. So you need to know the system and code it yourself. Not super hard but does take a bit of effort in your code.
On Windows 10 it does not run. Thanks for the info, I dig around and truly there's no "general" way to do this.
Dec 01
prev sibling parent Jacob Carlborg <doob me.com> writes:
On 2017-12-02 02:26, Adam D. Ruppe wrote:

 But this is intentional - there is no generic, reliable, cross-platform 
 way of handling it natively. So you need to know the system and code it 
 yourself. Not super hard but does take a bit of effort in your code.
Since the "scope" block is not executed, does that also mean that the runtime is not shutdown? If that's the case, does it make sense to have a generic signal handler in druntime that aborts the application and shuts down the druntime in a graceful way? -- /Jacob Carlborg
Dec 02
prev sibling next sibling parent kdevel <kdevel vogtner.de> writes:
On Saturday, 2 December 2017 at 00:41:19 UTC, Wanderer wrote:
 I wonder why `scope(exit)` code is not executed when the 
 program is terminated with Ctrl-C.
Which OS?
 For example:

 ```
 import std.stdio;
 import core.thread;

 void main()
 {
     scope (exit)
     {
         writeln("Cleanup");
     }
     writeln("Waiting...");
     Thread.sleep(10.seconds);
     writeln("Done waiting...");
 }
 ```

 If I wait 10 seconds, I get "Cleanup" output.
 But if I use Ctrl-C to terminate the program before 10 seconds 
 elapse, there's no "Cleanup" output.

 Is it intentional?
It's normal. Ctrl-C (under UNIX, Linux) sends SIGINT to the process. There are two options: 1. Handle this interrupt in your programm (But then: What about Ctrl-\ and SIQUIT)? 2. Prevent the generation of SIGINT: Put the terminal into raw mode [1]
 Is there any method to cleanup on Ctrl-C?
Yes. Take option 1. or option 2. [1] https://linux.die.net/man/3/cbreak "The raw and noraw routines place the terminal into or out of raw mode. Raw mode is similar to cbreak mode, in that characters typed are immediately passed through to the user program. The differences are that in raw mode, the interrupt, quit, suspend, and flow control characters are all passed through uninterpreted, instead of generating a signal. The behavior of the BREAK key depends on other bits in the tty driver that are not set by curses."
Dec 01
prev sibling parent reply codephantom <me noyb.com> writes:
On Saturday, 2 December 2017 at 00:41:19 UTC, Wanderer wrote:
 Is there any method to cleanup on Ctrl-C?
// ---------------------------------- import std.stdio; import core.thread; extern(C) void signal(int sig, void function(int)); extern(C) void exit(int exit_val); extern(C) void handle(int sig) { writeln("Control-C was pressed..aborting program....goodbye..."); // do more stuff? exit(-1); } void main() { enum SIGINT = 2; signal(SIGINT,&handle); scope (exit){ writeln("Cleanup"); } writeln("Waiting..."); Thread.sleep(10.seconds); writeln("Done waiting..."); } // ---------------------------------
Dec 01
parent reply Wanderer <d-wanderer hitbts.com> writes:
On Saturday, 2 December 2017 at 03:52:19 UTC, codephantom wrote:
 On Saturday, 2 December 2017 at 00:41:19 UTC, Wanderer wrote:
 Is there any method to cleanup on Ctrl-C?
// ---------------------------------- import std.stdio; import core.thread; extern(C) void signal(int sig, void function(int)); extern(C) void exit(int exit_val); extern(C) void handle(int sig) { writeln("Control-C was pressed..aborting program....goodbye..."); // do more stuff? exit(-1); } void main() { enum SIGINT = 2; signal(SIGINT,&handle); scope (exit){ writeln("Cleanup"); } writeln("Waiting..."); Thread.sleep(10.seconds); writeln("Done waiting..."); } // ---------------------------------
Thanks! This works. But it seems a little bit suspicions that D's type for handler function has `nothrow` ` nogc` and ` system`. I wonder why is that?
Dec 01
next sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Saturday, 2 December 2017 at 04:28:57 UTC, Wanderer wrote:
 Thanks! This works. But it seems a little bit suspicions that 
 D's type for handler function has `nothrow` ` nogc` and 
 ` system`. I wonder why is that?
Signal handlers are ridiculously limited in what they are allowed to do. Really, you should just set a global variable from the handler and check it on the outside.
Dec 01
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Dec 02, 2017 at 04:38:29AM +0000, Adam D. Ruppe via Digitalmars-d-learn
wrote:
 On Saturday, 2 December 2017 at 04:28:57 UTC, Wanderer wrote:
 Thanks! This works. But it seems a little bit suspicions that D's
 type for handler function has `nothrow` ` nogc` and ` system`. I
 wonder why is that?
Signal handlers are ridiculously limited in what they are allowed to do. Really, you should just set a global variable from the handler and check it on the outside.
Signal handlers can potentially be invoked while inside a non-reentrant libc or OS function, so trying to do anything that (indirectly or otherwise) calls that function will cause havoc to your program. Also, while the signal handler is running, some (all?) further signals may be blocked, meaning that your program might miss an important signal if your sig handler takes too long to run. Furthermore, the signal may have happened in the middle of your own code, so race conditions may apply (e.g. if you're modifying global data in both). So yeah, in general the signal handler should do as little as possible, usually set a flag or something to indicate that a signal has been received, and let the main program do the actual work of responding to the signal, outside of signal handler context. A neat trick I learned years ago is the so-called self-pipe trick, which is especially useful when your main program has a select/epoll loop. Basically, you use the Posix pipe() function to create a read/write pipe, and add the read end to your select/epoll fd set. Then in the signal handler you write to the write end of the pipe (e.g., write the signal number) and return. (The write() function is one of the few OS functions that's safe to call inside a signal handler.) Now you can do the "real" signal processing in your select/epoll loop when the read end of the pipe indicates that it's ready for reads. This lets you avoid the above problems with restrictions in signal handler context, and also integrates nicely into your event loop, so that you can be sure that you're done with whatever it is your other event loops are doing when you actually get around to processing the signal. T -- There's light at the end of the tunnel. It's the oncoming train.
Dec 01
parent reply Patrick Schluter <Patrick.Schluter bbox.fr> writes:
On Saturday, 2 December 2017 at 04:49:26 UTC, H. S. Teoh wrote:
 On Sat, Dec 02, 2017 at 04:38:29AM +0000, Adam D. Ruppe via 
 Digitalmars-d-learn wrote:
 [...]
Signal handlers can potentially be invoked while inside a non-reentrant libc or OS function, so trying to do anything that (indirectly or otherwise) calls that function will cause havoc to your program. Also, while the signal handler is running, some (all?) further signals may be blocked, meaning that your program might miss an important signal if your sig handler takes too long to run. Furthermore, the signal may have happened in the middle of your own code, so race conditions may apply (e.g. if you're modifying global data in both). [...]
On Linux you can use signalfd() for that, but nice trick if you want Posix portability.
Dec 02
parent reply "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Sat, Dec 02, 2017 at 11:32:17AM +0000, Patrick Schluter via
Digitalmars-d-learn wrote:
 On Saturday, 2 December 2017 at 04:49:26 UTC, H. S. Teoh wrote:
 On Sat, Dec 02, 2017 at 04:38:29AM +0000, Adam D. Ruppe via
 Digitalmars-d-learn wrote:
 [...]
Signal handlers can potentially be invoked while inside a non-reentrant libc or OS function, so trying to do anything that (indirectly or otherwise) calls that function will cause havoc to your program. Also, while the signal handler is running, some (all?) further signals may be blocked, meaning that your program might miss an important signal if your sig handler takes too long to run. Furthermore, the signal may have happened in the middle of your own code, so race conditions may apply (e.g. if you're modifying global data in both). [...]
On Linux you can use signalfd() for that, but nice trick if you want Posix portability.
Ha! I've been using Linux for decades now and this is the first time I'm aware of this function. Should simplify my code when I'm not planning to be Posix-portable. Thanks! T -- Heads I win, tails you lose.
Dec 02
parent Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 3 December 2017 at 02:56:38 UTC, H. S. Teoh wrote:
 Ha! I've been using Linux for decades now and this is the first 
 time I'm aware of this function.  Should simplify my code when 
 I'm not planning to be Posix-portable. Thanks!
In the same vein, make sure you read about timerfd and eventfd too. They rok.
Dec 02
prev sibling parent codephantom <me noyb.com> writes:
On Saturday, 2 December 2017 at 04:28:57 UTC, Wanderer wrote:
 Thanks! This works. But it seems a little bit suspicions that 
 D's type for handler function has `nothrow` ` nogc` and 
 ` system`. I wonder why is that?
During execution of that handler, it make sense to prohibit the allocation of any GC memory, and prohibit the throwing of any exceptions. I presume that's why they're there (??) system is default (btw, change -1 to 1 in the exit function, i.e. -1 is not valid, 0..255 is valid.. but some of those you should avoid using).
Dec 01