1/*
2 * Copyright (c) 2004, QUALCOMM Inc. All rights reserved.
3 * Created by:  abisain REMOVE-THIS AT qualcomm DOT com
4 * This file is licensed under the GPL license.  For the full content
5 * of this license, see the COPYING file at the top level of this
6 * source tree.
7 *
8 * Test that pthread_mutex_unlock()
9 * shall wakeup a high priority thread even when a low priority thread
10 * is running
11 *
12 * Steps:
13 * 1. Create a mutex and lock
14 * 2. Create a high priority thread and make it wait on the mutex
15 * 3. Create a low priority thread and let it busy-loop
16 * 4. Unlock the mutex and make sure that the higher priority thread
17 *    got woken up
18 *
19 */
20
21#include <pthread.h>
22#include <stdio.h>
23#include <stdlib.h>
24#include <signal.h>
25#include <unistd.h>
26#include <sys/time.h>
27#include "posixtest.h"
28
29#define TEST "5-5"
30#define AREA "scheduler"
31#define ERROR_PREFIX "unexpected error: " AREA " " TEST ": "
32
33#define HIGH_PRIORITY 10
34#define MID_PRIORITY 7
35#define LOW_PRIORITY 5
36#define RUNTIME 5
37
38/* mutex  high priority thread will wait on*/
39pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
40/* mutex required by the cond variable */
41pthread_mutex_t cond_mutex = PTHREAD_MUTEX_INITIALIZER;
42/* condition variable that threads block on*/
43pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
44
45volatile int woken_up = -1;
46volatile int low_done = -1;
47
48/* Utility function to find difference between two time values */
49float timediff(struct timespec t2, struct timespec t1)
50{
51	float diff = t2.tv_sec - t1.tv_sec;
52	diff += (t2.tv_nsec - t1.tv_nsec)/1000000000.0;
53	return diff;
54}
55
56/* This signal handler will wakeup the main thread by sending a signal
57 * to a condition variable that the main thread is waiting on.
58 */
59void signal_handler(int sig)
60{
61	int	 rc = 0;
62
63	rc = pthread_cond_signal(&cond);
64	if(rc != 0) {
65		printf(ERROR_PREFIX "pthread_cond_signal\n");
66		exit(PTS_UNRESOLVED);
67	}
68}
69
70void *hi_priority_thread(void *tmp)
71{
72	struct sched_param        param;
73	int                       policy;
74	int                       rc = 0;
75
76	param.sched_priority = HIGH_PRIORITY;
77	rc = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
78	if(rc != 0) {
79		printf(ERROR_PREFIX "pthread_setschedparam\n");
80		exit(PTS_UNRESOLVED);
81	}
82	rc = pthread_getschedparam(pthread_self(), &policy, &param);
83	if(rc != 0) {
84		printf(ERROR_PREFIX "pthread_getschedparam\n");
85		exit(PTS_UNRESOLVED);
86	}
87	if(policy != SCHED_RR) {
88		printf(ERROR_PREFIX "The policy is not correct\n");
89		exit(PTS_UNRESOLVED);
90	}
91	if(param.sched_priority != HIGH_PRIORITY) {
92		printf(ERROR_PREFIX "The priority is not correct\n");
93		exit(PTS_UNRESOLVED);
94	}
95
96	/* acquire the mutex */
97	rc = pthread_mutex_lock(&mutex);
98	if(rc != 0) {
99		printf(ERROR_PREFIX "pthread_mutex_lock\n");
100		exit(PTS_UNRESOLVED);
101	}
102
103	/* This variable is unprotected because the scheduling removes
104	 * the contention
105	 */
106	if(low_done != 1)
107		woken_up = 1;
108
109	rc = pthread_mutex_unlock(&mutex);
110	if(rc != 0) {
111		printf(ERROR_PREFIX "pthread_mutex_unlock\n");
112		exit(PTS_UNRESOLVED);
113	}
114	pthread_exit((void *) 0);
115}
116
117void *low_priority_thread(void *tmp)
118{
119	struct timespec           current_time, start_time;
120	struct sched_param        param;
121	int                       rc = 0;
122	int                       policy;
123
124	param.sched_priority = LOW_PRIORITY;
125	rc = pthread_setschedparam(pthread_self(), SCHED_RR, &param);
126	if(rc != 0) {
127		printf(ERROR_PREFIX "pthread_setschedparam\n");
128		exit(PTS_UNRESOLVED);
129	}
130	rc = pthread_getschedparam(pthread_self(), &policy, &param);
131	if(rc != 0) {
132		printf(ERROR_PREFIX "pthread_getschedparam\n");
133		exit(PTS_UNRESOLVED);
134	}
135	if(policy != SCHED_RR) {
136		printf(ERROR_PREFIX "Policy not correct\n");
137		exit(PTS_UNRESOLVED);
138	}
139	if(param.sched_priority != LOW_PRIORITY) {
140		printf(ERROR_PREFIX "Priority not correct\n");
141		exit(PTS_UNRESOLVED);
142	}
143
144	/* Grab the start time and busy loop for 5 seconds */
145	clock_gettime(CLOCK_REALTIME, &start_time);
146	while(1) {
147		clock_gettime(CLOCK_REALTIME, &current_time);
148		if(timediff(current_time, start_time) > RUNTIME)
149			break;
150	}
151	low_done = 1;
152	pthread_exit((void *) 0);
153}
154
155int main()
156{
157	pthread_t                high_id, low_id;
158	pthread_attr_t           low_attr, high_attr;
159	struct sched_param       param;
160	int                      rc = 0;
161	int                      policy;
162
163	param.sched_priority = MID_PRIORITY;
164	rc =  pthread_setschedparam(pthread_self(), SCHED_RR, &param);
165	if(rc != 0) {
166		printf(ERROR_PREFIX "pthread_setschedparam\n");
167		exit(PTS_UNRESOLVED);
168	}
169	rc = pthread_getschedparam(pthread_self(), &policy, &param);
170	if(rc != 0) {
171		printf(ERROR_PREFIX "pthread_getschedparam\n");
172		exit(PTS_UNRESOLVED);
173	}
174	if(policy != SCHED_RR) {
175		printf(ERROR_PREFIX "The policy is not correct\n");
176		exit(PTS_UNRESOLVED);
177	}
178	if(param.sched_priority != MID_PRIORITY) {
179		printf(ERROR_PREFIX "The priority is not correct\n");
180		exit(PTS_UNRESOLVED);
181	}
182
183	rc = pthread_mutex_lock(&mutex);
184	if(rc != 0) {
185		printf(ERROR_PREFIX "pthread_mutex_lock\n");
186		exit(PTS_UNRESOLVED);
187	}
188
189	/* create the higher priority */
190	rc = pthread_attr_init(&high_attr);
191	if(rc != 0) {
192		printf(ERROR_PREFIX "pthread_attr_init\n");
193		exit(PTS_UNRESOLVED);
194	}
195	rc = pthread_attr_setschedpolicy(&high_attr, SCHED_RR);
196	if(rc != 0) {
197		printf(ERROR_PREFIX "pthread_attr_setschedpolicy\n");
198		exit(PTS_UNRESOLVED);
199	}
200	param.sched_priority = HIGH_PRIORITY;
201	rc = pthread_attr_setschedparam(&high_attr, &param);
202	if(rc != 0) {
203		printf(ERROR_PREFIX "pthread_attr_setschedparam\n");
204		exit(PTS_UNRESOLVED);
205	}
206	rc = pthread_create(&high_id, &high_attr, hi_priority_thread, NULL);
207	if(rc != 0) {
208		printf(ERROR_PREFIX "pthread_create\n");
209		exit(PTS_UNRESOLVED);
210	}
211
212	/* Create the low priority thread */
213	rc = pthread_attr_init(&low_attr);
214	if(rc != 0) {
215		printf(ERROR_PREFIX "pthread_attr_init\n");
216		exit(PTS_UNRESOLVED);
217	}
218	rc = pthread_attr_setschedpolicy(&low_attr, SCHED_RR);
219	if(rc != 0) {
220		printf(ERROR_PREFIX "pthread_attr_setschedpolicy\n");
221		exit(PTS_UNRESOLVED);
222	}
223	param.sched_priority = LOW_PRIORITY;
224	rc = pthread_attr_setschedparam(&low_attr, &param);
225	if(rc != 0) {
226		printf(ERROR_PREFIX "pthread_attr_setschedparam\n");
227		exit(PTS_UNRESOLVED);
228	}
229	rc = pthread_create(&low_id, &low_attr, low_priority_thread, NULL);
230	if(rc != 0) {
231		printf(ERROR_PREFIX "pthread_create\n");
232		exit(PTS_UNRESOLVED);
233	}
234
235	/* setup a signal handler which will wakeup main later */
236	if(signal(SIGALRM, signal_handler) == SIG_ERR) {
237		perror(ERROR_PREFIX "signal");
238		exit(PTS_UNRESOLVED);
239	}
240
241	rc = pthread_mutex_lock(&cond_mutex);
242	if(rc != 0) {
243		printf(ERROR_PREFIX "pthread_mutex_lock\n");
244		exit(PTS_UNRESOLVED);
245	}
246
247	alarm(2);
248	rc = pthread_cond_wait(&cond, &cond_mutex);
249	if(rc != 0) {
250		printf(ERROR_PREFIX "pthread_cond_wait\n");
251		exit(PTS_UNRESOLVED);
252	}
253	rc = pthread_mutex_unlock(&cond_mutex);
254	if(rc != 0) {
255		printf(ERROR_PREFIX "pthread_mutex_unlock\n");
256		exit(PTS_UNRESOLVED);
257	}
258
259	/* Wake the other high priority thread up */
260	rc = pthread_mutex_unlock(&mutex);
261	if(rc != 0) {
262		printf(ERROR_PREFIX "pthread_mutex_unlock\n");
263		exit(PTS_UNRESOLVED);
264	}
265
266	/* Wait for the threads to exit */
267	rc = pthread_join(high_id, NULL);
268	if(rc != 0) {
269		printf(ERROR_PREFIX "pthread_join\n");
270		exit(PTS_UNRESOLVED);
271	}
272
273	rc = pthread_join(low_id, NULL);
274	if(rc != 0) {
275		printf(ERROR_PREFIX "pthread_join\n");
276		exit(PTS_UNRESOLVED);
277	}
278
279	/* Check the result */
280	if(woken_up == -1) {
281		printf("High priority was not woken up. Test FAILED.\n");
282		exit(PTS_FAIL);
283	}
284	printf("Test PASS\n");
285	exit(PTS_PASS);
286}
287