Blame glib/gwakeup.c

Packit Service d3d246
/*
Packit Service d3d246
 * Copyright © 2011 Canonical Limited
Packit Service d3d246
 *
Packit Service d3d246
 * This library is free software; you can redistribute it and/or
Packit Service d3d246
 * modify it under the terms of the GNU Lesser General Public
Packit Service d3d246
 * License as published by the Free Software Foundation; either
Packit Service d3d246
 * version 2.1 of the License, or (at your option) any later version.
Packit Service d3d246
 *
Packit Service d3d246
 * This library is distributed in the hope that it will be useful,
Packit Service d3d246
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service d3d246
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service d3d246
 * Lesser General Public License for more details.
Packit Service d3d246
 *
Packit Service d3d246
 * You should have received a copy of the GNU Lesser General Public
Packit Service d3d246
 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
Packit Service d3d246
 *
Packit Service d3d246
 * Author: Ryan Lortie <desrt@desrt.ca>
Packit Service d3d246
 */
Packit Service d3d246
Packit Service d3d246
#include "config.h"
Packit Service d3d246
Packit Service d3d246
Packit Service d3d246
/* gwakeup.c is special -- GIO and some test cases include it.  As such,
Packit Service d3d246
 * it cannot include other glib headers without triggering the single
Packit Service d3d246
 * includes warnings.  We have to manually include its dependencies here
Packit Service d3d246
 * (and at all other use sites).
Packit Service d3d246
 */
Packit Service d3d246
#ifdef GLIB_COMPILATION
Packit Service d3d246
#include "gtypes.h"
Packit Service d3d246
#include "gpoll.h"
Packit Service d3d246
#else
Packit Service d3d246
#include <glib.h>
Packit Service d3d246
#endif
Packit Service d3d246
Packit Service d3d246
#include "gwakeup.h"
Packit Service d3d246
Packit Service d3d246
/*< private >
Packit Service d3d246
 * SECTION:gwakeup
Packit Service d3d246
 * @title: GWakeup
Packit Service d3d246
 * @short_description: portable cross-thread event signal mechanism
Packit Service d3d246
 *
Packit Service d3d246
 * #GWakeup is a simple and portable way of signaling events between
Packit Service d3d246
 * different threads in a way that integrates nicely with g_poll().
Packit Service d3d246
 * GLib uses it internally for cross-thread signalling in the
Packit Service d3d246
 * implementation of #GMainContext and #GCancellable.
Packit Service d3d246
 *
Packit Service d3d246
 * You first create a #GWakeup with g_wakeup_new() and initialise a
Packit Service d3d246
 * #GPollFD from it using g_wakeup_get_pollfd().  Polling on the created
Packit Service d3d246
 * #GPollFD will block until g_wakeup_signal() is called, at which point
Packit Service d3d246
 * it will immediately return.  Future attempts to poll will continue to
Packit Service d3d246
 * return until g_wakeup_acknowledge() is called.  g_wakeup_free() is
Packit Service d3d246
 * used to free a #GWakeup.
Packit Service d3d246
 *
Packit Service d3d246
 * On sufficiently modern Linux, this is implemented using eventfd.  On
Packit Service d3d246
 * Windows it is implemented using an event handle.  On other systems it
Packit Service d3d246
 * is implemented with a pair of pipes.
Packit Service d3d246
 *
Packit Service d3d246
 * Since: 2.30
Packit Service d3d246
 **/
