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