11cb0ef41Sopenharmony_ci/* Copyright Joyent, Inc. and other Node contributors. All rights reserved.
21cb0ef41Sopenharmony_ci * Permission is hereby granted, free of charge, to any person obtaining a copy
31cb0ef41Sopenharmony_ci * of this software and associated documentation files (the "Software"), to
41cb0ef41Sopenharmony_ci * deal in the Software without restriction, including without limitation the
51cb0ef41Sopenharmony_ci * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
61cb0ef41Sopenharmony_ci * sell copies of the Software, and to permit persons to whom the Software is
71cb0ef41Sopenharmony_ci * furnished to do so, subject to the following conditions:
81cb0ef41Sopenharmony_ci *
91cb0ef41Sopenharmony_ci * The above copyright notice and this permission notice shall be included in
101cb0ef41Sopenharmony_ci * all copies or substantial portions of the Software.
111cb0ef41Sopenharmony_ci *
121cb0ef41Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
131cb0ef41Sopenharmony_ci * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
141cb0ef41Sopenharmony_ci * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
151cb0ef41Sopenharmony_ci * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
161cb0ef41Sopenharmony_ci * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
171cb0ef41Sopenharmony_ci * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
181cb0ef41Sopenharmony_ci * IN THE SOFTWARE.
191cb0ef41Sopenharmony_ci */
201cb0ef41Sopenharmony_ci
211cb0ef41Sopenharmony_ci/* This file contains both the uv__async internal infrastructure and the
221cb0ef41Sopenharmony_ci * user-facing uv_async_t functions.
231cb0ef41Sopenharmony_ci */
241cb0ef41Sopenharmony_ci
251cb0ef41Sopenharmony_ci#include "uv.h"
261cb0ef41Sopenharmony_ci#include "internal.h"
271cb0ef41Sopenharmony_ci#include "atomic-ops.h"
281cb0ef41Sopenharmony_ci
291cb0ef41Sopenharmony_ci#include <errno.h>
301cb0ef41Sopenharmony_ci#include <stdio.h>  /* snprintf() */
311cb0ef41Sopenharmony_ci#include <assert.h>
321cb0ef41Sopenharmony_ci#include <stdlib.h>
331cb0ef41Sopenharmony_ci#include <string.h>
341cb0ef41Sopenharmony_ci#include <unistd.h>
351cb0ef41Sopenharmony_ci#include <sched.h>  /* sched_yield() */
361cb0ef41Sopenharmony_ci
371cb0ef41Sopenharmony_ci#ifdef __linux__
381cb0ef41Sopenharmony_ci#include <sys/eventfd.h>
391cb0ef41Sopenharmony_ci#endif
401cb0ef41Sopenharmony_ci
411cb0ef41Sopenharmony_cistatic void uv__async_send(uv_loop_t* loop);
421cb0ef41Sopenharmony_cistatic int uv__async_start(uv_loop_t* loop);
431cb0ef41Sopenharmony_ci
441cb0ef41Sopenharmony_ci
451cb0ef41Sopenharmony_ciint uv_async_init(uv_loop_t* loop, uv_async_t* handle, uv_async_cb async_cb) {
461cb0ef41Sopenharmony_ci  int err;
471cb0ef41Sopenharmony_ci
481cb0ef41Sopenharmony_ci  err = uv__async_start(loop);
491cb0ef41Sopenharmony_ci  if (err)
501cb0ef41Sopenharmony_ci    return err;
511cb0ef41Sopenharmony_ci
521cb0ef41Sopenharmony_ci  uv__handle_init(loop, (uv_handle_t*)handle, UV_ASYNC);
531cb0ef41Sopenharmony_ci  handle->async_cb = async_cb;
541cb0ef41Sopenharmony_ci  handle->pending = 0;
551cb0ef41Sopenharmony_ci
561cb0ef41Sopenharmony_ci  QUEUE_INSERT_TAIL(&loop->async_handles, &handle->queue);
571cb0ef41Sopenharmony_ci  uv__handle_start(handle);
581cb0ef41Sopenharmony_ci
591cb0ef41Sopenharmony_ci  return 0;
601cb0ef41Sopenharmony_ci}
611cb0ef41Sopenharmony_ci
621cb0ef41Sopenharmony_ci
631cb0ef41Sopenharmony_ciint uv_async_send(uv_async_t* handle) {
641cb0ef41Sopenharmony_ci  /* Do a cheap read first. */
651cb0ef41Sopenharmony_ci  if (ACCESS_ONCE(int, handle->pending) != 0)
661cb0ef41Sopenharmony_ci    return 0;
671cb0ef41Sopenharmony_ci
681cb0ef41Sopenharmony_ci  /* Tell the other thread we're busy with the handle. */
691cb0ef41Sopenharmony_ci  if (cmpxchgi(&handle->pending, 0, 1) != 0)
701cb0ef41Sopenharmony_ci    return 0;
711cb0ef41Sopenharmony_ci
721cb0ef41Sopenharmony_ci  /* Wake up the other thread's event loop. */
731cb0ef41Sopenharmony_ci  uv__async_send(handle->loop);
741cb0ef41Sopenharmony_ci
751cb0ef41Sopenharmony_ci  /* Tell the other thread we're done. */
761cb0ef41Sopenharmony_ci  if (cmpxchgi(&handle->pending, 1, 2) != 1)
771cb0ef41Sopenharmony_ci    abort();
781cb0ef41Sopenharmony_ci
791cb0ef41Sopenharmony_ci  return 0;
801cb0ef41Sopenharmony_ci}
811cb0ef41Sopenharmony_ci
821cb0ef41Sopenharmony_ci
831cb0ef41Sopenharmony_ci/* Only call this from the event loop thread. */
841cb0ef41Sopenharmony_cistatic int uv__async_spin(uv_async_t* handle) {
851cb0ef41Sopenharmony_ci  int i;
861cb0ef41Sopenharmony_ci  int rc;
871cb0ef41Sopenharmony_ci
881cb0ef41Sopenharmony_ci  for (;;) {
891cb0ef41Sopenharmony_ci    /* 997 is not completely chosen at random. It's a prime number, acyclical
901cb0ef41Sopenharmony_ci     * by nature, and should therefore hopefully dampen sympathetic resonance.
911cb0ef41Sopenharmony_ci     */
921cb0ef41Sopenharmony_ci    for (i = 0; i < 997; i++) {
931cb0ef41Sopenharmony_ci      /* rc=0 -- handle is not pending.
941cb0ef41Sopenharmony_ci       * rc=1 -- handle is pending, other thread is still working with it.
951cb0ef41Sopenharmony_ci       * rc=2 -- handle is pending, other thread is done.
961cb0ef41Sopenharmony_ci       */
971cb0ef41Sopenharmony_ci      rc = cmpxchgi(&handle->pending, 2, 0);
981cb0ef41Sopenharmony_ci
991cb0ef41Sopenharmony_ci      if (rc != 1)
1001cb0ef41Sopenharmony_ci        return rc;
1011cb0ef41Sopenharmony_ci
1021cb0ef41Sopenharmony_ci      /* Other thread is busy with this handle, spin until it's done. */
1031cb0ef41Sopenharmony_ci      cpu_relax();
1041cb0ef41Sopenharmony_ci    }
1051cb0ef41Sopenharmony_ci
1061cb0ef41Sopenharmony_ci    /* Yield the CPU. We may have preempted the other thread while it's
1071cb0ef41Sopenharmony_ci     * inside the critical section and if it's running on the same CPU
1081cb0ef41Sopenharmony_ci     * as us, we'll just burn CPU cycles until the end of our time slice.
1091cb0ef41Sopenharmony_ci     */
1101cb0ef41Sopenharmony_ci    sched_yield();
1111cb0ef41Sopenharmony_ci  }
1121cb0ef41Sopenharmony_ci}
1131cb0ef41Sopenharmony_ci
1141cb0ef41Sopenharmony_ci
1151cb0ef41Sopenharmony_civoid uv__async_close(uv_async_t* handle) {
1161cb0ef41Sopenharmony_ci  uv__async_spin(handle);
1171cb0ef41Sopenharmony_ci  QUEUE_REMOVE(&handle->queue);
1181cb0ef41Sopenharmony_ci  uv__handle_stop(handle);
1191cb0ef41Sopenharmony_ci}
1201cb0ef41Sopenharmony_ci
1211cb0ef41Sopenharmony_ci
1221cb0ef41Sopenharmony_cistatic void uv__async_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {
1231cb0ef41Sopenharmony_ci  char buf[1024];
1241cb0ef41Sopenharmony_ci  ssize_t r;
1251cb0ef41Sopenharmony_ci  QUEUE queue;
1261cb0ef41Sopenharmony_ci  QUEUE* q;
1271cb0ef41Sopenharmony_ci  uv_async_t* h;
1281cb0ef41Sopenharmony_ci
1291cb0ef41Sopenharmony_ci  assert(w == &loop->async_io_watcher);
1301cb0ef41Sopenharmony_ci
1311cb0ef41Sopenharmony_ci  for (;;) {
1321cb0ef41Sopenharmony_ci    r = read(w->fd, buf, sizeof(buf));
1331cb0ef41Sopenharmony_ci
1341cb0ef41Sopenharmony_ci    if (r == sizeof(buf))
1351cb0ef41Sopenharmony_ci      continue;
1361cb0ef41Sopenharmony_ci
1371cb0ef41Sopenharmony_ci    if (r != -1)
1381cb0ef41Sopenharmony_ci      break;
1391cb0ef41Sopenharmony_ci
1401cb0ef41Sopenharmony_ci    if (errno == EAGAIN || errno == EWOULDBLOCK)
1411cb0ef41Sopenharmony_ci      break;
1421cb0ef41Sopenharmony_ci
1431cb0ef41Sopenharmony_ci    if (errno == EINTR)
1441cb0ef41Sopenharmony_ci      continue;
1451cb0ef41Sopenharmony_ci
1461cb0ef41Sopenharmony_ci    abort();
1471cb0ef41Sopenharmony_ci  }
1481cb0ef41Sopenharmony_ci
1491cb0ef41Sopenharmony_ci  QUEUE_MOVE(&loop->async_handles, &queue);
1501cb0ef41Sopenharmony_ci  while (!QUEUE_EMPTY(&queue)) {
1511cb0ef41Sopenharmony_ci    q = QUEUE_HEAD(&queue);
1521cb0ef41Sopenharmony_ci    h = QUEUE_DATA(q, uv_async_t, queue);
1531cb0ef41Sopenharmony_ci
1541cb0ef41Sopenharmony_ci    QUEUE_REMOVE(q);
1551cb0ef41Sopenharmony_ci    QUEUE_INSERT_TAIL(&loop->async_handles, q);
1561cb0ef41Sopenharmony_ci
1571cb0ef41Sopenharmony_ci    if (0 == uv__async_spin(h))
1581cb0ef41Sopenharmony_ci      continue;  /* Not pending. */
1591cb0ef41Sopenharmony_ci
1601cb0ef41Sopenharmony_ci    if (h->async_cb == NULL)
1611cb0ef41Sopenharmony_ci      continue;
1621cb0ef41Sopenharmony_ci
1631cb0ef41Sopenharmony_ci    h->async_cb(h);
1641cb0ef41Sopenharmony_ci  }
1651cb0ef41Sopenharmony_ci}
1661cb0ef41Sopenharmony_ci
1671cb0ef41Sopenharmony_ci
1681cb0ef41Sopenharmony_cistatic void uv__async_send(uv_loop_t* loop) {
1691cb0ef41Sopenharmony_ci  const void* buf;
1701cb0ef41Sopenharmony_ci  ssize_t len;
1711cb0ef41Sopenharmony_ci  int fd;
1721cb0ef41Sopenharmony_ci  int r;
1731cb0ef41Sopenharmony_ci
1741cb0ef41Sopenharmony_ci  buf = "";
1751cb0ef41Sopenharmony_ci  len = 1;
1761cb0ef41Sopenharmony_ci  fd = loop->async_wfd;
1771cb0ef41Sopenharmony_ci
1781cb0ef41Sopenharmony_ci#if defined(__linux__)
1791cb0ef41Sopenharmony_ci  if (fd == -1) {
1801cb0ef41Sopenharmony_ci    static const uint64_t val = 1;
1811cb0ef41Sopenharmony_ci    buf = &val;
1821cb0ef41Sopenharmony_ci    len = sizeof(val);
1831cb0ef41Sopenharmony_ci    fd = loop->async_io_watcher.fd;  /* eventfd */
1841cb0ef41Sopenharmony_ci  }
1851cb0ef41Sopenharmony_ci#endif
1861cb0ef41Sopenharmony_ci
1871cb0ef41Sopenharmony_ci  do
1881cb0ef41Sopenharmony_ci    r = write(fd, buf, len);
1891cb0ef41Sopenharmony_ci  while (r == -1 && errno == EINTR);
1901cb0ef41Sopenharmony_ci
1911cb0ef41Sopenharmony_ci  if (r == len)
1921cb0ef41Sopenharmony_ci    return;
1931cb0ef41Sopenharmony_ci
1941cb0ef41Sopenharmony_ci  if (r == -1)
1951cb0ef41Sopenharmony_ci    if (errno == EAGAIN || errno == EWOULDBLOCK)
1961cb0ef41Sopenharmony_ci      return;
1971cb0ef41Sopenharmony_ci
1981cb0ef41Sopenharmony_ci  abort();
1991cb0ef41Sopenharmony_ci}
2001cb0ef41Sopenharmony_ci
2011cb0ef41Sopenharmony_ci
2021cb0ef41Sopenharmony_cistatic int uv__async_start(uv_loop_t* loop) {
2031cb0ef41Sopenharmony_ci  int pipefd[2];
2041cb0ef41Sopenharmony_ci  int err;
2051cb0ef41Sopenharmony_ci
2061cb0ef41Sopenharmony_ci  if (loop->async_io_watcher.fd != -1)
2071cb0ef41Sopenharmony_ci    return 0;
2081cb0ef41Sopenharmony_ci
2091cb0ef41Sopenharmony_ci#ifdef __linux__
2101cb0ef41Sopenharmony_ci  err = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK);
2111cb0ef41Sopenharmony_ci  if (err < 0)
2121cb0ef41Sopenharmony_ci    return UV__ERR(errno);
2131cb0ef41Sopenharmony_ci
2141cb0ef41Sopenharmony_ci  pipefd[0] = err;
2151cb0ef41Sopenharmony_ci  pipefd[1] = -1;
2161cb0ef41Sopenharmony_ci#else
2171cb0ef41Sopenharmony_ci  err = uv__make_pipe(pipefd, UV_NONBLOCK_PIPE);
2181cb0ef41Sopenharmony_ci  if (err < 0)
2191cb0ef41Sopenharmony_ci    return err;
2201cb0ef41Sopenharmony_ci#endif
2211cb0ef41Sopenharmony_ci
2221cb0ef41Sopenharmony_ci  uv__io_init(&loop->async_io_watcher, uv__async_io, pipefd[0]);
2231cb0ef41Sopenharmony_ci  uv__io_start(loop, &loop->async_io_watcher, POLLIN);
2241cb0ef41Sopenharmony_ci  loop->async_wfd = pipefd[1];
2251cb0ef41Sopenharmony_ci
2261cb0ef41Sopenharmony_ci  return 0;
2271cb0ef41Sopenharmony_ci}
2281cb0ef41Sopenharmony_ci
2291cb0ef41Sopenharmony_ci
2301cb0ef41Sopenharmony_ciint uv__async_fork(uv_loop_t* loop) {
2311cb0ef41Sopenharmony_ci  if (loop->async_io_watcher.fd == -1) /* never started */
2321cb0ef41Sopenharmony_ci    return 0;
2331cb0ef41Sopenharmony_ci
2341cb0ef41Sopenharmony_ci  uv__async_stop(loop);
2351cb0ef41Sopenharmony_ci
2361cb0ef41Sopenharmony_ci  return uv__async_start(loop);
2371cb0ef41Sopenharmony_ci}
2381cb0ef41Sopenharmony_ci
2391cb0ef41Sopenharmony_ci
2401cb0ef41Sopenharmony_civoid uv__async_stop(uv_loop_t* loop) {
2411cb0ef41Sopenharmony_ci  if (loop->async_io_watcher.fd == -1)
2421cb0ef41Sopenharmony_ci    return;
2431cb0ef41Sopenharmony_ci
2441cb0ef41Sopenharmony_ci  if (loop->async_wfd != -1) {
2451cb0ef41Sopenharmony_ci    if (loop->async_wfd != loop->async_io_watcher.fd)
2461cb0ef41Sopenharmony_ci      uv__close(loop->async_wfd);
2471cb0ef41Sopenharmony_ci    loop->async_wfd = -1;
2481cb0ef41Sopenharmony_ci  }
2491cb0ef41Sopenharmony_ci
2501cb0ef41Sopenharmony_ci  uv__io_stop(loop, &loop->async_io_watcher, POLLIN);
2511cb0ef41Sopenharmony_ci  uv__close(loop->async_io_watcher.fd);
2521cb0ef41Sopenharmony_ci  loop->async_io_watcher.fd = -1;
2531cb0ef41Sopenharmony_ci}
254