1/* Licensed to the Apache Software Foundation (ASF) under one or more
2 * contributor license agreements.  See the NOTICE file distributed with
3 * this work for additional information regarding copyright ownership.
4 * The ASF licenses this file to You under the Apache License, Version 2.0
5 * (the "License"); you may not use this file except in compliance with
6 * the License.  You may obtain a copy of the License at
7 *
8 *     http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include "testutil.h"
18#include "apr_strings.h"
19#include "apr_thread_proc.h"
20#include "apr_errno.h"
21#include "apr_general.h"
22#include "apr_atomic.h"
23#include "apr_time.h"
24
25/* Use pthread_setconcurrency where it is available and not a nullop,
26 * i.e. platforms using M:N or M:1 thread models: */
27#if APR_HAS_THREADS && \
28   ((defined(SOLARIS2) && SOLARIS2 > 6) || defined(_AIX))
29/* also HP-UX, IRIX? ... */
30#define HAVE_PTHREAD_SETCONCURRENCY
31#endif
32
33#ifdef HAVE_PTHREAD_SETCONCURRENCY
34#include <pthread.h>
35#endif
36
37static void test_init(abts_case *tc, void *data)
38{
39    APR_ASSERT_SUCCESS(tc, "Could not initliaze atomics", apr_atomic_init(p));
40}
41
42static void test_set32(abts_case *tc, void *data)
43{
44    apr_uint32_t y32;
45    apr_atomic_set32(&y32, 2);
46    ABTS_INT_EQUAL(tc, 2, y32);
47}
48
49static void test_read32(abts_case *tc, void *data)
50{
51    apr_uint32_t y32;
52    apr_atomic_set32(&y32, 2);
53    ABTS_INT_EQUAL(tc, 2, apr_atomic_read32(&y32));
54}
55
56static void test_dec32(abts_case *tc, void *data)
57{
58    apr_uint32_t y32;
59    int rv;
60
61    apr_atomic_set32(&y32, 2);
62
63    rv = apr_atomic_dec32(&y32);
64    ABTS_INT_EQUAL(tc, 1, y32);
65    ABTS_ASSERT(tc, "atomic_dec returned zero when it shouldn't", rv != 0);
66
67    rv = apr_atomic_dec32(&y32);
68    ABTS_INT_EQUAL(tc, 0, y32);
69    ABTS_ASSERT(tc, "atomic_dec didn't returned zero when it should", rv == 0);
70}
71
72static void test_xchg32(abts_case *tc, void *data)
73{
74    apr_uint32_t oldval;
75    apr_uint32_t y32;
76
77    apr_atomic_set32(&y32, 100);
78    oldval = apr_atomic_xchg32(&y32, 50);
79
80    ABTS_INT_EQUAL(tc, 100, oldval);
81    ABTS_INT_EQUAL(tc, 50, y32);
82}
83
84static void test_xchgptr(abts_case *tc, void *data)
85{
86    int a;
87    volatile void *target_ptr = NULL;
88    void *old_ptr;
89
90    old_ptr = apr_atomic_xchgptr(&target_ptr, &a);
91    ABTS_PTR_EQUAL(tc, NULL, old_ptr);
92    ABTS_PTR_EQUAL(tc, &a, (void *) target_ptr);
93}
94
95static void test_cas_equal(abts_case *tc, void *data)
96{
97    apr_uint32_t casval = 0;
98    apr_uint32_t oldval;
99
100    oldval = apr_atomic_cas32(&casval, 12, 0);
101    ABTS_INT_EQUAL(tc, 0, oldval);
102    ABTS_INT_EQUAL(tc, 12, casval);
103}
104
105static void test_cas_equal_nonnull(abts_case *tc, void *data)
106{
107    apr_uint32_t casval = 12;
108    apr_uint32_t oldval;
109
110    oldval = apr_atomic_cas32(&casval, 23, 12);
111    ABTS_INT_EQUAL(tc, 12, oldval);
112    ABTS_INT_EQUAL(tc, 23, casval);
113}
114
115static void test_cas_notequal(abts_case *tc, void *data)
116{
117    apr_uint32_t casval = 12;
118    apr_uint32_t oldval;
119
120    oldval = apr_atomic_cas32(&casval, 23, 2);
121    ABTS_INT_EQUAL(tc, 12, oldval);
122    ABTS_INT_EQUAL(tc, 12, casval);
123}
124
125static void test_casptr_equal(abts_case *tc, void *data)
126{
127    int a;
128    volatile void *target_ptr = NULL;
129    void *old_ptr;
130
131    old_ptr = apr_atomic_casptr(&target_ptr, &a, NULL);
132    ABTS_PTR_EQUAL(tc, NULL, old_ptr);
133    ABTS_PTR_EQUAL(tc, &a, (void *) target_ptr);
134}
135
136static void test_casptr_equal_nonnull(abts_case *tc, void *data)
137{
138    int a, b;
139    volatile void *target_ptr = &a;
140    void *old_ptr;
141
142    old_ptr = apr_atomic_casptr(&target_ptr, &b, &a);
143    ABTS_PTR_EQUAL(tc, &a, old_ptr);
144    ABTS_PTR_EQUAL(tc, &b, (void *) target_ptr);
145}
146
147static void test_casptr_notequal(abts_case *tc, void *data)
148{
149    int a, b;
150    volatile void *target_ptr = &a;
151    void *old_ptr;
152
153    old_ptr = apr_atomic_casptr(&target_ptr, &a, &b);
154    ABTS_PTR_EQUAL(tc, &a, old_ptr);
155    ABTS_PTR_EQUAL(tc, &a, (void *) target_ptr);
156}
157
158static void test_add32(abts_case *tc, void *data)
159{
160    apr_uint32_t oldval;
161    apr_uint32_t y32;
162
163    apr_atomic_set32(&y32, 23);
164    oldval = apr_atomic_add32(&y32, 4);
165    ABTS_INT_EQUAL(tc, 23, oldval);
166    ABTS_INT_EQUAL(tc, 27, y32);
167}
168
169static void test_inc32(abts_case *tc, void *data)
170{
171    apr_uint32_t oldval;
172    apr_uint32_t y32;
173
174    apr_atomic_set32(&y32, 23);
175    oldval = apr_atomic_inc32(&y32);
176    ABTS_INT_EQUAL(tc, 23, oldval);
177    ABTS_INT_EQUAL(tc, 24, y32);
178}
179
180static void test_set_add_inc_sub(abts_case *tc, void *data)
181{
182    apr_uint32_t y32;
183
184    apr_atomic_set32(&y32, 0);
185    apr_atomic_add32(&y32, 20);
186    apr_atomic_inc32(&y32);
187    apr_atomic_sub32(&y32, 10);
188
189    ABTS_INT_EQUAL(tc, 11, y32);
190}
191
192static void test_wrap_zero(abts_case *tc, void *data)
193{
194    apr_uint32_t y32;
195    apr_uint32_t rv;
196    apr_uint32_t minus1 = -1;
197    char *str;
198
199    apr_atomic_set32(&y32, 0);
200    rv = apr_atomic_dec32(&y32);
201
202    ABTS_ASSERT(tc, "apr_atomic_dec32 on zero returned zero.", rv != 0);
203    str = apr_psprintf(p, "zero wrap failed: 0 - 1 = %d", y32);
204    ABTS_ASSERT(tc, str, y32 == minus1);
205}
206
207static void test_inc_neg1(abts_case *tc, void *data)
208{
209    apr_uint32_t y32 = -1;
210    apr_uint32_t minus1 = -1;
211    apr_uint32_t rv;
212    char *str;
213
214    rv = apr_atomic_inc32(&y32);
215
216    ABTS_ASSERT(tc, "apr_atomic_inc32 didn't return the old value.", rv == minus1);
217    str = apr_psprintf(p, "zero wrap failed: -1 + 1 = %d", y32);
218    ABTS_ASSERT(tc, str, y32 == 0);
219}
220
221
222#if APR_HAS_THREADS
223
224void *APR_THREAD_FUNC thread_func_mutex(apr_thread_t *thd, void *data);
225void *APR_THREAD_FUNC thread_func_atomic(apr_thread_t *thd, void *data);
226
227apr_thread_mutex_t *thread_lock;
228volatile apr_uint32_t mutex_locks = 0;
229volatile apr_uint32_t atomic_ops = 0;
230apr_status_t exit_ret_val = 123; /* just some made up number to check on later */
231
232#define NUM_THREADS 40
233#define NUM_ITERATIONS 20000
234
235void *APR_THREAD_FUNC thread_func_mutex(apr_thread_t *thd, void *data)
236{
237    int i;
238
239    for (i = 0; i < NUM_ITERATIONS; i++) {
240        apr_thread_mutex_lock(thread_lock);
241        mutex_locks++;
242        apr_thread_mutex_unlock(thread_lock);
243    }
244    apr_thread_exit(thd, exit_ret_val);
245    return NULL;
246}
247
248void *APR_THREAD_FUNC thread_func_atomic(apr_thread_t *thd, void *data)
249{
250    int i;
251
252    for (i = 0; i < NUM_ITERATIONS ; i++) {
253        apr_atomic_inc32(&atomic_ops);
254        apr_atomic_add32(&atomic_ops, 2);
255        apr_atomic_dec32(&atomic_ops);
256        apr_atomic_dec32(&atomic_ops);
257    }
258    apr_thread_exit(thd, exit_ret_val);
259    return NULL;
260}
261
262static void test_atomics_threaded(abts_case *tc, void *data)
263{
264    apr_thread_t *t1[NUM_THREADS];
265    apr_thread_t *t2[NUM_THREADS];
266    apr_status_t rv;
267    int i;
268
269#ifdef HAVE_PTHREAD_SETCONCURRENCY
270    pthread_setconcurrency(8);
271#endif
272
273    rv = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_DEFAULT, p);
274    APR_ASSERT_SUCCESS(tc, "Could not create lock", rv);
275
276    for (i = 0; i < NUM_THREADS; i++) {
277        apr_status_t r1, r2;
278        r1 = apr_thread_create(&t1[i], NULL, thread_func_mutex, NULL, p);
279        r2 = apr_thread_create(&t2[i], NULL, thread_func_atomic, NULL, p);
280        ABTS_ASSERT(tc, "Failed creating threads", !r1 && !r2);
281    }
282
283    for (i = 0; i < NUM_THREADS; i++) {
284        apr_status_t s1, s2;
285        apr_thread_join(&s1, t1[i]);
286        apr_thread_join(&s2, t2[i]);
287
288        ABTS_ASSERT(tc, "Invalid return value from thread_join",
289                    s1 == exit_ret_val && s2 == exit_ret_val);
290    }
291
292    ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS, mutex_locks);
293    ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS,
294                   apr_atomic_read32(&atomic_ops));
295
296    rv = apr_thread_mutex_destroy(thread_lock);
297    ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS);
298}
299
300#undef NUM_THREADS
301#define NUM_THREADS 7
302
303typedef struct tbox_t tbox_t;
304
305struct tbox_t {
306    abts_case *tc;
307    apr_uint32_t *mem;
308    apr_uint32_t preval;
309    apr_uint32_t postval;
310    apr_uint32_t loop;
311    void (*func)(tbox_t *box);
312};
313
314static APR_INLINE void busyloop_read32(tbox_t *tbox)
315{
316    apr_uint32_t val;
317
318    do {
319        val = apr_atomic_read32(tbox->mem);
320
321        if (val != tbox->preval)
322            apr_thread_yield();
323        else
324            break;
325    } while (1);
326}
327
328static void busyloop_set32(tbox_t *tbox)
329{
330    do {
331        busyloop_read32(tbox);
332        apr_atomic_set32(tbox->mem, tbox->postval);
333    } while (--tbox->loop);
334}
335
336static void busyloop_add32(tbox_t *tbox)
337{
338    apr_uint32_t val;
339
340    do {
341        busyloop_read32(tbox);
342        val = apr_atomic_add32(tbox->mem, tbox->postval);
343        apr_thread_mutex_lock(thread_lock);
344        ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
345        apr_thread_mutex_unlock(thread_lock);
346    } while (--tbox->loop);
347}
348
349static void busyloop_sub32(tbox_t *tbox)
350{
351    do {
352        busyloop_read32(tbox);
353        apr_atomic_sub32(tbox->mem, tbox->postval);
354    } while (--tbox->loop);
355}
356
357static void busyloop_inc32(tbox_t *tbox)
358{
359    apr_uint32_t val;
360
361    do {
362        busyloop_read32(tbox);
363        val = apr_atomic_inc32(tbox->mem);
364        apr_thread_mutex_lock(thread_lock);
365        ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
366        apr_thread_mutex_unlock(thread_lock);
367    } while (--tbox->loop);
368}
369
370static void busyloop_dec32(tbox_t *tbox)
371{
372    apr_uint32_t val;
373
374    do {
375        busyloop_read32(tbox);
376        val = apr_atomic_dec32(tbox->mem);
377        apr_thread_mutex_lock(thread_lock);
378        ABTS_INT_NEQUAL(tbox->tc, 0, val);
379        apr_thread_mutex_unlock(thread_lock);
380    } while (--tbox->loop);
381}
382
383static void busyloop_cas32(tbox_t *tbox)
384{
385    apr_uint32_t val;
386
387    do {
388        do {
389            val = apr_atomic_cas32(tbox->mem, tbox->postval, tbox->preval);
390
391            if (val != tbox->preval)
392                apr_thread_yield();
393            else
394                break;
395        } while (1);
396    } while (--tbox->loop);
397}
398
399static void busyloop_xchg32(tbox_t *tbox)
400{
401    apr_uint32_t val;
402
403    do {
404        busyloop_read32(tbox);
405        val = apr_atomic_xchg32(tbox->mem, tbox->postval);
406        apr_thread_mutex_lock(thread_lock);
407        ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
408        apr_thread_mutex_unlock(thread_lock);
409    } while (--tbox->loop);
410}
411
412static void *APR_THREAD_FUNC thread_func_busyloop(apr_thread_t *thd, void *data)
413{
414    tbox_t *tbox = data;
415
416    tbox->func(tbox);
417
418    apr_thread_exit(thd, 0);
419
420    return NULL;
421}
422
423static void test_atomics_busyloop_threaded(abts_case *tc, void *data)
424{
425    unsigned int i;
426    apr_status_t rv;
427    apr_uint32_t count = 0;
428    tbox_t tbox[NUM_THREADS];
429    apr_thread_t *thread[NUM_THREADS];
430
431    rv = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_DEFAULT, p);
432    APR_ASSERT_SUCCESS(tc, "Could not create lock", rv);
433
434    /* get ready */
435    for (i = 0; i < NUM_THREADS; i++) {
436        tbox[i].tc = tc;
437        tbox[i].mem = &count;
438        tbox[i].loop = 50;
439    }
440
441    tbox[0].preval = 98;
442    tbox[0].postval = 3891;
443    tbox[0].func = busyloop_add32;
444
445    tbox[1].preval = 3989;
446    tbox[1].postval = 1010;
447    tbox[1].func = busyloop_sub32;
448
449    tbox[2].preval = 2979;
450    tbox[2].postval = 0; /* not used */
451    tbox[2].func = busyloop_inc32;
452
453    tbox[3].preval = 2980;
454    tbox[3].postval = 16384;
455    tbox[3].func = busyloop_set32;
456
457    tbox[4].preval = 16384;
458    tbox[4].postval = 0; /* not used */
459    tbox[4].func = busyloop_dec32;
460
461    tbox[5].preval = 16383;
462    tbox[5].postval = 1048576;
463    tbox[5].func = busyloop_cas32;
464
465    tbox[6].preval = 1048576;
466    tbox[6].postval = 98; /* goto tbox[0] */
467    tbox[6].func = busyloop_xchg32;
468
469    /* get set */
470    for (i = 0; i < NUM_THREADS; i++) {
471        rv = apr_thread_create(&thread[i], NULL, thread_func_busyloop,
472                               &tbox[i], p);
473        ABTS_ASSERT(tc, "Failed creating thread", rv == APR_SUCCESS);
474    }
475
476    /* go! */
477    apr_atomic_set32(tbox->mem, 98);
478
479    for (i = 0; i < NUM_THREADS; i++) {
480        apr_status_t retval;
481        rv = apr_thread_join(&retval, thread[i]);
482        ABTS_ASSERT(tc, "Thread join failed", rv == APR_SUCCESS);
483        ABTS_ASSERT(tc, "Invalid return value from thread_join", retval == 0);
484    }
485
486    ABTS_INT_EQUAL(tbox->tc, 98, count);
487
488    rv = apr_thread_mutex_destroy(thread_lock);
489    ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS);
490}
491
492#endif /* !APR_HAS_THREADS */
493
494abts_suite *testatomic(abts_suite *suite)
495{
496    suite = ADD_SUITE(suite)
497
498    abts_run_test(suite, test_init, NULL);
499    abts_run_test(suite, test_set32, NULL);
500    abts_run_test(suite, test_read32, NULL);
501    abts_run_test(suite, test_dec32, NULL);
502    abts_run_test(suite, test_xchg32, NULL);
503    abts_run_test(suite, test_xchgptr, NULL);
504    abts_run_test(suite, test_cas_equal, NULL);
505    abts_run_test(suite, test_cas_equal_nonnull, NULL);
506    abts_run_test(suite, test_cas_notequal, NULL);
507    abts_run_test(suite, test_casptr_equal, NULL);
508    abts_run_test(suite, test_casptr_equal_nonnull, NULL);
509    abts_run_test(suite, test_casptr_notequal, NULL);
510    abts_run_test(suite, test_add32, NULL);
511    abts_run_test(suite, test_inc32, NULL);
512    abts_run_test(suite, test_set_add_inc_sub, NULL);
513    abts_run_test(suite, test_wrap_zero, NULL);
514    abts_run_test(suite, test_inc_neg1, NULL);
515
516#if APR_HAS_THREADS
517    abts_run_test(suite, test_atomics_threaded, NULL);
518    abts_run_test(suite, test_atomics_busyloop_threaded, NULL);
519#endif
520
521    return suite;
522}
523
524