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_cond_signal()
9 *   shall wakeup a high priority thread even when a low priority thread
10 *   is running
11
12 * Steps:
13 * 1. Create a condition variable
14 * 2. Create a high priority thread and make it wait on the cond
15 * 3. Create a low priority thread and let it busy-loop
16 * 4. Signal the cond in a signal handler and check that high
17 *    priority thread 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 <time.h>
27#include "posixtest.h"
28
29#define TEST "5-1"
30#define AREA "scheduler"
31#define ERROR_PREFIX "unexpected error: " AREA " " TEST ": "
32
33#define HIGH_PRIORITY 10
34#define LOW_PRIORITY  5
35#define RUNTIME       5
36#define POLICY        SCHED_RR
37
38/* mutex required by the cond variable */
39pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
40/* condition variable that threads block on*/
41pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
42
43/* Flags that the threads use to indicate events */
44int woken_up = -1;
45int low_done = -1;
46
47/* Signal handler that handle the ALRM and wakes up
48 * the high priority thread
49 */
50void signal_handler(int sig)
51{
52	if(pthread_cond_signal(&cond) != 0) {
53		printf(ERROR_PREFIX "pthread_cond_signal\n");
54		exit(PTS_UNRESOLVED);
55	}
56}
57
58/* Utility function to find difference between two time values */
59float timediff(struct timespec t2, struct timespec t1)
60{
61	float diff = t2.tv_sec - t1.tv_sec;
62	diff += (t2.tv_nsec - t1.tv_nsec)/1000000000.0;
63	return diff;
64}
65
66void *hi_priority_thread(void *tmp)
67{
68	struct sched_param        param;
69	int                       policy;
70	int                       rc = 0;
71
72	param.sched_priority = HIGH_PRIORITY;
73
74	rc = pthread_setschedparam(pthread_self(), POLICY, &param);
75	if(rc != 0) {
76		printf(ERROR_PREFIX "pthread_setschedparam\n");
77		exit(PTS_UNRESOLVED);
78	}
79	rc = pthread_getschedparam(pthread_self(), &policy, &param);
80	if(rc != 0) {
81		printf(ERROR_PREFIX "pthread_getschedparam\n");
82		exit(PTS_UNRESOLVED);
83	}
84	if((policy != POLICY) || (param.sched_priority != HIGH_PRIORITY)) {
85		printf("Error: the policy or priority not correct\n");
86		exit(PTS_UNRESOLVED);
87	}
88
89	/* Install a signal handler for ALRM */
90	if(signal(SIGALRM, signal_handler) != 0) {
91		perror(ERROR_PREFIX "signal:");
92		exit(PTS_UNRESOLVED);
93	}
94
95	/* acquire the mutex */
96	rc = pthread_mutex_lock(&mutex);
97	if(rc != 0) {
98		printf(ERROR_PREFIX "pthread_mutex_lock\n");
99		exit(PTS_UNRESOLVED);
100	}
101
102	/* Setup an alarm to go off in 2 seconds */
103	alarm(2);
104
105	/* Block, to be woken up by the signal handler */
106	rc = pthread_cond_wait(&cond, &mutex);
107	if(rc != 0) {
108		printf(ERROR_PREFIX "pthread_cond_wait\n");
109		exit(PTS_UNRESOLVED);
110	}
111
112	/* This variable is unprotected because the scheduling removes
113	 * the contention
114	 */
115	if(low_done != 1)
116		woken_up = 1;
117
118	rc = pthread_mutex_unlock(&mutex);
119	if(rc != 0) {
120		printf(ERROR_PREFIX "pthread_mutex_unlock\n");
121		exit(PTS_UNRESOLVED);
122	}
123	return NULL;
124}
125
126void *low_priority_thread(void *tmp)
127{
128	struct timespec        start_time, current_time;
129	struct sched_param     param;
130	int                    policy;
131	int                    rc = 0;
132
133	param.sched_priority = LOW_PRIORITY;
134
135	rc = pthread_setschedparam(pthread_self(), POLICY, &param);
136	if(rc != 0) {
137		printf(ERROR_PREFIX "pthread_setschedparam\n");
138		exit(PTS_UNRESOLVED);
139	}
140	rc = pthread_getschedparam(pthread_self(), &policy, &param);
141	if(rc != 0) {
142		printf(ERROR_PREFIX "pthread_getschedparam\n");
143		exit(PTS_UNRESOLVED);
144	}
145	if((policy != POLICY) || (param.sched_priority != LOW_PRIORITY)) {
146		printf("Error: the policy or priority not correct\n");
147		exit(PTS_UNRESOLVED);
148	}
149
150	/* grab the start time and busy loop for 5 seconds */
151	clock_gettime(CLOCK_REALTIME, &start_time);
152	while(1)
153	{
154		clock_gettime(CLOCK_REALTIME, &current_time);
155		if(timediff(current_time, start_time) > RUNTIME)
156			break;
157	}
158	low_done = 1;
159	return NULL;
160}
161
162int main()
163{
164	pthread_t                   high_id, low_id;
165	pthread_attr_t              high_attr, low_attr;
166	struct sched_param          param;
167	int                         rc = 0;
168
169	/* Create the higher priority thread */
170	rc = pthread_attr_init(&high_attr);
171	if(rc != 0) {
172		printf(ERROR_PREFIX "pthread_attr_init\n");
173		exit(PTS_UNRESOLVED);
174	}
175
176	rc = pthread_attr_setschedpolicy(&high_attr, POLICY);
177	if(rc != 0) {
178		printf(ERROR_PREFIX "pthread_attr_setschedpolicy\n");
179		exit(PTS_UNRESOLVED);
180	}
181	param.sched_priority = HIGH_PRIORITY;
182	rc = pthread_attr_setschedparam(&high_attr, &param);
183	if(rc != 0) {
184		printf(ERROR_PREFIX "pthread_attr_setschedparam\n");
185		exit(PTS_UNRESOLVED);
186	}
187	rc = pthread_create(&high_id, &high_attr, hi_priority_thread, NULL);
188	if(rc != 0) {
189		printf(ERROR_PREFIX "pthread_create\n");
190		exit(PTS_UNRESOLVED);
191	}
192
193	/* Create the low priority thread */
194	rc = pthread_attr_init(&low_attr);
195	if(rc != 0) {
196		printf(ERROR_PREFIX "pthread_attr_init\n");
197		exit(PTS_UNRESOLVED);
198	}
199	rc = pthread_attr_setschedpolicy(&low_attr, POLICY);
200	if(rc != 0) {
201		printf(ERROR_PREFIX "pthread_attr_setschedpolicy\n");
202		exit(PTS_UNRESOLVED);
203	}
204	param.sched_priority = LOW_PRIORITY;
205	rc = pthread_attr_setschedparam(&low_attr, &param);
206	if(rc != 0) {
207		printf(ERROR_PREFIX "pthread_attr_setschedparam\n");
208		exit(PTS_UNRESOLVED);
209	}
210	rc = pthread_create(&low_id, &low_attr, low_priority_thread, NULL);
211	if(rc != 0) {
212		printf(ERROR_PREFIX "pthread_create\n");
213		exit(PTS_UNRESOLVED);
214	}
215
216	/* Wait for the threads to exit */
217	rc = pthread_join(high_id, NULL);
218	if(rc != 0) {
219		printf(ERROR_PREFIX "pthread_join\n");
220		exit(PTS_UNRESOLVED);
221	}
222
223	rc = pthread_join(low_id, NULL);
224	if(rc != 0) {
225		printf(ERROR_PREFIX "pthread_join\n");
226		exit(PTS_UNRESOLVED);
227	}
228
229	/* Check the result */
230	if(woken_up == -1) {
231		printf("Test FAILED: high priority was not woken up\\n");
232		exit(PTS_FAIL);
233	}
234
235	printf("Test PASS\n");
236	exit(PTS_PASS);
237}
238