Live data from Hacker News

Ctrl-C

kevinlawler.com

81–90 of 94 posts

Re: Ctrl-C

#81
post #36

Earlier quoted context omitted.

It's a loop with few instruction that iterates many times, written in C.

Can’t we have a tight loop in any language? What’s special about C here?

Tight loops in higher-level languages generally always have yield points/opportunities to break provided by the runtime, even if the loop itself doesn't appear to have any.

Re: Ctrl-C

#82
post #72
post #34

Earlier quoted context omitted.

The SIGINT (if the terminal is configured to generate one) goes to the foreground process group, not the current process. See the termios man page, look for INTR. This gets complicated in shell pipelines (oh look, a process group) where one or more of the tools involved are fiddling with the global terminal state, in which case there may be a process group signal, or there might instead be a key for some random progr…

> someone wrote blocksig(1) Link?

https://thrig.github.io/2022/08/06/control+c.html probably gets you close enough

Re: Ctrl-C

#83
post #62
post #56

Earlier quoted context omitted.

The article’s point is basically proven by how many people here don’t even understand he’s talking about this, and not killing the program with Ctrl-C.

Rather the opposite; people have hard time understanding what he is talking about because most applications that people use already handle ctrl-c as author wants, so its not a problem many people encounter often. So its reasonable that people then think its talking about the problem that many are encountering, programs that just swallow ctrl-c without doing anything. This is not helped by author having this bit near…

Well maybe then it only proves people don’t read the articles they comment on :)

Re: Ctrl-C

#84

I feel this. Signal handling in Python code is especially complicated. I'm not even talking about multithreading here (not like you get anything out of it anyway). Python registers POSIX signal handlers, that upon SIGTERM/SIGINT, set a flag. Next time the interpreter loop runs, the flag is checked before the next instruction is being run and the stack is unwinded. When you call out to some C code, that C code may run…

Definitely - it is a total chore to get a threaded Python program to handle Ctrl-C/SIGINT properly.

Single-threaded Python handles it well - as long as you don't register a custom signal handler, Ctrl-C raises a KeyboardInterrupt exception immediately. KeyboardInterrupt is not a subclass of Exception, (it inherits from SystemExit, which inherits from BaseException directly), so any "except Exception:" clauses don't catch it. Which is the intent. This is also a primary reason to never use bare "except:" clauses (it will prevent Ctrl-C from working!).

For multithreaded Python, the easiest thing to do is just mark all your threads as daemon=True, so they die if the main thread exits. When you can't do that, the best bet is some "threading.Event" and custom SIGINT handler that triggers the event. I kind of wish SIGNINT by default would raise the KeyboardInterrupt in all threads, but I'm sure there are good reasons not to.

Re: Ctrl-C

#85

Earlier quoted context omitted.

Can’t we have a tight loop in any language? What’s special about C here?

Tight loops in higher-level languages generally always have yield points/opportunities to break provided by the runtime, even if the loop itself doesn't appear to have any.

Seems to me this says more about the programmer than the language.

Re: Ctrl-C

#86
post #34

This makes no sense. Ctrl-C is just a way to tell your terminal to send a SIGINT signal to the current process. How that process handles the signal is up to it! It's by definition ignorable, as the author points out, but it's not rocket science to handle it in a sane fashion even in a multi-threaded application. Modern languages make this trivial. The author makes it sound like some dark art but in reality you just h…

The SIGINT (if the terminal is configured to generate one) goes to the foreground process group, not the current process. See the termios man page, look for INTR. This gets complicated in shell pipelines (oh look, a process group) where one or more of the tools involved are fiddling with the global terminal state, in which case there may be a process group signal, or there might instead be a key for some random progr…

I know this! I was hoping to hand wave away the complexity of process groups.

And you probably know that the author of curses is the author of rogue!

Re: Ctrl-C

#87
post #55

This makes no sense. Ctrl-C is just a way to tell your terminal to send a SIGINT signal to the current process. How that process handles the signal is up to it! It's by definition ignorable, as the author points out, but it's not rocket science to handle it in a sane fashion even in a multi-threaded application. Modern languages make this trivial. The author makes it sound like some dark art but in reality you just h…

