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