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    void *ref = "little piggy";
88    volatile void *target_ptr = ref;
89    void *old_ptr;
90
91    old_ptr = apr_atomic_xchgptr(&target_ptr, &a);
92    ABTS_PTR_EQUAL(tc, ref, old_ptr);
93    ABTS_PTR_EQUAL(tc, &a, (void *) target_ptr);
94}
95
96static void test_cas_equal(abts_case *tc, void *data)
97{
98    apr_uint32_t casval = 0;
99    apr_uint32_t oldval;
100
101    oldval = apr_atomic_cas32(&casval, 12, 0);
102    ABTS_INT_EQUAL(tc, 0, oldval);
103    ABTS_INT_EQUAL(tc, 12, casval);
104}
105
106static void test_cas_equal_nonnull(abts_case *tc, void *data)
107{
108    apr_uint32_t casval = 12;
109    apr_uint32_t oldval;
110
111    oldval = apr_atomic_cas32(&casval, 23, 12);
112    ABTS_INT_EQUAL(tc, 12, oldval);
113    ABTS_INT_EQUAL(tc, 23, casval);
114}
115
116static void test_cas_notequal(abts_case *tc, void *data)
117{
118    apr_uint32_t casval = 12;
119    apr_uint32_t oldval;
120
121    oldval = apr_atomic_cas32(&casval, 23, 2);
122    ABTS_INT_EQUAL(tc, 12, oldval);
123    ABTS_INT_EQUAL(tc, 12, casval);
124}
125
126static void test_casptr_equal(abts_case *tc, void *data)
127{
128    int a;
129    volatile void *target_ptr = NULL;
130    void *old_ptr;
131
132    old_ptr = apr_atomic_casptr(&target_ptr, &a, NULL);
133    ABTS_PTR_EQUAL(tc, NULL, old_ptr);
134    ABTS_PTR_EQUAL(tc, &a, (void *) target_ptr);
135}
136
137static void test_casptr_equal_nonnull(abts_case *tc, void *data)
138{
139    int a, b;
140    volatile void *target_ptr = &a;
141    void *old_ptr;
142
143    old_ptr = apr_atomic_casptr(&target_ptr, &b, &a);
144    ABTS_PTR_EQUAL(tc, &a, old_ptr);
145    ABTS_PTR_EQUAL(tc, &b, (void *) target_ptr);
146}
147
148static void test_casptr_notequal(abts_case *tc, void *data)
149{
150    int a, b;
151    volatile void *target_ptr = &a;
152    void *old_ptr;
153
154    old_ptr = apr_atomic_casptr(&target_ptr, &a, &b);
155    ABTS_PTR_EQUAL(tc, &a, old_ptr);
156    ABTS_PTR_EQUAL(tc, &a, (void *) target_ptr);
157}
158
159static void test_add32(abts_case *tc, void *data)
160{
161    apr_uint32_t oldval;
162    apr_uint32_t y32;
163
164    apr_atomic_set32(&y32, 23);
165    oldval = apr_atomic_add32(&y32, 4);
166    ABTS_INT_EQUAL(tc, 23, oldval);
167    ABTS_INT_EQUAL(tc, 27, y32);
168}
169
170static void test_inc32(abts_case *tc, void *data)
171{
172    apr_uint32_t oldval;
173    apr_uint32_t y32;
174
175    apr_atomic_set32(&y32, 23);
176    oldval = apr_atomic_inc32(&y32);
177    ABTS_INT_EQUAL(tc, 23, oldval);
178    ABTS_INT_EQUAL(tc, 24, y32);
179}
180
181static void test_set_add_inc_sub(abts_case *tc, void *data)
182{
183    apr_uint32_t y32;
184
185    apr_atomic_set32(&y32, 0);
186    apr_atomic_add32(&y32, 20);
187    apr_atomic_inc32(&y32);
188    apr_atomic_sub32(&y32, 10);
189
190    ABTS_INT_EQUAL(tc, 11, y32);
191}
192
193static void test_wrap_zero(abts_case *tc, void *data)
194{
195    apr_uint32_t y32;
196    apr_uint32_t rv;
197    apr_uint32_t minus1 = -1;
198    char *str;
199
200    apr_atomic_set32(&y32, 0);
201    rv = apr_atomic_dec32(&y32);
202
203    ABTS_ASSERT(tc, "apr_atomic_dec32 on zero returned zero.", rv != 0);
204    str = apr_psprintf(p, "zero wrap failed: 0 - 1 = %d", y32);
205    ABTS_ASSERT(tc, str, y32 == minus1);
206}
207
208static void test_inc_neg1(abts_case *tc, void *data)
209{
210    apr_uint32_t y32 = -1;
211    apr_uint32_t minus1 = -1;
212    apr_uint32_t rv;
213    char *str;
214
215    rv = apr_atomic_inc32(&y32);
216
217    ABTS_ASSERT(tc, "apr_atomic_inc32 didn't return the old value.", rv == minus1);
218    str = apr_psprintf(p, "zero wrap failed: -1 + 1 = %d", y32);
219    ABTS_ASSERT(tc, str, y32 == 0);
220}
221
222
223#if APR_HAS_THREADS
224
225void *APR_THREAD_FUNC thread_func_mutex(apr_thread_t *thd, void *data);
226void *APR_THREAD_FUNC thread_func_atomic(apr_thread_t *thd, void *data);
227
228apr_thread_mutex_t *thread_lock;
229volatile apr_uint32_t mutex_locks = 0;
230volatile apr_uint32_t atomic_ops = 0;
231apr_status_t exit_ret_val = 123; /* just some made up number to check on later */
232
233#define NUM_THREADS 40
234#define NUM_ITERATIONS 20000
235
236void *APR_THREAD_FUNC thread_func_mutex(apr_thread_t *thd, void *data)
237{
238    int i;
239
240    for (i = 0; i < NUM_ITERATIONS; i++) {
241        apr_thread_mutex_lock(thread_lock);
242        mutex_locks++;
243        apr_thread_mutex_unlock(thread_lock);
244    }
245    apr_thread_exit(thd, exit_ret_val);
246    return NULL;
247}
248
249void *APR_THREAD_FUNC thread_func_atomic(apr_thread_t *thd, void *data)
250{
251    int i;
252
253    for (i = 0; i < NUM_ITERATIONS ; i++) {
254        apr_atomic_inc32(&atomic_ops);
255        apr_atomic_add32(&atomic_ops, 2);
256        apr_atomic_dec32(&atomic_ops);
257        apr_atomic_dec32(&atomic_ops);
258    }
259    apr_thread_exit(thd, exit_ret_val);
260    return NULL;
261}
262
263static void test_atomics_threaded(abts_case *tc, void *data)
264{
265    apr_thread_t *t1[NUM_THREADS];
266    apr_thread_t *t2[NUM_THREADS];
267    apr_status_t rv;
268    int i;
269
270#ifdef HAVE_PTHREAD_SETCONCURRENCY
271    pthread_setconcurrency(8);
272#endif
273
274    rv = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_DEFAULT, p);
275    APR_ASSERT_SUCCESS(tc, "Could not create lock", rv);
276
277    for (i = 0; i < NUM_THREADS; i++) {
278        apr_status_t r1, r2;
279        r1 = apr_thread_create(&t1[i], NULL, thread_func_mutex, NULL, p);
280        r2 = apr_thread_create(&t2[i], NULL, thread_func_atomic, NULL, p);
281        ABTS_ASSERT(tc, "Failed creating threads", !r1 && !r2);
282    }
283
284    for (i = 0; i < NUM_THREADS; i++) {
285        apr_status_t s1, s2;
286        apr_thread_join(&s1, t1[i]);
287        apr_thread_join(&s2, t2[i]);
288
289        ABTS_ASSERT(tc, "Invalid return value from thread_join",
290                    s1 == exit_ret_val && s2 == exit_ret_val);
291    }
292
293    ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS, mutex_locks);
294    ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS,
295                   apr_atomic_read32(&atomic_ops));
296
297    rv = apr_thread_mutex_destroy(thread_lock);
298    ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS);
299}
300
301#undef NUM_THREADS
302#define NUM_THREADS 7
303
304typedef struct tbox_t tbox_t;
305
306struct tbox_t {
307    abts_case *tc;
308    apr_uint32_t *mem;
309    apr_uint32_t preval;
310    apr_uint32_t postval;
311    apr_uint32_t loop;
312    void (*func)(tbox_t *box);
313};
314
315static APR_INLINE void busyloop_read32(tbox_t *tbox)
316{
317    apr_uint32_t val;
318
319    do {
320        val = apr_atomic_read32(tbox->mem);
321
322        if (val != tbox->preval)
323            apr_thread_yield();
324        else
325            break;
326    } while (1);
327}
328
329static void busyloop_set32(tbox_t *tbox)
330{
331    do {
332        busyloop_read32(tbox);
333        apr_atomic_set32(tbox->mem, tbox->postval);
334    } while (--tbox->loop);
335}
336
337static void busyloop_add32(tbox_t *tbox)
338{
339    apr_uint32_t val;
340
341    do {
342        busyloop_read32(tbox);
343        val = apr_atomic_add32(tbox->mem, tbox->postval);
344        apr_thread_mutex_lock(thread_lock);
345        ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
346        apr_thread_mutex_unlock(thread_lock);
347    } while (--tbox->loop);
348}
349
350static void busyloop_sub32(tbox_t *tbox)
351{
352    do {
353        busyloop_read32(tbox);
354        apr_atomic_sub32(tbox->mem, tbox->postval);
355    } while (--tbox->loop);
356}
357
358static void busyloop_inc32(tbox_t *tbox)
359{
360    apr_uint32_t val;
361
362    do {
363        busyloop_read32(tbox);
364        val = apr_atomic_inc32(tbox->mem);
365        apr_thread_mutex_lock(thread_lock);
366        ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
367        apr_thread_mutex_unlock(thread_lock);
368    } while (--tbox->loop);
369}
370
371static void busyloop_dec32(tbox_t *tbox)
372{
373    apr_uint32_t val;
374
375    do {
376        busyloop_read32(tbox);
377        val = apr_atomic_dec32(tbox->mem);
378        apr_thread_mutex_lock(thread_lock);
379        ABTS_INT_NEQUAL(tbox->tc, 0, val);
380        apr_thread_mutex_unlock(thread_lock);
381    } while (--tbox->loop);
382}
383
384static void busyloop_cas32(tbox_t *tbox)
385{
386    apr_uint32_t val;
387
388    do {
389        do {
390            val = apr_atomic_cas32(tbox->mem, tbox->postval, tbox->preval);
391
392            if (val != tbox->preval)
393                apr_thread_yield();
394            else
395                break;
396        } while (1);
397    } while (--tbox->loop);
398}
399
400static void busyloop_xchg32(tbox_t *tbox)
401{
402    apr_uint32_t val;
403
404    do {
405        busyloop_read32(tbox);
406        val = apr_atomic_xchg32(tbox->mem, tbox->postval);
407        apr_thread_mutex_lock(thread_lock);
408        ABTS_INT_EQUAL(tbox->tc, val, tbox->preval);
409        apr_thread_mutex_unlock(thread_lock);
410    } while (--tbox->loop);
411}
412
413static void *APR_THREAD_FUNC thread_func_busyloop(apr_thread_t *thd, void *data)
414{
415    tbox_t *tbox = data;
416
417    tbox->func(tbox);
418
419    apr_thread_exit(thd, 0);
420
421    return NULL;
422}
423
424static void test_atomics_busyloop_threaded(abts_case *tc, void *data)
425{
426    unsigned int i;
427    apr_status_t rv;
428    apr_uint32_t count = 0;
429    tbox_t tbox[NUM_THREADS];
430    apr_thread_t *thread[NUM_THREADS];
431
432    rv = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_DEFAULT, p);
433    APR_ASSERT_SUCCESS(tc, "Could not create lock", rv);
434
435    /* get ready */
436    for (i = 0; i < NUM_THREADS; i++) {
437        tbox[i].tc = tc;
438        tbox[i].mem = &count;
439        tbox[i].loop = 50;
440    }
441
442    tbox[0].preval = 98;
443    tbox[0].postval = 3891;
444    tbox[0].func = busyloop_add32;
445
446    tbox[1].preval = 3989;
447    tbox[1].postval = 1010;
448    tbox[1].func = busyloop_sub32;
449
450    tbox[2].preval = 2979;
451    tbox[2].postval = 0; /* not used */
452    tbox[2].func = busyloop_inc32;
453
454    tbox[3].preval = 2980;
455    tbox[3].postval = 16384;
456    tbox[3].func = busyloop_set32;
457
458    tbox[4].preval = 16384;
459    tbox[4].postval = 0; /* not used */
460    tbox[4].func = busyloop_dec32;
461
462    tbox[5].preval = 16383;
463    tbox[5].postval = 1048576;
464    tbox[5].func = busyloop_cas32;
465
466    tbox[6].preval = 1048576;
467    tbox[6].postval = 98; /* goto tbox[0] */
468    tbox[6].func = busyloop_xchg32;
469
470    /* get set */
471    for (i = 0; i < NUM_THREADS; i++) {
472        rv = apr_thread_create(&thread[i], NULL, thread_func_busyloop,
473                               &tbox[i], p);
474        ABTS_ASSERT(tc, "Failed creating thread", rv == APR_SUCCESS);
475    }
476
477    /* go! */
478    apr_atomic_set32(tbox->mem, 98);
479
480    for (i = 0; i < NUM_THREADS; i++) {
481        apr_status_t retval;
482        rv = apr_thread_join(&retval, thread[i]);
483        ABTS_ASSERT(tc, "Thread join failed", rv == APR_SUCCESS);
484        ABTS_ASSERT(tc, "Invalid return value from thread_join", retval == 0);
485    }
486
487    ABTS_INT_EQUAL(tbox->tc, 98, count);
488
489    rv = apr_thread_mutex_destroy(thread_lock);
490    ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS);
491}
492
493#endif /* !APR_HAS_THREADS */
494
495abts_suite *testatomic(abts_suite *suite)
496{
497    suite = ADD_SUITE(suite)
498
499    abts_run_test(suite, test_init, NULL);
500    abts_run_test(suite, test_set32, NULL);
501    abts_run_test(suite, test_read32, NULL);
502    abts_run_test(suite, test_dec32, NULL);
503    abts_run_test(suite, test_xchg32, NULL);
504    abts_run_test(suite, test_xchgptr, NULL);
505    abts_run_test(suite, test_cas_equal, NULL);
506    abts_run_test(suite, test_cas_equal_nonnull, NULL);
507    abts_run_test(suite, test_cas_notequal, NULL);
508    abts_run_test(suite, test_casptr_equal, NULL);
509    abts_run_test(suite, test_casptr_equal_nonnull, NULL);
510    abts_run_test(suite, test_casptr_notequal, NULL);
511    abts_run_test(suite, test_add32, NULL);
512    abts_run_test(suite, test_inc32, NULL);
513    abts_run_test(suite, test_set_add_inc_sub, NULL);
514    abts_run_test(suite, test_wrap_zero, NULL);
515    abts_run_test(suite, test_inc_neg1, NULL);
516
517#if APR_HAS_THREADS
518    abts_run_test(suite, test_atomics_threaded, NULL);
519    abts_run_test(suite, test_atomics_busyloop_threaded, NULL);
520#endif
521
522    return suite;
523}
524
525