Packit Service d3d246
#ifdef _WIN32
Packit Service d3d246
Packit Service d3d246
#include <windows.h>
Packit Service d3d246
Packit Service d3d246
#ifdef GLIB_COMPILATION
Packit Service d3d246
#include "gmessages.h"
Packit Service d3d246
#include "giochannel.h"
Packit Service d3d246
#include "gwin32.h"
Packit Service d3d246
#endif
Packit Service d3d246
Packit Service d3d246
GWakeup *
Packit Service d3d246
g_wakeup_new (void)
Packit Service d3d246
{
Packit Service d3d246
  HANDLE wakeup;
Packit Service d3d246
Packit Service d3d246
  wakeup = CreateEvent (NULL, TRUE, FALSE, NULL);
Packit Service d3d246
Packit Service d3d246
  if (wakeup == NULL)
Packit Service d3d246
    g_error ("Cannot create event for GWakeup: %s",
Packit Service d3d246
             g_win32_error_message (GetLastError ()));
Packit Service d3d246
Packit Service d3d246
  return (GWakeup *) wakeup;
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
void
Packit Service d3d246
g_wakeup_get_pollfd (GWakeup *wakeup,
Packit Service d3d246
                     GPollFD *poll_fd)
Packit Service d3d246
{
Packit Service d3d246
  poll_fd->fd = (gintptr) wakeup;
Packit Service d3d246
  poll_fd->events = G_IO_IN;
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
void
Packit Service d3d246
g_wakeup_acknowledge (GWakeup *wakeup)
Packit Service d3d246
{
Packit Service d3d246
  ResetEvent ((HANDLE) wakeup);
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
void
Packit Service d3d246
g_wakeup_signal (GWakeup *wakeup)
Packit Service d3d246
{
Packit Service d3d246
  SetEvent ((HANDLE) wakeup);
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
void
Packit Service d3d246
g_wakeup_free (GWakeup *wakeup)
Packit Service d3d246
{
Packit Service d3d246
  CloseHandle ((HANDLE) wakeup);
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
#else
Packit Service d3d246
Packit Service d3d246
#include "glib-unix.h"
Packit Service d3d246
#include <fcntl.h>
Packit Service d3d246
Packit Service d3d246
#if defined (HAVE_EVENTFD)
Packit Service d3d246
#include <sys/eventfd.h>
Packit Service d3d246
#endif
Packit Service d3d246
Packit Service d3d246
struct _GWakeup
Packit Service d3d246
{
Packit Service d3d246
  gint fds[2];
Packit Service d3d246
};
Packit Service d3d246
Packit Service d3d246
/**
Packit Service d3d246
 * g_wakeup_new:
Packit Service d3d246
 *
Packit Service d3d246
 * Creates a new #GWakeup.
Packit Service d3d246
 *
Packit Service d3d246
 * You should use g_wakeup_free() to free it when you are done.
Packit Service d3d246
 *
Packit Service d3d246
 * Returns: a new #GWakeup
Packit Service d3d246
 *
Packit Service d3d246
 * Since: 2.30
Packit Service d3d246
 **/
Packit Service d3d246
GWakeup *
Packit Service d3d246
g_wakeup_new (void)
Packit Service d3d246
{
Packit Service d3d246
  GError *error = NULL;
Packit Service d3d246
  GWakeup *wakeup;
Packit Service d3d246
Packit Service d3d246
  wakeup = g_slice_new (GWakeup);
Packit Service d3d246
Packit Service d3d246
  /* try eventfd first, if we think we can */
Packit Service d3d246
#if defined (HAVE_EVENTFD)
Packit Service d3d246
#ifndef TEST_EVENTFD_FALLBACK
Packit Service d3d246
  wakeup->fds[0] = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK);
Packit Service d3d246
#else
Packit Service d3d246
  wakeup->fds[0] = -1;
Packit Service d3d246
#endif
Packit Service d3d246
Packit Service d3d246
  if (wakeup->fds[0] != -1)
Packit Service d3d246
    {
Packit Service d3d246
      wakeup->fds[1] = -1;
Packit Service d3d246
      return wakeup;
Packit Service d3d246
    }
Packit Service d3d246
Packit Service d3d246
  /* for any failure, try a pipe instead */
Packit Service d3d246
#endif
Packit Service d3d246
Packit Service d3d246
  if (!g_unix_open_pipe (wakeup->fds, FD_CLOEXEC, &error))
Packit Service d3d246
    g_error ("Creating pipes for GWakeup: %s\n", error->message);
Packit Service d3d246
Packit Service d3d246
  if (!g_unix_set_fd_nonblocking (wakeup->fds[0], TRUE, &error) ||
Packit Service d3d246
      !g_unix_set_fd_nonblocking (wakeup->fds[1], TRUE, &error))
Packit Service d3d246
    g_error ("Set pipes non-blocking for GWakeup: %s\n", error->message);
Packit Service d3d246
Packit Service d3d246
  return wakeup;
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
/**
Packit Service d3d246
 * g_wakeup_get_pollfd:
Packit Service d3d246
 * @wakeup: a #GWakeup
Packit Service d3d246
 * @poll_fd: a #GPollFD
Packit Service d3d246
 *
Packit Service d3d246
 * Prepares a @poll_fd such that polling on it will succeed when
Packit Service d3d246
 * g_wakeup_signal() has been called on @wakeup.
Packit Service d3d246
 *
Packit Service d3d246
 * @poll_fd is valid until @wakeup is freed.
Packit Service d3d246
 *
Packit Service d3d246
 * Since: 2.30
Packit Service d3d246
 **/
Packit Service d3d246
void
Packit Service d3d246
g_wakeup_get_pollfd (GWakeup *wakeup,
Packit Service d3d246
                     GPollFD *poll_fd)
Packit Service d3d246
{
Packit Service d3d246
  poll_fd->fd = wakeup->fds[0];
Packit Service d3d246
  poll_fd->events = G_IO_IN;
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
/**
Packit Service d3d246
 * g_wakeup_acknowledge:
Packit Service d3d246
 * @wakeup: a #GWakeup
Packit Service d3d246
 *
Packit Service d3d246
 * Acknowledges receipt of a wakeup signal on @wakeup.
Packit Service d3d246
 *
Packit Service d3d246
 * You must call this after @wakeup polls as ready.  If not, it will
Packit Service d3d246
 * continue to poll as ready until you do so.
Packit Service d3d246
 *
Packit Service d3d246
 * If you call this function and @wakeup is not signaled, nothing
Packit Service d3d246
 * happens.
Packit Service d3d246
 *
Packit Service d3d246
 * Since: 2.30
Packit Service d3d246
 **/
Packit Service d3d246
void
Packit Service d3d246
g_wakeup_acknowledge (GWakeup *wakeup)
Packit Service d3d246
{
Packit Service d3d246
  char buffer[16];
Packit Service d3d246
Packit Service d3d246
  /* read until it is empty */
Packit Service d3d246
  while (read (wakeup->fds[0], buffer, sizeof buffer) == sizeof buffer);
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
/**
Packit Service d3d246
 * g_wakeup_signal:
Packit Service d3d246
 * @wakeup: a #GWakeup
Packit Service d3d246
 *
Packit Service d3d246
 * Signals @wakeup.
Packit Service d3d246
 *
Packit Service d3d246
 * Any future (or present) polling on the #GPollFD returned by
Packit Service d3d246
 * g_wakeup_get_pollfd() will immediately succeed until such a time as
Packit Service d3d246
 * g_wakeup_acknowledge() is called.
Packit Service d3d246
 *
Packit Service d3d246
 * This function is safe to call from a UNIX signal handler.
Packit Service d3d246
 *
Packit Service d3d246
 * Since: 2.30
Packit Service d3d246
 **/
Packit Service d3d246
void
Packit Service d3d246
g_wakeup_signal (GWakeup *wakeup)
Packit Service d3d246
{
Packit Service d3d246
  int res;
Packit Service d3d246
Packit Service d3d246
  if (wakeup->fds[1] == -1)
Packit Service d3d246
    {
Packit Service d3d246
      guint64 one = 1;
Packit Service d3d246
Packit Service d3d246
      /* eventfd() case. It requires a 64-bit counter increment value to be
Packit Service d3d246
       * written. */
Packit Service d3d246
      do
Packit Service d3d246
        res = write (wakeup->fds[0], &one, sizeof one);
Packit Service d3d246
      while (G_UNLIKELY (res == -1 && errno == EINTR));
Packit Service d3d246
    }
Packit Service d3d246
  else
Packit Service d3d246
    {
Packit Service d3d246
      guint8 one = 1;
Packit Service d3d246
Packit Service d3d246
      /* Non-eventfd() case. Only a single byte needs to be written, and it can
Packit Service d3d246
       * have an arbitrary value. */
Packit Service d3d246
      do
Packit Service d3d246
        res = write (wakeup->fds[1], &one, sizeof one);
Packit Service d3d246
      while (G_UNLIKELY (res == -1 && errno == EINTR));
Packit Service d3d246
    }
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
/**
Packit Service d3d246
 * g_wakeup_free:
Packit Service d3d246
 * @wakeup: a #GWakeup
Packit Service d3d246
 *
Packit Service d3d246
 * Frees @wakeup.
Packit Service d3d246
 *
Packit Service d3d246
 * You must not currently be polling on the #GPollFD returned by
Packit Service d3d246
 * g_wakeup_get_pollfd(), or the result is undefined.
Packit Service d3d246
 **/
Packit Service d3d246
void
Packit Service d3d246
g_wakeup_free (GWakeup *wakeup)
Packit Service d3d246
{
Packit Service d3d246
  close (wakeup->fds[0]);
Packit Service d3d246
Packit Service d3d246
  if (wakeup->fds[1] != -1)
Packit Service d3d246
    close (wakeup->fds[1]);
Packit Service d3d246
Packit Service d3d246
  g_slice_free (GWakeup, wakeup);
Packit Service d3d246
}
Packit Service d3d246
Packit Service d3d246
#endif /* !_WIN32 */