{"action":"created","issue":{"url":"https://api.github.com/repos/carllerche/mio/issues/16","labels_url":"https://api.github.com/repos/carllerche/mio/issues/16/labels{/name}","comments_url":"https://api.github.com/repos/carllerche/mio/issues/16/comments","events_url":"https://api.github.com/repos/carllerche/mio/issues/16/events","html_url":"https://github.com/carllerche/mio/issues/16","id":43425166,"number":16,"title":"Signal Handling","user":{"login":"wycats","id":4,"avatar_url":"https://avatars.githubusercontent.com/u/4?v=3","gravatar_id":"","url":"https://api.github.com/users/wycats","html_url":"https://github.com/wycats","followers_url":"https://api.github.com/users/wycats/followers","following_url":"https://api.github.com/users/wycats/following{/other_user}","gists_url":"https://api.github.com/users/wycats/gists{/gist_id}","starred_url":"https://api.github.com/users/wycats/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/wycats/subscriptions","organizations_url":"https://api.github.com/users/wycats/orgs","repos_url":"https://api.github.com/users/wycats/repos","events_url":"https://api.github.com/users/wycats/events{/privacy}","received_events_url":"https://api.github.com/users/wycats/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/carllerche/mio/milestones/1","html_url":"https://github.com/carllerche/mio/milestones/0.3.0%20-%20Announce","labels_url":"https://api.github.com/repos/carllerche/mio/milestones/1/labels","id":797769,"number":1,"title":"0.3.0 - Announce","description":"MIO is far enough to start being used by other people","creator":{"login":"carllerche","id":6180,"avatar_url":"https://avatars.githubusercontent.com/u/6180?v=3","gravatar_id":"","url":"https://api.github.com/users/carllerche","html_url":"https://github.com/carllerche","followers_url":"https://api.github.com/users/carllerche/followers","following_url":"https://api.github.com/users/carllerche/following{/other_user}","gists_url":"https://api.github.com/users/carllerche/gists{/gist_id}","starred_url":"https://api.github.com/users/carllerche/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/carllerche/subscriptions","organizations_url":"https://api.github.com/users/carllerche/orgs","repos_url":"https://api.github.com/users/carllerche/repos","events_url":"https://api.github.com/users/carllerche/events{/privacy}","received_events_url":"https://api.github.com/users/carllerche/received_events","type":"User","site_admin":false},"open_issues":2,"closed_issues":9,"state":"open","created_at":"2014-09-22T06:05:22Z","updated_at":"2014-12-13T05:13:06Z","due_on":null,"closed_at":null},"comments":10,"created_at":"2014-09-22T07:17:20Z","updated_at":"2015-05-14T20:21:51Z","closed_at":null,"body":"Signals, how do they work?\r\n\r\nThe goal of this proposal is to make it possible to write programs using mio that can successfully handle Unix signals.\r\n\r\nUnfortunately, signal handling is somewhat tricky, made even more complex by the way Unix delivers signals to multi-threaded programs.\r\n\r\nThe basics:\r\n\r\n* By default, each signal has one of these effects:\r\n  * Ignored\r\n  * Terminate\r\n  * Terminate and dump core\r\n* It is possible for a program to register a handler for most signals\r\n  (`SIGKILL` and `SIGSTOP` cannot be caught).\r\n* Individual threads can choose to mask signals\r\n* There can only be one handler function for all threads in the entire \r\n  program that are handling the signal.\r\n* For most signals, if a signal arrives and more than one thread has the \r\n  signal unmasked, the operating system will deliver it to an arbitrary \r\n  thread.\r\n* For signals that represent hardware failure (`SIGSEGV`, `SIGFPE`), the \r\n  signal is delivered to the thread that generated the failure.\r\n* The `sigwaitinfo` function allows a thread to synchronously wait for a set \r\n  of  signals. It can atomically unmask a set of signals and wait, which \r\n  avoids  race conditions.\r\n* In Linux, `signalfd` creates a file descriptor that can be used to \r\n  synchronously (and atomically) wait on signals, in a way that can work with\r\n  the multiplexing APIs (`epoll_wait`).\r\n* The \"self-pipe trick\" can be used with `sigwaitinfo` to emulate `signalfd`,\r\n  at the cost of an additional thread that is responsible for waiting on\r\n  signals.\r\n\r\n**At a high level, the goal for `mio` is to allow a consumer of a reactor to register interest in signals, to be delivered to a mio handler.**\r\n\r\nThis means that programs that want to use this facility will ignore signals on all threads, and **we** will use `sigwaitinfo` or `signalfd` to allow the reactor to register interest in signals. This also means that only one reactor can be interested in signals. Otherwise, an arbitrary interested reactor would receive the signals.\r\n\r\nIf a program uses this facility without successfully ignoring signals, signals may be delivered to random user threads instead of the reactor.\r\n\r\n## Initialization\r\n\r\nTo make it easy for users to successfully ignore signals across their entire program, a new function, `mio::initialize`, is added. Programs should run this function at the beginning of their `main` function, to ensure that any subsequent threads ignore signals, ensuring that the mio reactor gets notified of signals.\r\n\r\n## Handler API\r\n\r\n`mio::Handler` gets a new callback:\r\n\r\n```rs\r\npub trait Handler<T: Token, M: Send> {\r\n    // ...\r\n    fn signal(&mut self, reactor: &mut Reactor<T, M>, info: Siginfo);\r\n}\r\n```\r\n\r\nThe information provided to the handler will be the subset of `siginfo_t` that is reliably supported on Linux and Darwin. It will be organized by signal type; in particular fields related to `SIGCHLD` will be grouped together.\r\n\r\n## Control\r\n\r\nIn order to ensure that signal notifications are sent to the reactor loop, we need to:\r\n\r\n* At minimum, mask all signals for the reactor thread, so that signals are \r\n  only delivered when waiting.\r\n* Ideally, mask all signals for all threads, so that signals do not get \r\n  delivered to other threads.\r\n\r\nIt is in theory possible to use this facility without control over signal masking, but that will mean that signals can be missed if they get dispatched to another thread. For programs that want to handle signals, this is very unlikely to be desirable.\r\n\r\n## `EINTR`\r\n\r\nWhen a thread recieves a signal during a blocking system call, the system call may return with an `EINTR` error.\r\n\r\nTypically, this means that system calls must guard themselves against this possibility and attempt to retry the call in the case of `EINTR`. There is even a (not fully reliable) `sigaction` flag `SA_RESTART` that can be used to help with this problem.\r\n\r\nFor mio internals, this problem should not exist:\r\n\r\n* We require that the reactor thread mask all signals, and deliver signals \r\n  using the `signal` handler method, not `sigaction`.\r\n* We use atomic functionality (`signalfd` or `sigwaitinfo`) to unmask the \r\n  signals only at the same time as we know we are waiting for a signal.\r\n* As a result, all other blocking system calls will never be interrupted by a \r\n  signal.\r\n\r\nFor programs that want to sanely handle signals with mio, this problem also should not exist:\r\n\r\n* Programs that want to handle signals through mio callbacks will need to mask \r\n  signals process-wide.\r\n* As above, we use atomic functionality to unmask signals only at the same \r\n  time that we are waiting for a signal.\r\n* If signals are masked process-wide, this means that signals will always be \r\n  queued and delivered to the expected system call, and never interrupt other\r\n  system calls.\r\n\r\n> It is true that this strategy requires some control over the process, or at > least the thread running the reactor. However, if a user of mio wants to be\r\n> notified of signals, they will have to ensure that mio, and only mio, can\r\n> receive signals. Otherwise, signals may be silently dropped.\r\n\r\n> Also, if people have a use-case for using mio in a thread that cannot have\r\n> signals masked, we can revisit handling internal-to-mio `EINTR`s by\r\n> retrying. At the moment, it seems very strange to **need** to allow the same \r\n> thread that runs the mio reactor to register a `sigaction` and receive the\r\n> signal through a facility other than mio, but I may be missing something\r\n> obvious!\r\n\r\n## Implementation\r\n\r\n### Linux (epoll + `signalfd`)\r\n\r\nOn Linux, we simply use `signalfd` to accept all signals, after having masked all signals on the thread.\r\n\r\nWe then register `signalfd` with `epoll`. When `epoll_wait` returns, if the FD was readable, we consume signals via `read`, get the associated `signalfd_siginfo` stuctures and call the handler.\r\n\r\n### Other (Self-Pipe + Emulated `signalfd`)\r\n\r\nIn the interest of simplicity, on platforms without `signalfd`, we will emulate it using a separate thread and self-pipe.\r\n\r\n* Create a thread that is dedicated to waiting for signals.\r\n* Use `sigwaitinfo` in a loop to listen for signals\r\n* When signals arrive, write their `siginfo_t` structures into the pipe\r\n* Use the receiving end of the pipe with the system's multiplexing facility \r\n  (e.g. kqueue)\r\n\r\n> It seems possible to implement the functionality without a separate thread\r\n> using kqueue. If that proves important, we can investigate more\r\n> platform-specific options."},"comment":{"url":"https://api.github.com/repos/carllerche/mio/issues/comments/102156238","html_url":"https://github.com/carllerche/mio/issues/16#issuecomment-102156238","issue_url":"https://api.github.com/repos/carllerche/mio/issues/16","id":102156238,"user":{"login":"geofft","id":74644,"avatar_url":"https://avatars.githubusercontent.com/u/74644?v=3","gravatar_id":"","url":"https://api.github.com/users/geofft","html_url":"https://github.com/geofft","followers_url":"https://api.github.com/users/geofft/followers","following_url":"https://api.github.com/users/geofft/following{/other_user}","gists_url":"https://api.github.com/users/geofft/gists{/gist_id}","starred_url":"https://api.github.com/users/geofft/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/geofft/subscriptions","organizations_url":"https://api.github.com/users/geofft/orgs","repos_url":"https://api.github.com/users/geofft/repos","events_url":"https://api.github.com/users/geofft/events{/privacy}","received_events_url":"https://api.github.com/users/geofft/received_events","type":"User","site_admin":false},"created_at":"2015-05-14T20:21:51Z","updated_at":"2015-05-14T20:21:51Z","body":"Somewhat unrelatedly, I think I'm concluding that it's strictly worse to use `signalfd` on Linux than a dedicated thread with reentrant, async-safe handlers. The basic trouble is that all the normal (non-real-time) signals are permitted to coalesce, if multiple of them get delivered while they're being blocked. So if you write a program that blocks `SIGCHLD`, opens a `signalfd` for `SIGCHLD`, spawns two children that exit immediately, and then reads from the `signalfd`, you will nondeterministically get either one or two `signalfd_siginfo`s from the `signalfd`, depending on whether both of the processes exited before you received the first one, and the info for the second child will be lost. I've written up a test case (using a hacked-up nix) that demonstrates this. :(\r\n\r\nSee also [GHC ticket #2451](https://ghc.haskell.org/trac/ghc/ticket/2451), comment 19 onwards:\r\n\r\n> Really? My (vague) understanding is that a single SIGCHLD might be generated even though multiple children have exited -- i.e. SIGCHLD will not be queued the way real-time signals are. I see that w/ a test program which blocks SIGCHILD w/ sigprocmask -- 2 children exit, only one signal handler gets invoked.\r\n\r\nIf we want to get one siginfo per `SIGCHLD` (or other normal signal), I think we'll have to register a real signal handler with `SA_NODEFER`, and carefully make sure that it's re-entrant. (Which will involve some hilarious framing issues if we get interrupted while writing to our own self-pipe thread, but what can you do.) Since we need the separate thread on OS X anyway, I'm not terribly sad about requiring it on Linux.\r\n\r\nAlternatively, we could decide that the semantics for mio signal delivery involve siginfos as a best-effort thing, but acknowledge that they may get coalesced, and you may only get one siginfo if multiple signals of the same type were delivered between calls to the reactor."}}