Haskell's runtime is so complex that I don't think you can write signal handling functions in Haskell. The best you can do is to mark a sigatomic boolean inside the real signal handler and arrange the runtime for check for that boolean outside the signal handler.
Yup: see https://hackage.haskell.org/package/ghc-internal-9.1001.0/do... where it is clear that setting a handler simply writes to an array inside an MVar. And when the signal handler is run, the runtime starts a green thread to run it, which means user Haskell code does not need to worry about signal handler safe functions at all, since from the OS perspective the signal handler has returned. The user handler function simply runs as a new green thread independent of other threads.
But I like the fact that you brought up this idea. Haskell can't do it but in a parallel universe if there were another language with no runtime but with monads, we can actually solve this.
I am not sure but I think rust already allows safe signal handlers? The borrow checker makes you write thread safe code even without any active threading and signals are just emergency threads with some extra limitations... right? I don't understand this too deeply so I could be wrong here.
Rust does allow for safe signal handling, but it's sort of the same way that it allows for safe and correct interrupt handlers for people writing os kernels (signals are basically interrupts, just from kernel->user instead of hardware->kernel). You're basically constrained to no_std and have to be very careful about communications with the rest of the system using lock free mechanisms.
If handling a signal was equivalent to handling concurrency then it wouldn’t be as much of a problem.
IIRC a signal can take over execution of a running thread, so it will completely invalidate any critical sections etc. You cannot access any shared resource easily, cannot allocate memory and a lot more restrictions of this form.
Yes but the signal handling code acts as if it is on a different thread. So it cannot access the critical sections or mess up any existing state on the thread anyways. Sure the other parts need to be managed manually but just that should still go a long way. ...Right?
Not quite, by default the signal handler hijacks an existing thread. It is possible to keep a dedicated thread around that solely waits for signals, but that’s a workaround and you end up needing to also mask all signals from all other threads for correctness. And then there are also synchronous signals, which can’t be handled this way (eg. segfaults)
Imagine a scenario where the original thread state is in a critical section, in the middle of allocating memory (which may need a mutex for non-thread local allocations) etc.
The code within the signal handler can’t guarantee access to any shared resource, because the previous execution of the thread may have been in the middle of the critical section. With normal concurrency, the thread that doesn’t hold the mutex can just suspend itself and wait.
However, because the thread has been hijacked by the signal handler, the original critical section cannot complete until the signal has been handled, and the signal handling cannot yield to the original code because it is not suspendable.
Signal handling is distinct from a different thread because it blocks the execution of the “preempted thread” until the signal handler completes.
As a example, if the preempted code grabs a lock for a resource, then signal handler completion can not depend on grabbing that lock because that lock will never be released until the preempted code runs again and the preempted code can never run again until the signal handler completes.
A correct signal handler can never wait for a resource held by regular code. This precludes coordination or sharing via normal locks or critical sections.
Yup: see https://hackage.haskell.org/package/ghc-internal-9.1001.0/do... where it is clear that setting a handler simply writes to an array inside an MVar. And when the signal handler is run, the runtime starts a green thread to run it, which means user Haskell code does not need to worry about signal handler safe functions at all, since from the OS perspective the signal handler has returned. The user handler function simply runs as a new green thread independent of other threads.
But I like the fact that you brought up this idea. Haskell can't do it but in a parallel universe if there were another language with no runtime but with monads, we can actually solve this.