1/*
2 * Copyright © 2011 Canonical Limited
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 *
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
16 *
17 * Author: Ryan Lortie <desrt@desrt.ca>
18 */
19
20#include "config.h"
21
22
23/* gwakeup.c is special -- GIO and some test cases include it. As such,
24 * it cannot include other glib headers without triggering the single
25 * includes warnings. We have to manually include its dependencies here
26 * (and at all other use sites).
27 */
28#ifdef GLIB_COMPILATION
29#include "gtypes.h"
30#include "gpoll.h"
31#else
32#include <glib.h>
33#endif
34
35#include "gwakeup.h"
36
37/*< private >
38 * SECTION:gwakeup
39 * @title: GWakeup
40 * @short_description: portable cross-thread event signal mechanism
41 *
42 * #GWakeup is a simple and portable way of signaling events between
43 * different threads in a way that integrates nicely with g_poll().
44 * GLib uses it internally for cross-thread signalling in the
45 * implementation of #GMainContext and #GCancellable.
46 *
47 * You first create a #GWakeup with g_wakeup_new() and initialise a
48 * #GPollFD from it using g_wakeup_get_pollfd(). Polling on the created
49 * #GPollFD will block until g_wakeup_signal() is called, at which point
50 * it will immediately return. Future attempts to poll will continue to
51 * return until g_wakeup_acknowledge() is called. g_wakeup_free() is
52 * used to free a #GWakeup.
53 *
54 * On sufficiently modern Linux, this is implemented using eventfd. On
55 * Windows it is implemented using an event handle. On other systems it
56 * is implemented with a pair of pipes.
57 *
58 * Since: 2.30
59 **/
60#ifdef _WIN32
61
62#include <windows.h>
63
64#ifdef GLIB_COMPILATION
65#include "gmessages.h"
66#include "giochannel.h"
67#include "gwin32.h"
68#endif
69
70GWakeup *
71g_wakeup_new (void)
72{
73 HANDLE wakeup;
74
75 wakeup = CreateEvent (NULL, TRUE, FALSE, NULL);
76
77 if (wakeup == NULL)
78 g_error ("Cannot create event for GWakeup: %s",
79 g_win32_error_message (GetLastError ()));
80
81 return (GWakeup *) wakeup;
82}
83
84void
85g_wakeup_get_pollfd (GWakeup *wakeup,
86 GPollFD *poll_fd)
87{
88 poll_fd->fd = (gintptr) wakeup;
89 poll_fd->events = G_IO_IN;
90}
91
92void
93g_wakeup_acknowledge (GWakeup *wakeup)
94{
95 ResetEvent ((HANDLE) wakeup);
96}
97
98void
99g_wakeup_signal (GWakeup *wakeup)
100{
101 SetEvent ((HANDLE) wakeup);
102}
103
104void
105g_wakeup_free (GWakeup *wakeup)
106{
107 CloseHandle ((HANDLE) wakeup);
108}
109
110#else
111
112#include "glib-unix.h"
113#include <fcntl.h>
114
115#if defined (HAVE_EVENTFD)
116#include <sys/eventfd.h>
117#endif
118
119struct _GWakeup
120{
121 gint fds[2];
122};
123
124/**
125 * g_wakeup_new:
126 *
127 * Creates a new #GWakeup.
128 *
129 * You should use g_wakeup_free() to free it when you are done.
130 *
131 * Returns: a new #GWakeup
132 *
133 * Since: 2.30
134 **/
135GWakeup *
136g_wakeup_new (void)
137{
138 GError *error = NULL;
139 GWakeup *wakeup;
140
141 wakeup = g_slice_new (GWakeup);
142
143 /* try eventfd first, if we think we can */
144#if defined (HAVE_EVENTFD)
145#ifndef TEST_EVENTFD_FALLBACK
146 wakeup->fds[0] = eventfd (count: 0, EFD_CLOEXEC | EFD_NONBLOCK);
147#else
148 wakeup->fds[0] = -1;
149#endif
150
151 if (wakeup->fds[0] != -1)
152 {
153 wakeup->fds[1] = -1;
154 return wakeup;
155 }
156
157 /* for any failure, try a pipe instead */
158#endif
159
160 if (!g_unix_open_pipe (fds: wakeup->fds, FD_CLOEXEC, error: &error))
161 g_error ("Creating pipes for GWakeup: %s", error->message);
162
163 if (!g_unix_set_fd_nonblocking (fd: wakeup->fds[0], TRUE, error: &error) ||
164 !g_unix_set_fd_nonblocking (fd: wakeup->fds[1], TRUE, error: &error))
165 g_error ("Set pipes non-blocking for GWakeup: %s", error->message);
166
167 return wakeup;
168}
169
170/**
171 * g_wakeup_get_pollfd:
172 * @wakeup: a #GWakeup
173 * @poll_fd: a #GPollFD
174 *
175 * Prepares a @poll_fd such that polling on it will succeed when
176 * g_wakeup_signal() has been called on @wakeup.
177 *
178 * @poll_fd is valid until @wakeup is freed.
179 *
180 * Since: 2.30
181 **/
182void
183g_wakeup_get_pollfd (GWakeup *wakeup,
184 GPollFD *poll_fd)
185{
186 poll_fd->fd = wakeup->fds[0];
187 poll_fd->events = G_IO_IN;
188}
189
190/**
191 * g_wakeup_acknowledge:
192 * @wakeup: a #GWakeup
193 *
194 * Acknowledges receipt of a wakeup signal on @wakeup.
195 *
196 * You must call this after @wakeup polls as ready. If not, it will
197 * continue to poll as ready until you do so.
198 *
199 * If you call this function and @wakeup is not signaled, nothing
200 * happens.
201 *
202 * Since: 2.30
203 **/
204void
205g_wakeup_acknowledge (GWakeup *wakeup)
206{
207 char buffer[16];
208
209 /* read until it is empty */
210 while (read (fd: wakeup->fds[0], buf: buffer, nbytes: sizeof buffer) == sizeof buffer);
211}
212
213/**
214 * g_wakeup_signal:
215 * @wakeup: a #GWakeup
216 *
217 * Signals @wakeup.
218 *
219 * Any future (or present) polling on the #GPollFD returned by
220 * g_wakeup_get_pollfd() will immediately succeed until such a time as
221 * g_wakeup_acknowledge() is called.
222 *
223 * This function is safe to call from a UNIX signal handler.
224 *
225 * Since: 2.30
226 **/
227void
228g_wakeup_signal (GWakeup *wakeup)
229{
230 int res;
231
232 if (wakeup->fds[1] == -1)
233 {
234 guint64 one = 1;
235
236 /* eventfd() case. It requires a 64-bit counter increment value to be
237 * written. */
238 do
239 res = write (fd: wakeup->fds[0], buf: &one, n: sizeof one);
240 while (G_UNLIKELY (res == -1 && errno == EINTR));
241 }
242 else
243 {
244 guint8 one = 1;
245
246 /* Non-eventfd() case. Only a single byte needs to be written, and it can
247 * have an arbitrary value. */
248 do
249 res = write (fd: wakeup->fds[1], buf: &one, n: sizeof one);
250 while (G_UNLIKELY (res == -1 && errno == EINTR));
251 }
252}
253
254/**
255 * g_wakeup_free:
256 * @wakeup: a #GWakeup
257 *
258 * Frees @wakeup.
259 *
260 * You must not currently be polling on the #GPollFD returned by
261 * g_wakeup_get_pollfd(), or the result is undefined.
262 **/
263void
264g_wakeup_free (GWakeup *wakeup)
265{
266 close (fd: wakeup->fds[0]);
267
268 if (wakeup->fds[1] != -1)
269 close (fd: wakeup->fds[1]);
270
271 g_slice_free (GWakeup, wakeup);
272}
273
274#endif /* !_WIN32 */
275

source code of gtk/subprojects/glib/glib/gwakeup.c