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