1/*
2 * Copyright (c) 2002, Intel Corporation. All rights reserved.
3 * This file is licensed under the GPL license.  For the full content
4 * of this license, see the COPYING file at the top level of this
5 * source tree.
6
7 * Test that pthread_rwlock_timedwrlock(pthread_rwlock_t *rwlock)
8 *
9 *	pthread_rwlock_timedwrlock() function shall apply a write lock to the
10 *	read-write lock referenced by rwlock as in the pthread_rwlock_wrlock() function.
11 *	However, if the lock cannot be acquired without waiting for other threads to
12 *	unlock the lock, this wait shall be terminate when the specified timeout expires.
13 *
14 * Steps:
15 * 1.  Initialize rwlock
16 * 2.  Main creats thread0.
17 * 3.  Thread0 does pthread_rwlock_timedwrlock(), should get the lock successfully then unlock.
18 * 4.  Main thread locks 'rwlock' for reading with pthread_rwlock_rdlock()
19 * 5.  Create a child thread, the thread time-locks 'rwlock' for writing,
20 *	using pthread_rwlock_timedwrlock(), should block, but when the timer expires,
21 *	that wait will be terminated.
22 * 6.  Main thread unlocks 'rwlock'
23 * 7.  Main thread locks 'rwlock' for writing.
24 * 8.  Create child thread to lock 'rwlock' for writing, using pthread_rwlock_timedwrlock,
25 *	 it should block but when the timer expires, the wait will be terminated
26 * 8.  Main thread unlocks 'rwlock'
27 */
28
29#define _XOPEN_SOURCE 600
30#include <pthread.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <errno.h>
35#include <sys/time.h>
36#include "posixtest.h"
37
38#define TIMEOUT 3
39
40static pthread_rwlock_t rwlock;
41static int thread_state;
42static struct timeval currsec1, currsec2;
43static int expired;
44
45/* thread_state indicates child thread state:
46	1: not in child thread yet;
47	2: just enter child thread ;
48	3: just before child thread exit;
49*/
50
51#define NOT_CREATED_THREAD 1
52#define ENTERED_THREAD 2
53#define EXITING_THREAD 3
54
55static void *fn_wr(void *arg)
56{
57	struct timespec timeout;
58	int rc;
59	thread_state = ENTERED_THREAD;
60
61	gettimeofday(&currsec1, NULL);
62
63	/* Absolute time, not relative. */
64	timeout.tv_sec = currsec1.tv_sec + TIMEOUT;
65	timeout.tv_nsec = currsec1.tv_usec * 1000;
66
67	printf("thread: attempt timed write lock, %d secs\n", TIMEOUT);
68	rc = pthread_rwlock_timedwrlock(&rwlock, &timeout);
69	if (rc == ETIMEDOUT) {
70		printf("thread: timer expired\n");
71		expired = 1;
72	} else if (rc == 0) {
73		printf("thread: acquired write lock\n");
74		expired = 0;
75		printf("thread: unlock write lock\n");
76		if (pthread_rwlock_unlock(&rwlock) != 0) {
77			printf("thread: error release write lock\n");
78			exit(PTS_UNRESOLVED);
79		}
80	} else {
81		printf("thread: Error in pthread_rwlock_timedrdlock().\n");
82		exit(PTS_UNRESOLVED);
83	}
84
85	/* Get time after the mutex timed out in locking. */
86	gettimeofday(&currsec2, NULL);
87	thread_state = EXITING_THREAD;
88	pthread_exit(0);
89	return NULL;
90}
91
92int main(void)
93{
94	int cnt = 0;
95	pthread_t thread0, thread1, thread2;
96
97	if (pthread_rwlock_init(&rwlock, NULL) != 0) {
98		printf("Error at pthread_rwlock_init()\n");
99		return PTS_UNRESOLVED;
100	}
101
102	printf("main: create thread0\n");
103	thread_state = NOT_CREATED_THREAD;
104	if (pthread_create(&thread0, NULL, fn_wr, NULL) != 0) {
105		printf("Error creating thread0\n");
106		return PTS_UNRESOLVED;
107	}
108
109	/* thread0 should not block at all since no one else has locked rwlock */
110
111	cnt = 0;
112	expired = 0;
113	do {
114		sleep(1);
115	} while (thread_state != EXITING_THREAD && cnt++ < 2 * TIMEOUT);
116
117	if (thread_state == EXITING_THREAD) {
118		if (expired == 1) {
119			printf("Test FAILED: the timer expired\n");
120			exit(PTS_FAIL);
121		} else
122			printf("thread0 correctly acquired the write lock.\n");
123	} else if (thread_state == ENTERED_THREAD) {
124		printf
125		    ("Test FAILED: thread0 incorrectly blocked on timedwrlock\n");
126		exit(PTS_FAIL);
127	} else {
128		printf("Unexpected state for thread0 %d\n", thread_state);
129		exit(PTS_UNRESOLVED);
130	}
131
132	if (pthread_join(thread0, NULL) != 0) {
133		printf("Error when joining thread0\n");
134		return PTS_UNRESOLVED;
135	}
136
137	printf("main: attempt read lock\n");
138	/* We have no lock, this read lock should succeed */
139	if (pthread_rwlock_rdlock(&rwlock) != 0) {
140		printf("Error at pthread_rwlock_rdlock()\n");
141		return PTS_UNRESOLVED;
142	}
143	printf("main: acquired read lock\n");
144
145	thread_state = NOT_CREATED_THREAD;
146
147	printf("main: create thread1\n");
148	if (pthread_create(&thread1, NULL, fn_wr, NULL) != 0) {
149		printf("Error when creating thread1\n");
150		return PTS_UNRESOLVED;
151	}
152
153	/* If the shared data is not altered by child after TIMEOUT*2 seconds,
154	   we regard it as blocked */
155
156	/* we expect the thread to expire blocking after timeout */
157	cnt = 0;
158	do {
159		sleep(1);
160	} while (thread_state != EXITING_THREAD && cnt++ < 2 * TIMEOUT);
161
162	if (thread_state == EXITING_THREAD) {
163		/* the child thread does not block, check the time interval */
164		struct timeval time_diff;
165		time_diff.tv_sec = currsec2.tv_sec - currsec1.tv_sec;
166		time_diff.tv_usec = currsec2.tv_usec - currsec1.tv_usec;
167		if (time_diff.tv_usec < 0) {
168			--time_diff.tv_sec;
169			time_diff.tv_usec += 1000000;
170		}
171		if (time_diff.tv_sec < TIMEOUT) {
172			printf
173			    ("Test FAILED: the timer expired and blocking was terminated, but the timeout is not correct: expected to wait for %d, but waited for %ld.%06ld\n",
174			     TIMEOUT, (long)time_diff.tv_sec,
175			     (long)time_diff.tv_usec);
176			exit(PTS_FAIL);
177		} else
178			printf("thread1 correctly expired at timeout.\n");
179	} else if (thread_state == ENTERED_THREAD) {
180		printf
181		    ("Test FAILED: wait is not terminated even when the timer expired\n");
182		exit(PTS_FAIL);
183	} else {
184		printf("Unexpected state for thread1 %d\n", thread_state);
185		exit(PTS_UNRESOLVED);
186	}
187
188	printf("main: unlock read lock\n");
189	if (pthread_rwlock_unlock(&rwlock) != 0) {
190		printf("Error when release read lock\n");
191		exit(PTS_UNRESOLVED);
192	}
193
194	if (pthread_join(thread1, NULL) != 0) {
195		printf("Error when joining thread1\n");
196		return PTS_UNRESOLVED;
197	}
198
199	printf("main: attempt write lock\n");
200	if (pthread_rwlock_wrlock(&rwlock) != 0) {
201		printf("Error at pthread_rwlock_wrlock()\n");
202		return PTS_UNRESOLVED;
203	}
204	printf("main: acquired write lock\n");
205
206	thread_state = NOT_CREATED_THREAD;
207	cnt = 0;
208	printf("main: create thread2\n");
209	if (pthread_create(&thread2, NULL, fn_wr, NULL) != 0) {
210		printf("Error when creating thread2\n");
211		return PTS_UNRESOLVED;
212	}
213
214	/* we expect thread2 to expire blocking after timeout */
215	do {
216		sleep(1);
217	} while (thread_state != EXITING_THREAD && cnt++ < 2 * TIMEOUT);
218
219	if (thread_state == EXITING_THREAD) {
220		/* the child thread does not block, check the time interval */
221		struct timeval time_diff;
222		time_diff.tv_sec = currsec2.tv_sec - currsec1.tv_sec;
223		time_diff.tv_usec = currsec2.tv_usec - currsec1.tv_usec;
224		if (time_diff.tv_usec < 0) {
225			--time_diff.tv_sec;
226			time_diff.tv_usec += 1000000;
227		}
228		if (time_diff.tv_sec < TIMEOUT) {
229			printf
230			    ("Test FAILED: for thread 2, the timer expired and waiter terminated, but the timeout is not correct\n");
231			exit(PTS_FAIL);
232		} else
233			printf("thread2 correctly expired at timeout.\n");
234
235	} else if (thread_state == ENTERED_THREAD) {
236		printf
237		    ("Test FAILED: for thread2, wait is not terminated even when the timer expired\n");
238		exit(PTS_FAIL);
239	} else {
240		printf("Unexpected state for thread2 %d\n", thread_state);
241		exit(PTS_UNRESOLVED);
242	}
243
244	printf("main: unlock write lock\n");
245	thread_state = NOT_CREATED_THREAD;
246	if (pthread_rwlock_unlock(&rwlock) != 0) {
247		printf("Error releasing write lock\n");
248		exit(PTS_UNRESOLVED);
249	}
250
251	if (pthread_rwlock_destroy(&rwlock) != 0) {
252		printf("Error at pthread_rwlockattr_destroy()\n");
253		return PTS_UNRESOLVED;
254	}
255
256	printf("Test PASSED\n");
257	return PTS_PASS;
258}
259