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