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_timedrdlock(pthread_rwlock_t *rwlock)
8  *
9  *  The function shall apply a read lock to the read-write lock referenced by
10  *  rwlock as in the pthread_rwlock_rdlock(). However, if the lock cannot be
11  *  acquired with out waiting for other threads to unlock the lock, this wait
12  *  shall be terminated when the specified timeout expires.
13  *
14  * Steps:
15  * 1.  Initialize a pthread_rwlock_t object 'rwlock' with pthread_rwlock_init()
16  * 2.  Main thread lock 'rwlock' for reading with pthread_rwlock_rdlock()
17  * 3.  Create a child thread, the thread lock 'rwlock' for reading,
18  *     using pthread_rwlock_timedrdlock(), should get read lock. Thread unlocks 'rwlock'.
19  * 4.  Main thread unlock 'rwlock'
20  * 5.  Main thread lock 'rwlock' for writing
21  * 6.  Create child thread to lock 'rwlock' for reading,
22  *     using pthread_rwlock_timedrdlock, should block
23  *     but when the timer expires, the wait will be terminated
24  * 7.  Main thread unlock 'rwlock'
25  */
26 
27 /* Test for CLOCK_REALTIME */
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 /* thread_state indicates child thread state:
39 	1: not in child thread yet;
40 	2: just enter child thread ;
41 	3: just before child thread exit;
42 */
43 
44 #define NOT_CREATED_THREAD 1
45 #define ENTERED_THREAD 2
46 #define EXITING_THREAD 3
47 
48 #define TIMEOUT 3
49 
50 static pthread_rwlock_t rwlock;
51 static int thread_state;
52 static struct timeval currsec1, currsec2;
53 
fn_rd(void * arg)54 static void* fn_rd(void *arg)
55 {
56 
57 	thread_state = ENTERED_THREAD;
58 	struct timespec timeout, ts;
59 	int rc;
60 #ifdef CLOCK_REALTIME
61 	printf("Test CLOCK_REALTIME\n");
62 	clock_gettime(CLOCK_REALTIME, &ts);
63 	currsec1.tv_sec = ts.tv_sec;
64 	currsec1.tv_usec = ts.tv_nsec / 1000;
65 #else
66 	gettimeofday(&currsec1, NULL);
67 #endif
68 	/* Absolute time, not relative. */
69 	timeout.tv_sec = currsec1.tv_sec + TIMEOUT;
70 	timeout.tv_nsec = currsec1.tv_usec * 1000;
71 
72 	printf("thread: attempt timed read lock, %d secs\n", TIMEOUT);
73 	rc = pthread_rwlock_timedrdlock(&rwlock, &timeout);
74 	if(rc  == ETIMEDOUT)
75 		printf("thread: timer expired\n");
76 	else if(rc == 0)
77 	{
78 		printf("thread: acquired read lock\n");
79 		printf("thread: unlock read lock\n");
80 		if(pthread_rwlock_unlock(&rwlock) != 0)
81 		{
82 			exit(PTS_UNRESOLVED);
83 		}
84 	}
85 	else
86 	{
87 		printf("Error: thread: in pthread_rwlock_timedrdlock(), return code:%d\n", rc);
88 		exit(PTS_UNRESOLVED);
89 	}
90 
91 	/* Get time after the pthread_rwlock_timedrdlock() call. */
92 #ifdef CLOCK_REALTIME
93 	clock_gettime(CLOCK_REALTIME, &ts);
94 	currsec2.tv_sec = ts.tv_sec;
95 	currsec2.tv_usec = ts.tv_nsec / 1000;
96 #else
97 	gettimeofday(&currsec2, NULL);
98 #endif
99 	thread_state = EXITING_THREAD;
100 	pthread_exit(0);
101 	return NULL;
102 }
103 
main()104 int main()
105 {
106 	int cnt = 0;
107 	pthread_t rd_thread1, rd_thread2;
108 
109 	if(pthread_rwlock_init(&rwlock, NULL) != 0)
110 	{
111 		printf("main: Error at pthread_rwlock_init()\n");
112 		return PTS_UNRESOLVED;
113 	}
114 
115 	printf("main: attempt read lock\n");
116 	if(pthread_rwlock_rdlock(&rwlock) != 0)
117 	{
118 		printf("main: Error at pthread_rwlock_rdlock()\n");
119 		return PTS_UNRESOLVED;
120 	}
121 	printf("main: acquired read lock\n");
122 
123 	thread_state = NOT_CREATED_THREAD;
124 
125 	printf("main: create rd_thread1\n");
126 	if(pthread_create(&rd_thread1, NULL, fn_rd, NULL) != 0)
127 	{
128 		printf("main: Error when creating rd_thread1\n");
129 		return PTS_UNRESOLVED;
130 	}
131 
132 	/* If the shared data is not altered by child after 5 seconds,
133 	   we regard it as blocked */
134 
135 	/* we expect the thread not to block */
136 	cnt = 0;
137 	do{
138 		sleep(1);
139 	}while (thread_state !=EXITING_THREAD && cnt++ < 5);
140 
141 	if(thread_state == ENTERED_THREAD)
142 	{
143 		/* the child thread started but blocked */
144 		printf("Test FAILED: rd_thread1 blocked on pthread_rwlock_timedrdlock()\n");
145 		exit(PTS_FAIL);
146 	}
147 	else if(thread_state != EXITING_THREAD)
148 	{
149 		printf("Unexpected thread state %d\n", thread_state);
150 		exit(PTS_UNRESOLVED);
151 	}
152 
153 	if(pthread_join(rd_thread1, NULL) != 0)
154 	{
155 		printf("main: Error when join rd_thread1\n");
156 		exit(PTS_UNRESOLVED);
157 	}
158 
159 	printf("main: unlock read lock\n");
160 	if(pthread_rwlock_unlock(&rwlock) != 0)
161 	{
162 		printf("main: Error when release read lock\n");
163 		return PTS_UNRESOLVED;
164 	}
165 
166 	printf("main: attempt write lock\n");
167 	if(pthread_rwlock_wrlock(&rwlock) != 0)
168 	{
169 		printf("main: Failed to get write lock\n");
170 		return PTS_UNRESOLVED;
171 	}
172 	printf("main: acquired write lock\n");
173 
174 	thread_state = NOT_CREATED_THREAD;
175 	printf("main: create rd_thread2\n");
176 	if(pthread_create(&rd_thread2, NULL, fn_rd, NULL) != 0)
177 	{
178 		printf("main: Failed to create rd_thread2\n");
179 		return PTS_UNRESOLVED;
180 	}
181 
182 	/* we expect rd_thread2 to block and timeout. */
183 	cnt = 0;
184 	do{
185 		sleep(1);
186 	}while (thread_state !=EXITING_THREAD && cnt++ < 5);
187 
188 	if(thread_state == EXITING_THREAD)
189 	{
190 		/* the child thread does not block, check the time interval */
191 		struct timeval time_diff;
192 		time_diff.tv_sec = currsec2.tv_sec - currsec1.tv_sec;
193 		time_diff.tv_usec = currsec2.tv_usec - currsec1.tv_usec;
194 		if (time_diff.tv_usec < 0)
195 		{
196 			--time_diff.tv_sec;
197 			time_diff.tv_usec += 1000000;
198 		}
199 		if(time_diff.tv_sec < TIMEOUT)
200 		{
201 			printf("Test FAILED: the timer expired and thread terminated, "
202 				"but the timeout is not correct: "
203 				"start time %ld.%06ld, end time %ld.%06ld\n",
204 				(long) currsec1.tv_sec, (long) currsec1.tv_usec,
205 				(long) currsec2.tv_sec, (long) currsec2.tv_usec);
206 			exit(PTS_FAIL);
207 		} else
208 			printf("thread: read lock correctly timed out\n");
209 	}
210 	else if(thread_state == ENTERED_THREAD)
211 	{
212 		printf("Test FAILED: read block was not terminated even when the timer expired\n");
213 		exit(PTS_FAIL);
214 	}
215 	else
216 	{
217 		printf("Unexpected thread state %d\n", thread_state);
218 		return PTS_UNRESOLVED;
219 	}
220 
221 	printf("main: unlock write lock\n");
222 	if(pthread_rwlock_unlock(&rwlock) != 0)
223 	{
224 		printf("main: Failed to release write lock\n");
225 		exit(PTS_UNRESOLVED);
226 	}
227 
228 	if(pthread_rwlock_destroy(&rwlock) != 0)
229 	{
230 		printf("Error at pthread_rwlockattr_destroy()\n");
231 		exit(PTS_UNRESOLVED);
232 	}
233 
234 	printf("Test PASSED\n");
235 	return PTS_PASS;
236 }
237