GWakeup: add signal safety note
[platform/upstream/glib.git] / glib / gwakeup.c
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 of the licence, 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, write to the
16  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17  * Boston, MA 02111-1307, USA.
18  *
19  * Author: Ryan Lortie <desrt@desrt.ca>
20  */
21
22 #include "config.h"
23
24 #include "gwakeup.h"
25
26 /**
27  * SECTION:gwakeup
28  * @title: GWakeup
29  * @short_description: portable cross-thread event signal mechanism
30  *
31  * #GWakeup is a simple and portable way of signaling events between
32  * different threads in a way that integrates nicely with g_poll().
33  * GLib uses it internally for cross-thread signalling in the
34  * implementation of #GMainContext and #GCancellable.
35  *
36  * You first create a #GWakeup with g_wakeup_new() and initialise a
37  * #GPollFD from it using g_wakeup_get_pollfd().  Polling on the created
38  * #GPollFD will block until g_wakeup_signal() is called, at which point
39  * it will immediately return.  Future attempts to poll will continue to
40  * return until g_wakeup_acknowledge() is called.  g_wakeup_free() is
41  * used to free a #GWakeup.
42  *
43  * On sufficiently modern Linux, this is implemented using eventfd.  On
44  * Windows it is implemented using an event handle.  On other systems it
45  * is implemented with a pair of pipes.
46  *
47  * Since: 2.30
48  **/
49 #ifdef _WIN32
50
51 #include <windows.h>
52 #include "gmessages.h"
53 #include "giochannel.h"
54 #include "gwin32.h"
55
56 GWakeup *
57 g_wakeup_new (void)
58 {
59   HANDLE wakeup;
60
61   wakeup = CreateEvent (NULL, TRUE, FALSE, NULL);
62
63   if (wakeup == NULL)
64     g_error ("Cannot create event for GWakeup: %s",
65              g_win32_error_message (GetLastError ()));
66
67   return (GWakeup *) wakeup;
68 }
69
70 void
71 g_wakeup_get_pollfd (GWakeup *wakeup,
72                      GPollFD *poll_fd)
73 {
74   poll_fd->fd = (gintptr) wakeup;
75   poll_fd->events = G_IO_IN;
76 }
77
78 void
79 g_wakeup_acknowledge (GWakeup *wakeup)
80 {
81   ResetEvent ((HANDLE) wakeup);
82 }
83
84 void
85 g_wakeup_signal (GWakeup *wakeup)
86 {
87   SetEvent ((HANDLE) wakeup);
88 }
89
90 void
91 g_wakeup_free (GWakeup *wakeup)
92 {
93   CloseHandle ((HANDLE) wakeup);
94 }
95
96 #else
97
98 #include "glib-unix.h"
99 #include <fcntl.h>
100
101 #if defined (HAVE_EVENTFD)
102 #include <sys/eventfd.h>
103 #endif
104
105 struct _GWakeup
106 {
107   gint fds[2];
108 };
109
110 /**
111  * g_wakeup_new:
112  *
113  * Creates a new #GWakeup.
114  *
115  * You should use g_wakeup_free() to free it when you are done.
116  *
117  * Returns: a new #GWakeup
118  *
119  * Since: 2.30
120  **/
121 GWakeup *
122 g_wakeup_new (void)
123 {
124   GError *error = NULL;
125   GWakeup *wakeup;
126
127   wakeup = g_slice_new (GWakeup);
128
129   /* try eventfd first, if we think we can */
130 #if defined (HAVE_EVENTFD)
131   wakeup->fds[0] = eventfd (0, EFD_CLOEXEC | EFD_NONBLOCK);
132
133   if (wakeup->fds[0] != -1)
134     {
135       wakeup->fds[1] = -1;
136       return wakeup;
137     }
138
139   /* for any failure, try a pipe instead */
140 #endif
141
142   if (!g_unix_open_pipe (wakeup->fds, FD_CLOEXEC, &error))
143     g_error ("Creating pipes for GWakeup: %s\n", error->message);
144
145   if (!g_unix_set_fd_nonblocking (wakeup->fds[0], TRUE, &error) ||
146       !g_unix_set_fd_nonblocking (wakeup->fds[1], TRUE, &error))
147     g_error ("Set pipes non-blocking for GWakeup: %s\n", error->message);
148
149   return wakeup;
150 }
151
152 /**
153  * g_wakeup_get_pollfd:
154  * @wakeup: a #GWakeup
155  * @poll_fd: a #GPollFD
156  *
157  * Prepares a @poll_fd such that polling on it will succeed when
158  * g_wakeup_signal() has been called on @wakeup.
159  *
160  * @poll_fd is valid until @wakeup is freed.
161  *
162  * Since: 2.30
163  **/
164 void
165 g_wakeup_get_pollfd (GWakeup *wakeup,
166                      GPollFD *poll_fd)
167 {
168   poll_fd->fd = wakeup->fds[0];
169   poll_fd->events = G_IO_IN;
170 }
171
172 /**
173  * g_wakeup_acknowledge:
174  * @wakeup: a #GWakeup
175  *
176  * Acknowledges receipt of a wakeup signal on @wakeup.
177  *
178  * You must call this after @wakeup polls as ready.  If not, it will
179  * continue to poll as ready until you do so.
180  *
181  * If you call this function and @wakeup is not signaled, nothing
182  * happens.
183  *
184  * Since: 2.30
185  **/
186 void
187 g_wakeup_acknowledge (GWakeup *wakeup)
188 {
189   char buffer[16];
190
191   /* read until it is empty */
192   while (read (wakeup->fds[0], buffer, sizeof buffer) == sizeof buffer);
193 }
194
195 /**
196  * g_wakeup_signal:
197  * @wakeup: a #GWakeup
198  *
199  * Signals @wakeup.
200  *
201  * Any future (or present) polling on the #GPollFD returned by
202  * g_wakeup_get_pollfd() will immediately succeed until such a time as
203  * g_wakeup_acknowledge() is called.
204  *
205  * This function is safe to call from a UNIX signal handler.
206  *
207  * Since: 2.30
208  **/
209 void
210 g_wakeup_signal (GWakeup *wakeup)
211 {
212   guint64 one = 1;
213
214   if (wakeup->fds[1] == -1)
215     write (wakeup->fds[0], &one, sizeof one);
216   else
217     write (wakeup->fds[1], &one, 1);
218 }
219
220 /**
221  * g_wakeup_free:
222  * @wakeup: a #GWakeup
223  *
224  * Frees @wakeup.
225  *
226  * You must not currently be polling on the #GPollFD returned by
227  * g_wakeup_get_pollfd(), or the result is undefined.
228  **/
229 void
230 g_wakeup_free (GWakeup *wakeup)
231 {
232   close (wakeup->fds[0]);
233
234   if (wakeup->fds[1] != -1)
235     close (wakeup->fds[1]);
236
237   g_slice_free (GWakeup, wakeup);
238 }
239
240 #endif /* !_WIN32 */