Line data Source code
1 : /* t-lock.c - Check the lock functions
2 : * Copyright (C) 2013, 2015 g10 Code GmbH
3 : *
4 : * This file is part of libgpg-error.
5 : *
6 : * libgpg-error is free software; you can redistribute it and/or
7 : * modify it under the terms of the GNU Lesser General Public License
8 : * as published by the Free Software Foundation; either version 2.1 of
9 : * the License, or (at your option) any later version.
10 : *
11 : * libgpg-error is distributed in the hope that it will be useful, but
12 : * WITHOUT ANY WARRANTY; without even the implied warranty of
13 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 : * Lesser General Public License for more details.
15 : *
16 : * You should have received a copy of the GNU Lesser General Public
17 : * License along with this program; if not, see <https://www.gnu.org/licenses/>.
18 : */
19 :
20 : #if HAVE_CONFIG_H
21 : # include <config.h>
22 : #endif
23 :
24 : #include <stdio.h>
25 : #include <stdlib.h>
26 : #include <string.h>
27 : #include <assert.h>
28 : #include <sys/types.h>
29 : #include <unistd.h>
30 : #ifdef _WIN32
31 : # include <windows.h>
32 : # include <time.h>
33 : #else
34 : # ifdef USE_POSIX_THREADS
35 : # include <pthread.h>
36 : # endif
37 : #endif
38 :
39 : #define PGM "t-lock"
40 :
41 : #include "t-common.h"
42 :
43 : #ifdef _WIN32
44 : # define THREAD_RET_TYPE DWORD WINAPI
45 : # define THREAD_RET_VALUE 0
46 : #else
47 : # define THREAD_RET_TYPE void *
48 : # define THREAD_RET_VALUE NULL
49 : #endif
50 :
51 :
52 : /* Our tests works by having a a couple of accountant threads which do
53 : random transactions between accounts and a revision threads which
54 : checks that the balance of all accounts is invariant. The idea for
55 : this check is due to Bruno Haible. */
56 : #define N_ACCOUNT 8
57 : #define ACCOUNT_VALUE 42
58 : static int account[N_ACCOUNT];
59 : GPGRT_LOCK_DEFINE (accounts_lock);
60 :
61 : /* Number of transactions done by each accountant. */
62 : #define N_TRANSACTIONS 1000
63 :
64 : /* Number of accountants to run. */
65 : #define N_ACCOUNTANTS 5
66 :
67 : /* Maximum transaction value. A quite low value is used so that we
68 : would get an integer overflow. */
69 : #define MAX_TRANSACTION_VALUE 50
70 :
71 : /* Flag to tell the revision thread to finish. */
72 : static volatile int stop_revision_thread;
73 :
74 :
75 : /* Initialze all accounts. */
76 : static void
77 : init_accounts (void)
78 : {
79 : int i;
80 :
81 8 : for (i=0; i < N_ACCOUNT; i++)
82 8 : account[i] = ACCOUNT_VALUE;
83 : }
84 :
85 :
86 : /* Check that the sum of all accounts matches the initial sum. */
87 : static void
88 9163 : check_accounts (void)
89 : {
90 : int i, sum;
91 :
92 : sum = 0;
93 82467 : for (i = 0; i < N_ACCOUNT; i++)
94 73304 : sum += account[i];
95 9163 : if (sum != N_ACCOUNT * ACCOUNT_VALUE)
96 0 : die ("accounts out of balance");
97 9163 : }
98 :
99 :
100 : static void
101 : print_accounts (void)
102 : {
103 : int i;
104 :
105 0 : for (i=0; i < N_ACCOUNT; i++)
106 0 : printf ("account %d: %6d\n", i, account[i]);
107 : }
108 :
109 :
110 : #if defined(_WIN32) || defined(USE_POSIX_THREADS)
111 : /* Get a a random integer value in the range 0 to HIGH. */
112 : static unsigned int
113 : get_rand (int high)
114 : {
115 15000 : return (unsigned int)(1+(int)((double)(high+1)*rand ()/(RAND_MAX+1.0))) - 1;
116 : }
117 :
118 :
119 : /* Pick a random account. Note that this function is not
120 : thread-safe. */
121 : static int
122 : pick_account (void)
123 : {
124 : return get_rand (N_ACCOUNT - 1);
125 : }
126 :
127 :
128 : /* Pick a random value for a transaction. This is not thread-safe. */
129 : static int
130 : pick_value (void)
131 : {
132 : return get_rand (MAX_TRANSACTION_VALUE);
133 : }
134 :
135 :
136 : /* This is the revision department. */
137 : static THREAD_RET_TYPE
138 3 : revision_thread (void *arg)
139 : {
140 : gpg_err_code_t rc;
141 : int i = 0;
142 :
143 : (void)arg;
144 :
145 9165 : while (!stop_revision_thread)
146 : {
147 9159 : rc = gpgrt_lock_lock (&accounts_lock);
148 9159 : if (rc)
149 0 : fail ("gpgrt_lock_lock failed at %d: %s", __LINE__, gpg_strerror (rc));
150 :
151 9159 : check_accounts ();
152 9159 : rc = gpgrt_lock_unlock (&accounts_lock);
153 9159 : if (rc)
154 0 : fail ("gpgrt_lock_unlock failed at %d: %s", __LINE__,gpg_strerror (rc));
155 9159 : if (!(++i%7))
156 1307 : gpgrt_yield ();
157 : }
158 3 : return THREAD_RET_VALUE;
159 : }
160 :
161 :
162 : /* This is one of our accountants. */
163 : static THREAD_RET_TYPE
164 17 : accountant_thread (void *arg)
165 : {
166 : gpg_err_code_t rc;
167 : int i;
168 : int acc1, acc2;
169 : int value;
170 :
171 : (void)arg;
172 :
173 : #ifdef _WIN32
174 : srand (time(NULL)*getpid()); /* Windows needs it per thread. */
175 : #endif
176 14756 : for (i = 0; i < N_TRANSACTIONS; i++)
177 : {
178 14741 : rc = gpgrt_lock_lock (&accounts_lock);
179 15000 : if (rc)
180 0 : fail ("gpgrt_lock_lock failed at %d: %s", __LINE__, gpg_strerror (rc));
181 :
182 : acc1 = pick_account ();
183 : acc2 = pick_account ();
184 : value = pick_value ();
185 15000 : account[acc1] += value;
186 15000 : account[acc2] -= value;
187 :
188 15000 : rc = gpgrt_lock_unlock (&accounts_lock);
189 14751 : if (rc)
190 0 : fail ("gpgrt_lock_unlock failed at %d: %s", __LINE__,gpg_strerror (rc));
191 14735 : if (i && !(i%8))
192 1853 : gpgrt_yield ();
193 : }
194 15 : return THREAD_RET_VALUE;
195 : }
196 : #endif /*_WIN32||USE_POSIX_THREADS*/
197 :
198 :
199 : static void
200 3 : run_test (void)
201 : {
202 : #ifdef _WIN32
203 : HANDLE rthread;
204 : HANDLE athreads[N_ACCOUNTANTS];
205 : int i;
206 : int rc;
207 :
208 : stop_revision_thread = 0;
209 : rthread = CreateThread (NULL, 0, revision_thread, NULL, 0, NULL);
210 : if (!rthread)
211 : die ("error creating revision thread: rc=%d", (int)GetLastError ());
212 :
213 : for (i=0; i < N_ACCOUNTANTS; i++)
214 : {
215 : athreads[i] = CreateThread (NULL, 0, accountant_thread, NULL, 0, NULL);
216 : if (!athreads[i])
217 : die ("error creating accountant thread %d: rc=%d",
218 : i, (int)GetLastError ());
219 : }
220 :
221 : for (i=0; i < N_ACCOUNTANTS; i++)
222 : {
223 : rc = WaitForSingleObject (athreads[i], INFINITE);
224 : if (rc == WAIT_OBJECT_0)
225 : show ("accountant thread %d has terminated", i);
226 : else
227 : fail ("waiting for accountant thread %d failed: %d",
228 : i, (int)GetLastError ());
229 : CloseHandle (athreads[i]);
230 : }
231 : stop_revision_thread = 1;
232 :
233 : rc = WaitForSingleObject (rthread, INFINITE);
234 : if (rc == WAIT_OBJECT_0)
235 : show ("revision thread has terminated");
236 : else
237 : fail ("waiting for revision thread failed: %d", (int)GetLastError ());
238 : CloseHandle (rthread);
239 :
240 : #else /*!_WIN32*/
241 : # ifdef USE_POSIX_THREADS
242 : pthread_t rthread;
243 : pthread_t athreads[N_ACCOUNTANTS];
244 : int i;
245 :
246 3 : stop_revision_thread = 0;
247 3 : pthread_create (&rthread, NULL, revision_thread, NULL);
248 :
249 18 : for (i=0; i < N_ACCOUNTANTS; i++)
250 15 : pthread_create (&athreads[i], NULL, accountant_thread, NULL);
251 :
252 15 : for (i=0; i < N_ACCOUNTANTS; i++)
253 : {
254 15 : pthread_join (athreads[i], NULL);
255 15 : show ("accountant thread %d has terminated", i);
256 : }
257 :
258 3 : stop_revision_thread = 1;
259 3 : pthread_join (rthread, NULL);
260 3 : show ("revision thread has terminated");
261 : # else /*!USE_POSIX_THREADS*/
262 : verbose++;
263 : show ("no thread support - skipping test\n", PGM);
264 : verbose--;
265 : # endif /*!USE_POSIX_THREADS*/
266 : #endif /*!_WIN32*/
267 :
268 3 : gpgrt_lock_destroy (&accounts_lock);
269 3 : }
270 :
271 :
272 : int
273 1 : main (int argc, char **argv)
274 : {
275 : int last_argc = -1;
276 : int rc;
277 :
278 1 : if (argc)
279 : {
280 1 : argc--; argv++;
281 : }
282 1 : while (argc && last_argc != argc )
283 : {
284 : last_argc = argc;
285 0 : if (!strcmp (*argv, "--help"))
286 : {
287 0 : puts (
288 : "usage: ./t-lock [options]\n"
289 : "\n"
290 : "Options:\n"
291 : " --verbose Show what is going on\n"
292 : " --debug Flyswatter\n"
293 : );
294 0 : exit (0);
295 : }
296 0 : if (!strcmp (*argv, "--verbose"))
297 : {
298 0 : verbose = 1;
299 0 : argc--; argv++;
300 : }
301 0 : else if (!strcmp (*argv, "--debug"))
302 : {
303 0 : verbose = debug = 1;
304 0 : argc--; argv++;
305 : }
306 : }
307 :
308 1 : srand (time(NULL)*getpid());
309 :
310 1 : if (!gpg_error_check_version (GPG_ERROR_VERSION))
311 : {
312 0 : die ("gpg_error_check_version returned an error");
313 : errorcount++;
314 : }
315 :
316 : init_accounts ();
317 1 : check_accounts ();
318 1 : run_test ();
319 1 : check_accounts ();
320 : /* Run a second time to check deinit code. */
321 1 : run_test ();
322 1 : check_accounts ();
323 : /* And a third time to test an explicit init. */
324 1 : rc = gpgrt_lock_init (&accounts_lock);
325 1 : if (rc)
326 0 : fail ("gpgrt_lock_init failed at %d: %s", __LINE__, gpg_strerror (rc));
327 1 : run_test ();
328 1 : check_accounts ();
329 1 : if (verbose)
330 : print_accounts ();
331 :
332 1 : return errorcount ? 1 : 0;
333 : }
|