diff options
| author | ashleyh <gh@ashleyh.eu> | 2014-02-26 22:10:25 +0000 | 
|---|---|---|
| committer | ashleyh <gh@ashleyh.eu> | 2014-02-26 22:10:25 +0000 | 
| commit | 00ba300d3941d5e9d59ffffcb760eb0f9d1079ef (patch) | |
| tree | 2b22d404b040adf2441af3b8369db44b800ac5ab /third-party/libuv/src/unix/signal.c | |
| parent | 14cbd618ec4930878afc6f9073d9ebb9e514fd75 (diff) | |
| parent | 314791dca7b80e167165694222dfc0f70dd77e5c (diff) | |
| download | rneovim-00ba300d3941d5e9d59ffffcb760eb0f9d1079ef.tar.gz rneovim-00ba300d3941d5e9d59ffffcb760eb0f9d1079ef.tar.bz2 rneovim-00ba300d3941d5e9d59ffffcb760eb0f9d1079ef.zip | |
Merge branch 'master' into pr36
Conflicts:
	README.md
Diffstat (limited to 'third-party/libuv/src/unix/signal.c')
| -rw-r--r-- | third-party/libuv/src/unix/signal.c | 465 | 
1 files changed, 465 insertions, 0 deletions
| diff --git a/third-party/libuv/src/unix/signal.c b/third-party/libuv/src/unix/signal.c new file mode 100644 index 0000000000..0b7a405c15 --- /dev/null +++ b/third-party/libuv/src/unix/signal.c @@ -0,0 +1,465 @@ +/* Copyright Joyent, Inc. and other Node contributors. All rights reserved. + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to + * deal in the Software without restriction, including without limitation the + * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or + * sell copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS + * IN THE SOFTWARE. + */ + +#include "uv.h" +#include "internal.h" + +#include <assert.h> +#include <errno.h> +#include <signal.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + + +typedef struct { +  uv_signal_t* handle; +  int signum; +} uv__signal_msg_t; + +RB_HEAD(uv__signal_tree_s, uv_signal_s); + + +static int uv__signal_unlock(void); +static void uv__signal_event(uv_loop_t* loop, uv__io_t* w, unsigned int events); +static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2); +static void uv__signal_stop(uv_signal_t* handle); + + +static pthread_once_t uv__signal_global_init_guard = PTHREAD_ONCE_INIT; +static struct uv__signal_tree_s uv__signal_tree = +    RB_INITIALIZER(uv__signal_tree); +static int uv__signal_lock_pipefd[2]; + + +RB_GENERATE_STATIC(uv__signal_tree_s, +                   uv_signal_s, tree_entry, +                   uv__signal_compare) + + +static void uv__signal_global_init(void) { +  if (uv__make_pipe(uv__signal_lock_pipefd, 0)) +    abort(); + +  if (uv__signal_unlock()) +    abort(); +} + + +void uv__signal_global_once_init(void) { +  pthread_once(&uv__signal_global_init_guard, uv__signal_global_init); +} + + + +static int uv__signal_lock(void) { +  int r; +  char data; + +  do { +    r = read(uv__signal_lock_pipefd[0], &data, sizeof data); +  } while (r < 0 && errno == EINTR); + +  return (r < 0) ? -1 : 0; +} + + +static int uv__signal_unlock(void) { +  int r; +  char data = 42; + +  do { +    r = write(uv__signal_lock_pipefd[1], &data, sizeof data); +  } while (r < 0 && errno == EINTR); + +  return (r < 0) ? -1 : 0; +} + + +static void uv__signal_block_and_lock(sigset_t* saved_sigmask) { +  sigset_t new_mask; + +  if (sigfillset(&new_mask)) +    abort(); + +  if (pthread_sigmask(SIG_SETMASK, &new_mask, saved_sigmask)) +    abort(); + +  if (uv__signal_lock()) +    abort(); +} + + +static void uv__signal_unlock_and_unblock(sigset_t* saved_sigmask) { +  if (uv__signal_unlock()) +    abort(); + +  if (pthread_sigmask(SIG_SETMASK, saved_sigmask, NULL)) +    abort(); +} + + +static uv_signal_t* uv__signal_first_handle(int signum) { +  /* This function must be called with the signal lock held. */ +  uv_signal_t lookup; +  uv_signal_t* handle; + +  lookup.signum = signum; +  lookup.loop = NULL; + +  handle = RB_NFIND(uv__signal_tree_s, &uv__signal_tree, &lookup); + +  if (handle != NULL && handle->signum == signum) +    return handle; + +  return NULL; +} + + +static void uv__signal_handler(int signum) { +  uv__signal_msg_t msg; +  uv_signal_t* handle; +  int saved_errno; + +  saved_errno = errno; +  memset(&msg, 0, sizeof msg); + +  if (uv__signal_lock()) { +    errno = saved_errno; +    return; +  } + +  for (handle = uv__signal_first_handle(signum); +       handle != NULL && handle->signum == signum; +       handle = RB_NEXT(uv__signal_tree_s, &uv__signal_tree, handle)) { +    int r; + +    msg.signum = signum; +    msg.handle = handle; + +    /* write() should be atomic for small data chunks, so the entire message +     * should be written at once. In theory the pipe could become full, in +     * which case the user is out of luck. +     */ +    do { +      r = write(handle->loop->signal_pipefd[1], &msg, sizeof msg); +    } while (r == -1 && errno == EINTR); + +    assert(r == sizeof msg || +           (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK))); + +    if (r != -1) +      handle->caught_signals++; +  } + +  uv__signal_unlock(); +  errno = saved_errno; +} + + +static int uv__signal_register_handler(int signum) { +  /* When this function is called, the signal lock must be held. */ +  struct sigaction sa; + +  /* XXX use a separate signal stack? */ +  memset(&sa, 0, sizeof(sa)); +  if (sigfillset(&sa.sa_mask)) +    abort(); +  sa.sa_handler = uv__signal_handler; + +  /* XXX save old action so we can restore it later on? */ +  if (sigaction(signum, &sa, NULL)) +    return -errno; + +  return 0; +} + + +static void uv__signal_unregister_handler(int signum) { +  /* When this function is called, the signal lock must be held. */ +  struct sigaction sa; + +  memset(&sa, 0, sizeof(sa)); +  sa.sa_handler = SIG_DFL; + +  /* sigaction can only fail with EINVAL or EFAULT; an attempt to deregister a +   * signal implies that it was successfully registered earlier, so EINVAL +   * should never happen. +   */ +  if (sigaction(signum, &sa, NULL)) +    abort(); +} + + +static int uv__signal_loop_once_init(uv_loop_t* loop) { +  int err; + +  /* Return if already initialized. */ +  if (loop->signal_pipefd[0] != -1) +    return 0; + +  err = uv__make_pipe(loop->signal_pipefd, UV__F_NONBLOCK); +  if (err) +    return err; + +  uv__io_init(&loop->signal_io_watcher, +              uv__signal_event, +              loop->signal_pipefd[0]); +  uv__io_start(loop, &loop->signal_io_watcher, UV__POLLIN); + +  return 0; +} + + +void uv__signal_loop_cleanup(uv_loop_t* loop) { +  QUEUE* q; + +  /* Stop all the signal watchers that are still attached to this loop. This +   * ensures that the (shared) signal tree doesn't contain any invalid entries +   * entries, and that signal handlers are removed when appropriate. +   */ +  QUEUE_FOREACH(q, &loop->handle_queue) { +    uv_handle_t* handle = QUEUE_DATA(q, uv_handle_t, handle_queue); + +    if (handle->type == UV_SIGNAL) +      uv__signal_stop((uv_signal_t*) handle); +  } + +  if (loop->signal_pipefd[0] != -1) { +    uv__close(loop->signal_pipefd[0]); +    loop->signal_pipefd[0] = -1; +  } + +  if (loop->signal_pipefd[1] != -1) { +    uv__close(loop->signal_pipefd[1]); +    loop->signal_pipefd[1] = -1; +  } +} + + +int uv_signal_init(uv_loop_t* loop, uv_signal_t* handle) { +  int err; + +  err = uv__signal_loop_once_init(loop); +  if (err) +    return err; + +  uv__handle_init(loop, (uv_handle_t*) handle, UV_SIGNAL); +  handle->signum = 0; +  handle->caught_signals = 0; +  handle->dispatched_signals = 0; + +  return 0; +} + + +void uv__signal_close(uv_signal_t* handle) { + +  uv__signal_stop(handle); + +  /* If there are any caught signals "trapped" in the signal pipe, we can't +   * call the close callback yet. Otherwise, add the handle to the finish_close +   * queue. +   */ +  if (handle->caught_signals == handle->dispatched_signals) { +    uv__make_close_pending((uv_handle_t*) handle); +  } +} + + +int uv_signal_start(uv_signal_t* handle, uv_signal_cb signal_cb, int signum) { +  sigset_t saved_sigmask; +  int err; + +  assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); + +  /* If the user supplies signum == 0, then return an error already. If the +   * signum is otherwise invalid then uv__signal_register will find out +   * eventually. +   */ +  if (signum == 0) +    return -EINVAL; + +  /* Short circuit: if the signal watcher is already watching {signum} don't +   * go through the process of deregistering and registering the handler. +   * Additionally, this avoids pending signals getting lost in the small time +   * time frame that handle->signum == 0. +   */ +  if (signum == handle->signum) { +    handle->signal_cb = signal_cb; +    return 0; +  } + +  /* If the signal handler was already active, stop it first. */ +  if (handle->signum != 0) { +    uv__signal_stop(handle); +  } + +  uv__signal_block_and_lock(&saved_sigmask); + +  /* If at this point there are no active signal watchers for this signum (in +   * any of the loops), it's time to try and register a handler for it here. +   */ +  if (uv__signal_first_handle(signum) == NULL) { +    err = uv__signal_register_handler(signum); +    if (err) { +      /* Registering the signal handler failed. Must be an invalid signal. */ +      uv__signal_unlock_and_unblock(&saved_sigmask); +      return err; +    } +  } + +  handle->signum = signum; +  RB_INSERT(uv__signal_tree_s, &uv__signal_tree, handle); + +  uv__signal_unlock_and_unblock(&saved_sigmask); + +  handle->signal_cb = signal_cb; +  uv__handle_start(handle); + +  return 0; +} + + +static void uv__signal_event(uv_loop_t* loop, +                             uv__io_t* w, +                             unsigned int events) { +  uv__signal_msg_t* msg; +  uv_signal_t* handle; +  char buf[sizeof(uv__signal_msg_t) * 32]; +  size_t bytes, end, i; +  int r; + +  bytes = 0; +  end = 0; + +  do { +    r = read(loop->signal_pipefd[0], buf + bytes, sizeof(buf) - bytes); + +    if (r == -1 && errno == EINTR) +      continue; + +    if (r == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) { +      /* If there are bytes in the buffer already (which really is extremely +       * unlikely if possible at all) we can't exit the function here. We'll +       * spin until more bytes are read instead. +       */ +      if (bytes > 0) +        continue; + +      /* Otherwise, there was nothing there. */ +      return; +    } + +    /* Other errors really should never happen. */ +    if (r == -1) +      abort(); + +    bytes += r; + +    /* `end` is rounded down to a multiple of sizeof(uv__signal_msg_t). */ +    end = (bytes / sizeof(uv__signal_msg_t)) * sizeof(uv__signal_msg_t); + +    for (i = 0; i < end; i += sizeof(uv__signal_msg_t)) { +      msg = (uv__signal_msg_t*) (buf + i); +      handle = msg->handle; + +      if (msg->signum == handle->signum) { +        assert(!(handle->flags & UV_CLOSING)); +        handle->signal_cb(handle, handle->signum); +      } + +      handle->dispatched_signals++; + +      /* If uv_close was called while there were caught signals that were not +       * yet dispatched, the uv__finish_close was deferred. Make close pending +       * now if this has happened. +       */ +      if ((handle->flags & UV_CLOSING) && +          (handle->caught_signals == handle->dispatched_signals)) { +        uv__make_close_pending((uv_handle_t*) handle); +      } +    } + +    bytes -= end; + +    /* If there are any "partial" messages left, move them to the start of the +     * the buffer, and spin. This should not happen. +     */ +    if (bytes) { +      memmove(buf, buf + end, bytes); +      continue; +    } +  } while (end == sizeof buf); +} + + +static int uv__signal_compare(uv_signal_t* w1, uv_signal_t* w2) { +  /* Compare signums first so all watchers with the same signnum end up +   * adjacent. +   */ +  if (w1->signum < w2->signum) return -1; +  if (w1->signum > w2->signum) return 1; + +  /* Sort by loop pointer, so we can easily look up the first item after +   * { .signum = x, .loop = NULL }. +   */ +  if (w1->loop < w2->loop) return -1; +  if (w1->loop > w2->loop) return 1; + +  if (w1 < w2) return -1; +  if (w1 > w2) return 1; + +  return 0; +} + + +int uv_signal_stop(uv_signal_t* handle) { +  assert(!(handle->flags & (UV_CLOSING | UV_CLOSED))); +  uv__signal_stop(handle); +  return 0; +} + + +static void uv__signal_stop(uv_signal_t* handle) { +  uv_signal_t* removed_handle; +  sigset_t saved_sigmask; + +  /* If the watcher wasn't started, this is a no-op. */ +  if (handle->signum == 0) +    return; + +  uv__signal_block_and_lock(&saved_sigmask); + +  removed_handle = RB_REMOVE(uv__signal_tree_s, &uv__signal_tree, handle); +  assert(removed_handle == handle); +  (void) removed_handle; + +  /* Check if there are other active signal watchers observing this signal. If +   * not, unregister the signal handler. +   */ +  if (uv__signal_first_handle(handle->signum) == NULL) +    uv__signal_unregister_handler(handle->signum); + +  uv__signal_unlock_and_unblock(&saved_sigmask); + +  handle->signum = 0; +  uv__handle_stop(handle); +} | 
