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