nexmon – Blame information for rev 1
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
1 | office | 1 | /* Unit tests for GCond |
2 | * Copyright (C) 2011 Red Hat, Inc |
||
3 | * Author: Matthias Clasen |
||
4 | * |
||
5 | * This work is provided "as is"; redistribution and modification |
||
6 | * in whole or in part, in any medium, physical or electronic is |
||
7 | * permitted without restriction. |
||
8 | * |
||
9 | * This work 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. |
||
12 | * |
||
13 | * In no event shall the authors or contributors be liable for any |
||
14 | * direct, indirect, incidental, special, exemplary, or consequential |
||
15 | * damages (including, but not limited to, procurement of substitute |
||
16 | * goods or services; loss of use, data, or profits; or business |
||
17 | * interruption) however caused and on any theory of liability, whether |
||
18 | * in contract, strict liability, or tort (including negligence or |
||
19 | * otherwise) arising in any way out of the use of this software, even |
||
20 | * if advised of the possibility of such damage. |
||
21 | */ |
||
22 | |||
23 | /* We are testing some deprecated APIs here */ |
||
24 | #define GLIB_DISABLE_DEPRECATION_WARNINGS |
||
25 | |||
26 | #include <glib.h> |
||
27 | |||
28 | static GCond cond; |
||
29 | static GMutex mutex; |
||
30 | static volatile gint next; |
||
31 | |||
32 | static void |
||
33 | push_value (gint value) |
||
34 | { |
||
35 | g_mutex_lock (&mutex); |
||
36 | while (next != 0) |
||
37 | g_cond_wait (&cond, &mutex); |
||
38 | next = value; |
||
39 | if (g_test_verbose ()) |
||
40 | g_printerr ("Thread %p producing next value: %d\n", g_thread_self (), value); |
||
41 | if (value % 10 == 0) |
||
42 | g_cond_broadcast (&cond); |
||
43 | else |
||
44 | g_cond_signal (&cond); |
||
45 | g_mutex_unlock (&mutex); |
||
46 | } |
||
47 | |||
48 | static gint |
||
49 | pop_value (void) |
||
50 | { |
||
51 | gint value; |
||
52 | |||
53 | g_mutex_lock (&mutex); |
||
54 | while (next == 0) |
||
55 | { |
||
56 | if (g_test_verbose ()) |
||
57 | g_printerr ("Thread %p waiting for cond\n", g_thread_self ()); |
||
58 | g_cond_wait (&cond, &mutex); |
||
59 | } |
||
60 | value = next; |
||
61 | next = 0; |
||
62 | g_cond_broadcast (&cond); |
||
63 | if (g_test_verbose ()) |
||
64 | g_printerr ("Thread %p consuming value %d\n", g_thread_self (), value); |
||
65 | g_mutex_unlock (&mutex); |
||
66 | |||
67 | return value; |
||
68 | } |
||
69 | |||
70 | static gpointer |
||
71 | produce_values (gpointer data) |
||
72 | { |
||
73 | gint total; |
||
74 | gint i; |
||
75 | |||
76 | total = 0; |
||
77 | |||
78 | for (i = 1; i < 100; i++) |
||
79 | { |
||
80 | total += i; |
||
81 | push_value (i); |
||
82 | } |
||
83 | |||
84 | push_value (-1); |
||
85 | push_value (-1); |
||
86 | |||
87 | if (g_test_verbose ()) |
||
88 | g_printerr ("Thread %p produced %d altogether\n", g_thread_self (), total); |
||
89 | |||
90 | return GINT_TO_POINTER (total); |
||
91 | } |
||
92 | |||
93 | static gpointer |
||
94 | consume_values (gpointer data) |
||
95 | { |
||
96 | gint accum = 0; |
||
97 | gint value; |
||
98 | |||
99 | while (TRUE) |
||
100 | { |
||
101 | value = pop_value (); |
||
102 | if (value == -1) |
||
103 | break; |
||
104 | |||
105 | accum += value; |
||
106 | } |
||
107 | |||
108 | if (g_test_verbose ()) |
||
109 | g_printerr ("Thread %p accumulated %d\n", g_thread_self (), accum); |
||
110 | |||
111 | return GINT_TO_POINTER (accum); |
||
112 | } |
||
113 | |||
114 | static GThread *producer, *consumer1, *consumer2; |
||
115 | |||
116 | static void |
||
117 | test_cond1 (void) |
||
118 | { |
||
119 | gint total, acc1, acc2; |
||
120 | |||
121 | producer = g_thread_create (produce_values, NULL, TRUE, NULL); |
||
122 | consumer1 = g_thread_create (consume_values, NULL, TRUE, NULL); |
||
123 | consumer2 = g_thread_create (consume_values, NULL, TRUE, NULL); |
||
124 | |||
125 | total = GPOINTER_TO_INT (g_thread_join (producer)); |
||
126 | acc1 = GPOINTER_TO_INT (g_thread_join (consumer1)); |
||
127 | acc2 = GPOINTER_TO_INT (g_thread_join (consumer2)); |
||
128 | |||
129 | g_assert_cmpint (total, ==, acc1 + acc2); |
||
130 | } |
||
131 | |||
132 | typedef struct |
||
133 | { |
||
134 | GMutex mutex; |
||
135 | GCond cond; |
||
136 | gint limit; |
||
137 | gint count; |
||
138 | } Barrier; |
||
139 | |||
140 | static void |
||
141 | barrier_init (Barrier *barrier, |
||
142 | gint limit) |
||
143 | { |
||
144 | g_mutex_init (&barrier->mutex); |
||
145 | g_cond_init (&barrier->cond); |
||
146 | barrier->limit = limit; |
||
147 | barrier->count = limit; |
||
148 | } |
||
149 | |||
150 | static gint |
||
151 | barrier_wait (Barrier *barrier) |
||
152 | { |
||
153 | gint ret; |
||
154 | |||
155 | g_mutex_lock (&barrier->mutex); |
||
156 | barrier->count--; |
||
157 | if (barrier->count == 0) |
||
158 | { |
||
159 | ret = -1; |
||
160 | barrier->count = barrier->limit; |
||
161 | g_cond_broadcast (&barrier->cond); |
||
162 | } |
||
163 | else |
||
164 | { |
||
165 | ret = 0; |
||
166 | while (barrier->count != barrier->limit) |
||
167 | g_cond_wait (&barrier->cond, &barrier->mutex); |
||
168 | } |
||
169 | g_mutex_unlock (&barrier->mutex); |
||
170 | |||
171 | return ret; |
||
172 | } |
||
173 | |||
174 | static void |
||
175 | barrier_clear (Barrier *barrier) |
||
176 | { |
||
177 | g_mutex_clear (&barrier->mutex); |
||
178 | g_cond_clear (&barrier->cond); |
||
179 | } |
||
180 | |||
181 | static Barrier b; |
||
182 | static gint check; |
||
183 | |||
184 | static gpointer |
||
185 | cond2_func (gpointer data) |
||
186 | { |
||
187 | gint value = GPOINTER_TO_INT (data); |
||
188 | gint ret; |
||
189 | |||
190 | g_atomic_int_inc (&check); |
||
191 | |||
192 | if (g_test_verbose ()) |
||
193 | g_printerr ("thread %d starting, check %d\n", value, g_atomic_int_get (&check)); |
||
194 | |||
195 | g_usleep (10000 * value); |
||
196 | |||
197 | g_atomic_int_inc (&check); |
||
198 | |||
199 | if (g_test_verbose ()) |
||
200 | g_printerr ("thread %d reaching barrier, check %d\n", value, g_atomic_int_get (&check)); |
||
201 | |||
202 | ret = barrier_wait (&b); |
||
203 | |||
204 | g_assert_cmpint (g_atomic_int_get (&check), ==, 10); |
||
205 | |||
206 | if (g_test_verbose ()) |
||
207 | g_printerr ("thread %d leaving barrier (%d), check %d\n", value, ret, g_atomic_int_get (&check)); |
||
208 | |||
209 | return NULL; |
||
210 | } |
||
211 | |||
212 | /* this test demonstrates how to use a condition variable |
||
213 | * to implement a barrier |
||
214 | */ |
||
215 | static void |
||
216 | test_cond2 (void) |
||
217 | { |
||
218 | gint i; |
||
219 | GThread *threads[5]; |
||
220 | |||
221 | g_atomic_int_set (&check, 0); |
||
222 | |||
223 | barrier_init (&b, 5); |
||
224 | for (i = 0; i < 5; i++) |
||
225 | threads[i] = g_thread_create (cond2_func, GINT_TO_POINTER (i), TRUE, NULL); |
||
226 | |||
227 | for (i = 0; i < 5; i++) |
||
228 | g_thread_join (threads[i]); |
||
229 | |||
230 | g_assert_cmpint (g_atomic_int_get (&check), ==, 10); |
||
231 | |||
232 | barrier_clear (&b); |
||
233 | } |
||
234 | |||
235 | static void |
||
236 | test_wait_until (void) |
||
237 | { |
||
238 | gint64 until; |
||
239 | GMutex lock; |
||
240 | GCond cond; |
||
241 | |||
242 | /* This test will make sure we don't wait too much or too little. |
||
243 | * |
||
244 | * We check the 'too long' with a timeout of 60 seconds. |
||
245 | * |
||
246 | * We check the 'too short' by verifying a guarantee of the API: we |
||
247 | * should not wake up until the specified time has passed. |
||
248 | */ |
||
249 | g_mutex_init (&lock); |
||
250 | g_cond_init (&cond); |
||
251 | |||
252 | until = g_get_monotonic_time () + G_TIME_SPAN_SECOND; |
||
253 | |||
254 | /* Could still have spurious wakeups, so we must loop... */ |
||
255 | g_mutex_lock (&lock); |
||
256 | while (g_cond_wait_until (&cond, &lock, until)) |
||
257 | ; |
||
258 | g_mutex_unlock (&lock); |
||
259 | |||
260 | /* Make sure it's after the until time */ |
||
261 | g_assert_cmpint (until, <=, g_get_monotonic_time ()); |
||
262 | |||
263 | /* Make sure it returns FALSE on timeout */ |
||
264 | until = g_get_monotonic_time () + G_TIME_SPAN_SECOND / 50; |
||
265 | g_mutex_lock (&lock); |
||
266 | g_assert (g_cond_wait_until (&cond, &lock, until) == FALSE); |
||
267 | g_mutex_unlock (&lock); |
||
268 | |||
269 | g_mutex_clear (&lock); |
||
270 | g_cond_clear (&cond); |
||
271 | } |
||
272 | |||
273 | int |
||
274 | main (int argc, char *argv[]) |
||
275 | { |
||
276 | g_test_init (&argc, &argv, NULL); |
||
277 | |||
278 | g_test_add_func ("/thread/cond1", test_cond1); |
||
279 | g_test_add_func ("/thread/cond2", test_cond2); |
||
280 | g_test_add_func ("/thread/cond/wait-until", test_wait_until); |
||
281 | |||
282 | return g_test_run (); |
||
283 | } |