1 /*
2 * Copyright (c) 2008-2013 Zmanda, Inc. All Rights Reserved.
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
11 * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
12 * for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 *
18 * Contact information: Zmanda Inc., 465 S. Mathilda Ave., Suite 300
19 * Sunnyvale, CA 94085, USA, or: http://www.zmanda.com
20 */
21
22 #include "amanda.h"
23 #include "amsemaphore.h"
24 #include "testutils.h"
25 #include "util.h"
26
27 /*
28 * test that decrement waits properly
29 */
30
31 struct test_decr_wait_data {
32 amsemaphore_t *sem;
33 gboolean increment_called;
34 };
35
36 static gpointer
test_decr_wait_thread(gpointer datap)37 test_decr_wait_thread(gpointer datap)
38 {
39 struct test_decr_wait_data *data = datap;
40
41 /* should block */
42 amsemaphore_decrement(data->sem, 20);
43
44 /* if increment hasn't been called yet, that's an error. */
45 if (!data->increment_called)
46 return GINT_TO_POINTER(0);
47
48 return GINT_TO_POINTER(1);
49 }
50
51 static gboolean
test_decr_wait(void)52 test_decr_wait(void)
53 {
54 GThread *th;
55 struct test_decr_wait_data data = { NULL, FALSE };
56 int rv;
57
58 data.sem = amsemaphore_new_with_value(10),
59
60 th = g_thread_create(test_decr_wait_thread, (gpointer)&data, TRUE, NULL);
61
62 /* sleep to give amsemaphore_decrement() a chance to block (or not). */
63 g_usleep(G_USEC_PER_SEC / 4);
64
65 /* and then increment the semaphore enough that the decrement can succeed */
66 data.increment_called = TRUE;
67 amsemaphore_increment(data.sem, 10);
68
69 /* join the thread and see how it fared. */
70 rv = GPOINTER_TO_INT(g_thread_join(th));
71
72 amsemaphore_free(data.sem);
73
74 return (rv == 1);
75 }
76
77
78 /*
79 * test that amsemaphore_wait_empty waits properly
80 */
81
82 static gpointer
test_wait_empty_thread(gpointer datap)83 test_wait_empty_thread(gpointer datap)
84 {
85 amsemaphore_t *sem = datap;
86
87 /* should block */
88 amsemaphore_decrement(sem, 20);
89
90 /* value should be 10 now (decremented from 30) */
91 if (sem->value != 10)
92 return GINT_TO_POINTER(1);
93
94 /* sleep for a bit */
95 g_usleep(G_USEC_PER_SEC / 4);
96
97 /* decrement those last 10, which should trigger the zero */
98 amsemaphore_decrement(sem, 10);
99
100 return GINT_TO_POINTER(0);
101 }
102
103 static gboolean
test_wait_empty(void)104 test_wait_empty(void)
105 {
106 GThread *th;
107 amsemaphore_t *sem = amsemaphore_new_with_value(10);
108 int rv;
109
110 th = g_thread_create(test_wait_empty_thread, (gpointer)sem, TRUE, NULL);
111
112 /* sleep to give amsemaphore_decrement() a chance to block (or not). */
113 g_usleep(G_USEC_PER_SEC / 4);
114
115 /* add another 10, so decrement can hit zero next time it's called */
116 amsemaphore_increment(sem, 10);
117
118 /* and wait on the semaphore emptying */
119 amsemaphore_wait_empty(sem);
120
121 /* join the thread and see how it fared. */
122 rv = GPOINTER_TO_INT(g_thread_join(th));
123
124 amsemaphore_free(sem);
125
126 return (rv == 1);
127 }
128
129 /*
130 * test that amsemaphore_force_adjust correctly wakes both
131 * amsemaphore_decrement and amsemaphore_wait_empty.
132 */
133
134 static gpointer
test_force_adjust_thread(gpointer datap)135 test_force_adjust_thread(gpointer datap)
136 {
137 amsemaphore_t *sem = datap;
138
139 /* this should block */
140 amsemaphore_decrement(sem, 20);
141
142 /* and this should block, too - it's fun */
143 amsemaphore_wait_empty(sem);
144
145 return NULL;
146 }
147
148 static gboolean
test_force_adjust(void)149 test_force_adjust(void)
150 {
151 GThread *th;
152 amsemaphore_t *sem = amsemaphore_new_with_value(10);
153
154 th = g_thread_create(test_force_adjust_thread, (gpointer)sem, TRUE, NULL);
155
156 /* sleep to give amsemaphore_decrement() a chance to block (or not). */
157 g_usleep(G_USEC_PER_SEC / 4);
158
159 /* add another 20, so decrement can proceed, but leave the value at 10 */
160 amsemaphore_force_adjust(sem, 20);
161
162 /* sleep to give amsemaphore_wait_empty() a chance to block (or not). */
163 g_usleep(G_USEC_PER_SEC / 4);
164
165 /* and empty out the semaphore */
166 amsemaphore_force_adjust(sem, -10);
167
168 g_thread_join(th);
169
170 amsemaphore_free(sem);
171
172 /* it we didn't hang yet, it's all good */
173 return TRUE;
174 }
175
176 /*
177 * test that amsemaphore_force_set correctly wakes both
178 * amsemaphore_decrement and amsemaphore_wait_empty.
179 */
180
181 static gpointer
test_force_set_thread(gpointer datap)182 test_force_set_thread(gpointer datap)
183 {
184 amsemaphore_t *sem = datap;
185
186 /* this should block */
187 amsemaphore_decrement(sem, 20);
188
189 /* and this should block, too - it's fun */
190 amsemaphore_wait_empty(sem);
191
192 return NULL;
193 }
194
195 static gboolean
test_force_set(void)196 test_force_set(void)
197 {
198 GThread *th;
199 amsemaphore_t *sem = amsemaphore_new_with_value(10);
200
201 th = g_thread_create(test_force_set_thread, (gpointer)sem, TRUE, NULL);
202
203 /* sleep to give amsemaphore_decrement() a chance to block (or not). */
204 g_usleep(G_USEC_PER_SEC / 4);
205
206 /* set it to 30, so decrement can proceed, but leave the value at 10 */
207 amsemaphore_force_set(sem, 30);
208
209 /* sleep to give amsemaphore_wait_empty() a chance to block (or not). */
210 g_usleep(G_USEC_PER_SEC / 4);
211
212 /* and empty out the semaphore */
213 amsemaphore_force_set(sem, 0);
214
215 g_thread_join(th);
216
217 amsemaphore_free(sem);
218
219 /* it we didn't hang yet, it's all good */
220 return TRUE;
221 }
222
223 /*
224 * Main loop
225 */
226
227 int
main(int argc,char ** argv)228 main(int argc, char **argv)
229 {
230 #if defined(G_THREADS_ENABLED) && !defined(G_THREADS_IMPL_NONE)
231 static TestUtilsTest tests[] = {
232 TU_TEST(test_decr_wait, 90),
233 TU_TEST(test_wait_empty, 90),
234 TU_TEST(test_force_adjust, 90),
235 TU_TEST(test_force_set, 90),
236 TU_END()
237 };
238
239 glib_init();
240
241 return testutils_run_tests(argc, argv, tests);
242 #else
243 g_fprintf(stderr, "No thread support on this platform -- nothing to test\n");
244 return 0;
245 #endif
246 }
247