1#include <glib.h>
2#include <glib/gwakeup.h>
3#ifdef G_OS_UNIX
4#include <unistd.h>
5#endif
6
7#ifdef _WIN32
8static void alarm (int sec) { }
9#endif
10
11static gboolean
12check_signaled (GWakeup *wakeup)
13{
14 GPollFD fd;
15
16 g_wakeup_get_pollfd (wakeup, poll_fd: &fd);
17 return g_poll (fds: &fd, nfds: 1, timeout: 0);
18}
19
20static void
21wait_for_signaled (GWakeup *wakeup)
22{
23 GPollFD fd;
24
25 g_wakeup_get_pollfd (wakeup, poll_fd: &fd);
26 g_poll (fds: &fd, nfds: 1, timeout: -1);
27}
28
29static void
30test_semantics (void)
31{
32 GWakeup *wakeup;
33 gint i;
34
35 /* prevent the test from deadlocking */
36 alarm (seconds: 60);
37
38 wakeup = g_wakeup_new ();
39 g_assert (!check_signaled (wakeup));
40
41 g_wakeup_signal (wakeup);
42 g_assert (check_signaled (wakeup));
43
44 g_wakeup_acknowledge (wakeup);
45 g_assert (!check_signaled (wakeup));
46
47 g_wakeup_free (wakeup);
48
49 /* free unused */
50 wakeup = g_wakeup_new ();
51 g_wakeup_free (wakeup);
52
53 /* free while signaled */
54 wakeup = g_wakeup_new ();
55 g_wakeup_signal (wakeup);
56 g_wakeup_free (wakeup);
57
58 /* ensure excessive signalling doesn't deadlock */
59 wakeup = g_wakeup_new ();
60 for (i = 0; i < 1000000; i++)
61 g_wakeup_signal (wakeup);
62 g_assert (check_signaled (wakeup));
63
64 /* ensure a single acknowledgement is sufficient */
65 g_wakeup_acknowledge (wakeup);
66 g_assert (!check_signaled (wakeup));
67
68 g_wakeup_free (wakeup);
69
70 /* cancel the alarm */
71 alarm (seconds: 0);
72}
73
74struct token
75{
76 gpointer owner;
77 gint ttl;
78};
79
80struct context
81{
82 GSList *pending_tokens;
83 GMutex lock;
84 GWakeup *wakeup;
85 gboolean quit;
86};
87
88#define NUM_THREADS 50
89#define NUM_TOKENS 5
90#define TOKEN_TTL 100000
91
92static struct context contexts[NUM_THREADS];
93static GThread *threads[NUM_THREADS];
94static GWakeup *last_token_wakeup;
95static gint tokens_alive; /* (atomic) */
96
97static void
98context_init (struct context *ctx)
99{
100 ctx->pending_tokens = NULL;
101 g_mutex_init (mutex: &ctx->lock);
102 ctx->wakeup = g_wakeup_new ();
103 ctx->quit = FALSE;
104}
105
106static void
107context_clear (struct context *ctx)
108{
109 g_assert (ctx->pending_tokens == NULL);
110 g_assert (ctx->quit);
111
112 g_mutex_clear (mutex: &ctx->lock);
113 g_wakeup_free (wakeup: ctx->wakeup);
114}
115
116static void
117context_quit (struct context *ctx)
118{
119 g_atomic_int_set (&ctx->quit, TRUE);
120 g_wakeup_signal (wakeup: ctx->wakeup);
121}
122
123static struct token *
124context_try_pop_token (struct context *ctx)
125{
126 struct token *token = NULL;
127
128 g_mutex_lock (mutex: &ctx->lock);
129 if (ctx->pending_tokens != NULL)
130 {
131 token = ctx->pending_tokens->data;
132 ctx->pending_tokens = g_slist_delete_link (list: ctx->pending_tokens,
133 link_: ctx->pending_tokens);
134 }
135 g_mutex_unlock (mutex: &ctx->lock);
136
137 return token;
138}
139
140static void
141context_push_token (struct context *ctx,
142 struct token *token)
143{
144 g_assert (token->owner == ctx);
145
146 g_mutex_lock (mutex: &ctx->lock);
147 ctx->pending_tokens = g_slist_prepend (list: ctx->pending_tokens, data: token);
148 g_mutex_unlock (mutex: &ctx->lock);
149
150 g_wakeup_signal (wakeup: ctx->wakeup);
151}
152
153static void
154dispatch_token (struct token *token)
155{
156 if (token->ttl > 0)
157 {
158 struct context *ctx;
159 gint next_ctx;
160
161 next_ctx = g_test_rand_int_range (begin: 0, NUM_THREADS);
162 ctx = &contexts[next_ctx];
163 token->owner = ctx;
164 token->ttl--;
165
166 context_push_token (ctx, token);
167 }
168 else
169 {
170 g_slice_free (struct token, token);
171
172 if (g_atomic_int_dec_and_test (&tokens_alive))
173 g_wakeup_signal (wakeup: last_token_wakeup);
174 }
175}
176
177static struct token *
178token_new (int ttl)
179{
180 struct token *token;
181
182 token = g_slice_new (struct token);
183 token->ttl = ttl;
184
185 g_atomic_int_inc (&tokens_alive);
186
187 return token;
188}
189
190static gpointer
191thread_func (gpointer data)
192{
193 struct context *ctx = data;
194 struct token *token;
195
196 while (!g_atomic_int_get (&ctx->quit))
197 {
198 wait_for_signaled (wakeup: ctx->wakeup);
199 g_wakeup_acknowledge (wakeup: ctx->wakeup);
200
201 while ((token = context_try_pop_token (ctx)) != NULL)
202 {
203 g_assert (token->owner == ctx);
204 dispatch_token (token);
205 }
206 }
207
208 return NULL;
209}
210
211static void
212test_threaded (void)
213{
214 gint i;
215
216 /* make sure we don't block forever */
217 alarm (seconds: 60);
218
219 /* simple mainloop test based on GWakeup.
220 *
221 * create a bunch of contexts and a thread to 'run' each one. create
222 * some tokens and randomly pass them between the threads, until the
223 * TTL on each token is zero.
224 *
225 * when no tokens are left, signal that we are done. the mainthread
226 * will then signal each worker thread to exit and join them to make
227 * sure that works.
228 */
229
230 last_token_wakeup = g_wakeup_new ();
231
232 /* create contexts, assign to threads */
233 for (i = 0; i < NUM_THREADS; i++)
234 {
235 context_init (ctx: &contexts[i]);
236 threads[i] = g_thread_new (name: "test", func: thread_func, data: &contexts[i]);
237 }
238
239 /* dispatch tokens */
240 for (i = 0; i < NUM_TOKENS; i++)
241 dispatch_token (token: token_new (TOKEN_TTL));
242
243 /* wait until all tokens are gone */
244 wait_for_signaled (wakeup: last_token_wakeup);
245
246 /* ask threads to quit, join them, cleanup */
247 for (i = 0; i < NUM_THREADS; i++)
248 {
249 context_quit (ctx: &contexts[i]);
250 g_thread_join (thread: threads[i]);
251 context_clear (ctx: &contexts[i]);
252 }
253
254 g_wakeup_free (wakeup: last_token_wakeup);
255
256 /* cancel alarm */
257 alarm (seconds: 0);
258}
259
260int
261main (int argc, char **argv)
262{
263 g_test_init (argc: &argc, argv: &argv, NULL);
264
265#ifdef TEST_EVENTFD_FALLBACK
266#define TESTNAME_SUFFIX "-fallback"
267#else
268#define TESTNAME_SUFFIX
269#endif
270
271
272 g_test_add_func (testpath: "/gwakeup/semantics" TESTNAME_SUFFIX, test_func: test_semantics);
273 g_test_add_func (testpath: "/gwakeup/threaded" TESTNAME_SUFFIX, test_func: test_threaded);
274
275 return g_test_run ();
276}
277

source code of gtk/subprojects/glib/glib/tests/gwakeuptest.c