1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *  * Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 *  * Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in
12 *    the documentation and/or other materials provided with the
13 *    distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
16 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
17 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
18 * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19 * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
22 * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
25 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28#include <pthread.h>
29#include <errno.h>
30#include <string.h>
31#include <stdarg.h>
32#include <stdio.h>
33#include <stdlib.h>
34
35/* Posix states that EDEADLK should be returned in case a deadlock condition
36 * is detected with a PTHREAD_MUTEX_ERRORCHECK lock() or trylock(), but
37 * GLibc returns EBUSY instead.
38 */
39#ifdef HOST
40#  define ERRNO_PTHREAD_EDEADLK   EBUSY
41#else
42#  define ERRNO_PTHREAD_EDEADLK   EDEADLK
43#endif
44
45static void __attribute__((noreturn))
46panic(const char* func, const char* format, ...)
47{
48    va_list  args;
49    fprintf(stderr, "%s: ", func);
50    va_start(args, format);
51    vfprintf(stderr, format, args);
52    va_end(args);
53    fprintf(stderr, "\n");
54    exit(1);
55}
56
57#define  PANIC(...)   panic(__FUNCTION__,__VA_ARGS__)
58
59static void __attribute__((noreturn))
60error(int  errcode, const char* func, const char* format, ...)
61{
62    va_list  args;
63    fprintf(stderr, "%s: ", func);
64    va_start(args, format);
65    vfprintf(stderr, format, args);
66    va_end(args);
67    fprintf(stderr, " error=%d: %s\n", errcode, strerror(errcode));
68    exit(1);
69}
70
71/* return current time in seconds as floating point value */
72static double
73time_now(void)
74{
75    struct timespec ts[1];
76
77    clock_gettime(CLOCK_MONOTONIC, ts);
78    return (double)ts->tv_sec + ts->tv_nsec/1e9;
79}
80
81static void
82time_sleep(double  delay)
83{
84    struct timespec ts;
85    int             ret;
86
87    ts.tv_sec  = (time_t)delay;
88    ts.tv_nsec = (long)((delay - ts.tv_sec)*1e9);
89
90    do {
91        ret = nanosleep(&ts, &ts);
92    } while (ret < 0 && errno == EINTR);
93}
94
95#define  ERROR(errcode,...)   error((errcode),__FUNCTION__,__VA_ARGS__)
96
97#define  TZERO(cond)   \
98    { int _ret = (cond); if (_ret != 0) ERROR(_ret,"%d:%s", __LINE__, #cond); }
99
100#define  TTRUE(cond)   \
101    { if (!(cond)) PANIC("%d:%s", __LINE__, #cond); }
102
103#define  TFALSE(cond)   \
104    { if (!!(cond)) PANIC("%d:%s", __LINE__, #cond); }
105
106#define  TEXPECT_INT(cond,val) \
107    { int _ret = (cond); if (_ret != (val)) PANIC("%d:%s returned %d (%d expected)", __LINE__, #cond, _ret, (val)); }
108
109/* perform a simple init/lock/unlock/destroy test on a rwlock of given attributes */
110static void do_test_rwlock_rd1(pthread_rwlockattr_t *attr)
111{
112    int               ret;
113    pthread_rwlock_t  lock[1];
114
115    TZERO(pthread_rwlock_init(lock, attr));
116    TZERO(pthread_rwlock_rdlock(lock));
117    TZERO(pthread_rwlock_unlock(lock));
118    TZERO(pthread_rwlock_destroy(lock));
119}
120
121static void do_test_rwlock_wr1(pthread_rwlockattr_t *attr)
122{
123    int               ret;
124    pthread_rwlock_t  lock[1];
125
126    TZERO(pthread_rwlock_init(lock, attr));
127    TZERO(pthread_rwlock_wrlock(lock));
128    TZERO(pthread_rwlock_unlock(lock));
129    TZERO(pthread_rwlock_destroy(lock));
130}
131
132static void set_rwlockattr_shared(pthread_rwlockattr_t *attr, int shared)
133{
134    int  newshared;
135    TZERO(pthread_rwlockattr_setpshared(attr, shared));
136    newshared = ~shared;
137    TZERO(pthread_rwlockattr_getpshared(attr, &newshared));
138    TEXPECT_INT(newshared,shared);
139}
140
141/* simple init/lock/unlock/destroy on all rwlock types */
142static void do_test_1(void)
143{
144    int                  ret, type;
145    pthread_rwlockattr_t  attr[1];
146
147    do_test_rwlock_rd1(NULL);
148    do_test_rwlock_wr1(NULL);
149
150    /* non-shared version */
151
152    TZERO(pthread_rwlockattr_init(attr));
153
154    set_rwlockattr_shared(attr, PTHREAD_PROCESS_PRIVATE);
155    do_test_rwlock_rd1(attr);
156    do_test_rwlock_wr1(attr);
157
158    set_rwlockattr_shared(attr, PTHREAD_PROCESS_SHARED);
159    do_test_rwlock_rd1(attr);
160    do_test_rwlock_wr1(attr);
161
162    TZERO(pthread_rwlockattr_destroy(attr));
163}
164
165static void do_test_rwlock_rd2_rec(pthread_rwlockattr_t *attr)
166{
167    pthread_rwlock_t  lock[1];
168
169    TZERO(pthread_rwlock_init(lock, attr));
170    TZERO(pthread_rwlock_tryrdlock(lock));
171    TZERO(pthread_rwlock_unlock(lock));
172    TZERO(pthread_rwlock_destroy(lock));
173
174    TZERO(pthread_rwlock_init(lock, attr));
175    TZERO(pthread_rwlock_tryrdlock(lock));
176    TZERO(pthread_rwlock_tryrdlock(lock));
177    TZERO(pthread_rwlock_unlock(lock));
178    TZERO(pthread_rwlock_unlock(lock));
179    TZERO(pthread_rwlock_destroy(lock));
180}
181
182static void do_test_rwlock_wr2_rec(pthread_rwlockattr_t *attr)
183{
184    pthread_rwlock_t  lock[1];
185
186    TZERO(pthread_rwlock_init(lock, attr));
187    TZERO(pthread_rwlock_trywrlock(lock));
188    TZERO(pthread_rwlock_unlock(lock));
189    TZERO(pthread_rwlock_destroy(lock));
190
191    TZERO(pthread_rwlock_init(lock, attr));
192    TZERO(pthread_rwlock_trywrlock(lock));
193#ifdef HOST
194    /* The host implementation (GLibc) does not support recursive
195     * write locks */
196    TEXPECT_INT(pthread_rwlock_trywrlock(lock),EBUSY);
197#else
198    /* Our implementation supports recursive write locks ! */
199    TZERO(pthread_rwlock_trywrlock(lock));
200    TZERO(pthread_rwlock_unlock(lock));
201#endif
202    TZERO(pthread_rwlock_unlock(lock));
203    TZERO(pthread_rwlock_destroy(lock));
204}
205
206static void do_test_2(void)
207{
208    pthread_rwlockattr_t  attr[1];
209
210    do_test_rwlock_rd2_rec(NULL);
211    do_test_rwlock_wr2_rec(NULL);
212
213    /* non-shared version */
214
215    TZERO(pthread_rwlockattr_init(attr));
216
217    set_rwlockattr_shared(attr, PTHREAD_PROCESS_PRIVATE);
218    do_test_rwlock_rd2_rec(attr);
219    do_test_rwlock_wr2_rec(attr);
220
221    set_rwlockattr_shared(attr, PTHREAD_PROCESS_SHARED);
222    do_test_rwlock_rd2_rec(attr);
223    do_test_rwlock_wr2_rec(attr);
224
225    TZERO(pthread_rwlockattr_destroy(attr));
226}
227
228/* This is more complex example to test contention of rwlockes.
229 * Essentially, what happens is this:
230 *
231 * - main thread creates a rwlock and rdlocks it
232 * - it then creates thread 1 and thread 2
233 *
234 * - it then record the current time, sleep for a specific 'waitDelay'
235 *   then unlock the rwlock.
236 *
237 * - thread 1 tryrdlocks() the rwlock. It shall acquire the lock
238 *   immediately, then release it, then wrlock().
239 *
240 * - thread 2 trywrlocks() the rwlock. In case of failure (EBUSY), it waits
241 *   for a small amount of time (see 'spinDelay') and tries again, until
242 *   it succeeds. It then unlocks the rwlock.
243 *
244 * The goal of this test is to verify that thread 1 has been stopped
245 * for a sufficiently long time (in the wrlock), and that thread 2 has
246 * been spinning for the same minimum period. There is no guarantee as
247 * to which thread is going to acquire the rwlock first.
248 */
249typedef struct {
250    pthread_rwlock_t  rwlock[1];
251    double            t0;
252    double            waitDelay;
253    double            spinDelay;
254} Test3State;
255
256static void* do_rwlock_test_rd3_t1(void* arg)
257{
258    Test3State *s = arg;
259    double      t1;
260
261    /* try-acquire the lock, should succeed immediately */
262    TZERO(pthread_rwlock_tryrdlock(s->rwlock));
263    TZERO(pthread_rwlock_unlock(s->rwlock));
264
265    /* wrlock() the lock, now */
266    TZERO(pthread_rwlock_wrlock(s->rwlock));
267
268    t1 = time_now();
269    //DEBUG ONLY: printf("t1-s->t0=%g waitDelay=%g\n", t1-s->t0, s->waitDelay);
270    TTRUE((t1-s->t0) >= s->waitDelay);
271    TZERO(pthread_rwlock_unlock(s->rwlock));
272    return NULL;
273}
274
275static void* do_rwlock_test_rd3_t2(void* arg)
276{
277    Test3State *s = arg;
278    double      t1;
279
280    for (;;) {
281        int ret = pthread_rwlock_trywrlock(s->rwlock);
282        if (ret == 0)
283            break;
284        if (ret == EBUSY) {
285            time_sleep(s->spinDelay);
286            continue;
287        }
288    }
289    t1 = time_now();
290    TTRUE((t1-s->t0) >= s->waitDelay);
291    TZERO(pthread_rwlock_unlock(s->rwlock));
292    return NULL;
293}
294
295
296static void do_test_rwlock_rd3(pthread_rwlockattr_t *attr, double delay)
297{
298    Test3State  s[1];
299    pthread_t   th1, th2;
300    void*       dummy;
301
302    TZERO(pthread_rwlock_init(s->rwlock, attr));
303    s->waitDelay = delay;
304    s->spinDelay = delay/20.;
305
306    TZERO(pthread_rwlock_rdlock(s->rwlock));
307
308    pthread_create(&th1, NULL, do_rwlock_test_rd3_t1, s);
309    pthread_create(&th2, NULL, do_rwlock_test_rd3_t2, s);
310
311    s->t0 = time_now();
312    time_sleep(delay);
313
314    TZERO(pthread_rwlock_unlock(s->rwlock));
315
316    TZERO(pthread_join(th1, &dummy));
317    TZERO(pthread_join(th2, &dummy));
318}
319
320static void do_test_3(double  delay)
321{
322    pthread_rwlockattr_t  attr[1];
323
324    do_test_rwlock_rd3(NULL, delay);
325
326    /* non-shared version */
327
328    TZERO(pthread_rwlockattr_init(attr));
329
330    set_rwlockattr_shared(attr, PTHREAD_PROCESS_PRIVATE);
331    do_test_rwlock_rd3(attr, delay);
332
333    set_rwlockattr_shared(attr, PTHREAD_PROCESS_SHARED);
334    do_test_rwlock_rd3(attr, delay);
335
336    TZERO(pthread_rwlockattr_destroy(attr));
337}
338
339
340int main(void)
341{
342    do_test_1();
343    do_test_2();
344    do_test_3(0.1);
345    return 0;
346}
347