There is an entire world here beyond registering for a signal that comment seems unaware of. Even the simplest of preliminaries: registering for a signal is arguably non-trivial and incorrectly specified in many places since sigaction() supersedes signal(). > it's not rocket science to handle it in a sane fashion even in a multi-threaded application. Modern languages make this trivial. The author makes it sound like…

How about C? Bash? Perl? Java? Go? Rust?

I've done plenty of signal handling in Python and it's extremely straight forward. Like other languages, the runtime takes care of safely propagating information from the signal handler to other execution contexts, which requires being careful in a language like C (it's not hard, but you can't be naive). I wouldn't be surprised if there were bugs in Python, it's a mess generally and I'm not a fan.

Postgres queries run as subprocesses. You can send them any signals you want. Postgres tries very hard to be durable, and it handles signals carefully but often to the dismay of the operator who can't force it to stop without SIGKILL.

> registering for a signal is arguably non-trivial and incorrectly specified in many places since sigaction() supersedes signal().

This isn't a good argument, no one uses signal(2), I'm not aware of that ever being recommended in recent history and even the docs on my system scream "never use this" quite clearly.

Look, if you're not going to read the docs, signal handling will be the least of your concern. Signal behavior is extremely well documented.

Re: Ctrl-C

#88
post #6
post #4

Earlier quoted context omitted.

Which is exactly what the author is saying shouldn't be needed.

Author is talking about looking up PIDs, kill -9 %1 saves you from that.

It may not be %1; if the shell says

    [2]+  Stopped                 units
it's %2. But it's also %+, %%, and just %, which is what the "+" after the [2] means. In my case %1 is evince zhegalkin-sm7433.pdf (Running, not Stopped), which I definitely do not want to kill.

Plenty of people open a new terminal window for every new program they want to run, but I commonly have several "jobs" in the same window, stopped or even running. Less often now that monitors are bigger, but still.

Re: Ctrl-C

#89

Earlier quoted context omitted.

> it's not rocket science to handle it in a sane fashion even in a multi-threaded application It's not, though you need to be careful if you want to exit cleanly -- you can't just exit() or _exit(). You have to get all the threads to exit, and that requires a global volatile sig_atomic_t flag and some way to signal all threads that might be sleeping in event loops or what have you.

A separate thread with sigwait() may be easier. Though I must admit, I've rarely had to do that manually, as I'm usually using a language or framework that gives an easier way to get notified about signals (e.g. Python KeyboardInterrupt or listeners in Boost ASIO or libuv). Aside from saving some boilerplate, those also emulate equivalent signals in Windows. Threads waiting on event loops is exactly what you want on…

I never use sigwait() or sigsuspend() -- I just have signal handlers that write a byte (e.g., the signal number) into a pipe, and maybe they set a global volatile sig_atomic_t variable. DJB calls this a "self-pipe". A self-pipe turns signals into I/O events that select/poll/epoll/event ports/kqueue/io_ring/whatever can handle like any other events.

At exit time I just make sure every thread can get an event. The main thread can pthread_cond_wait() for all the other threads to exit, waiting for the count of them to fall to zero.

Re: Ctrl-C

#90
post #42

Just wonder if author is aware of SIGSTOP/SIGCONT that allows to pause/resume any process gracefully ? Both signals can be caught and handled. Crtl-C (SIGINT), as far as I know, was used to "gracefully terminate" interactive process from day zero of Unix. I cannot find any use in that of what author proposes: suspend execution by sending SIGINT, but then what ? Get to some process built-in debugging shell ? Isn't tha…

SIGSTOP cannot be caught and handled; you're thinking of SIGTSTP.

Non-interactive programs do not need any special handling for SIGINT, and that seems to be what you're talking about. The author was talking about interactive programs like irb, bc, bash, gdb, and python, all of which behave as they desire, returning you to their REPL prompt upon receipt of SIGINT. One example of an interactive program that does not have the desired behavior is GNU Units.

Post reply on HN