12

How to capture different signals such as SIGINT and SIGTERM in Swift correctly? For example, when people stop my script by pressing Control-C, I want to do some cleanup before terminating it.

Papershine
  • 4,995
  • 2
  • 24
  • 48

2 Answers2

22

Dispatch Sources can be used to monitor UNIX signals.

Here is a simple example, a Swift 3 translation of the C code in the "Monitoring Signals" section from the Concurrency Programming Guide.

import Dispatch // or Foundation

signal(SIGINT, SIG_IGN) // // Make sure the signal does not terminate the application.

let sigintSrc = DispatchSource.makeSignalSource(signal: SIGINT, queue: .main)
sigintSrc.setEventHandler {
    print("Got SIGINT")
    // ...
    exit(0)
}
sigintSrc.resume()

Note that this requires an active GCD event loop, e.g. with

dispatchMain()

in a command-line program.

Martin R
  • 529,903
  • 94
  • 1,240
  • 1,382
  • 1
    Remember to `import Dispatch`! – Papershine Aug 16 '17 at 13:04
  • @paper1111: You are right. It worked for me because my test code already imported Foundation. – Martin R Aug 16 '17 at 13:08
  • Didn't work for me, the eventHandler never gets called. Does the whole programm need to be run in `DispatchQueue.main`? – YourMJK Oct 28 '18 at 11:51
  • @M.J.K: Yes. As I said, it requires an active GCD event loop. – Martin R Oct 28 '18 at 12:29
  • Sorry if I'm missing something basic, but why does this work when all of the code is together in the main function, but if I try to register the signal handler in a class init, and run dispatchMain in the main function, the program blocks forever on sigint? – A Tyshka Aug 08 '20 at 03:44
  • @ATyshka I had a similar issue where the signal handler wasn't executing. As it turns out I forgot to also copy the `signintSrc.resume()` command which from the docs, "Resumes the invocation of block objects on a dispatch object". So without this, the block was never going to be executed. Maybe you also forgot to add this where you moved the handler definition? – JonnyB Oct 30 '20 at 20:40
-1

I use a simpler approach which is enough for my needs.

There is a limitation though. You can only access global-scoped things from the signalCallback and please check the @cobbal comment below for other limitations.

Just paste those lines before any code is executed in the main.swift:

let signalCallback: sig_t = { signal in
    NSLog("Got signal: \(signal)")
    exit(signal)
}

signal(SIGINT, signalCallback)
Anton Plebanovich
  • 1,296
  • 17
  • 17
  • 3
    Only a limited set of C functions are considered safe to use inside of a signal handler. Since the swift runtime may call all sorts of C functions under the hood, it's unsafe to use any swift at all in a signal handler. From https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/sigaction.2.html :"That is to say, the behaviour of such functions when called from a signal handler is undefined. In general though, signal handlers should do little more than set a flag; most other actions are not safe." – cobbal Feb 22 '23 at 19:49