1 /******************************************************************************
2 *
3 * Copyright © International Business Machines Corp., 2006, 2008
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18 *
19 * NAME
20 * prio-wake.c
21 *
22 * DESCRIPTION
23 * Test priority ordered wakeup with pthread_cond_*
24 * * Steps:
25 * - Creates a number of worker threads with increasing FIFO priorities
26 * (by default, num worker threads = num cpus)
27 * - Create a master thread
28 * - The time the worker thread starts running is noted. Each of the
29 * worker threads then waits on the same _condvar_. The time it
30 * wakes up also noted.
31 * - Once all the threads finish execution, the start and wakeup times
32 * of all the threads is displayed.
33 * - The output must indicate that the thread wakeup happened in a
34 * priority order.
35 *
36 * USAGE:
37 *
38 *
39 * AUTHOR
40 * Darren Hart <dvhltc@us.ibm.com>
41 *
42 * HISTORY
43 * 2006-Apr-26: Initial version by Darren Hart
44 * 2006-May-25: Updated to use new librt.h features
45 *
46 *****************************************************************************/
47
48 #include <stdio.h>
49 #include <stdlib.h>
50 #include <signal.h>
51 #include <time.h>
52 #include <pthread.h>
53 #include <sched.h>
54 #include <errno.h>
55 #include <sys/syscall.h>
56 #include <librttest.h>
57 #include <libstats.h>
58
59 volatile int running_threads = 0;
60 static int rt_threads = 0;
61 static int locked_broadcast = 1;
62 static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
63 static pthread_mutex_t mutex;
64 static volatile nsec_t beginrun;
65
66 static int ret = 0;
67
usage(void)68 void usage(void)
69 {
70 rt_help();
71 printf("prio-wake specific options:\n");
72 printf(" -n# #: number of worker threads\n");
73 printf(" -l# 1:lock the mutex before broadcast, 0:don't\n");
74 printf(" defaults to 1\n");
75 }
76
parse_args(int c,char * v)77 int parse_args(int c, char *v)
78 {
79
80 int handled = 1;
81 switch (c) {
82 case 'h':
83 usage();
84 exit(0);
85 case 'n':
86 rt_threads = atoi(v);
87 break;
88 case 'l':
89 locked_broadcast = atoi(v);
90 break;
91 default:
92 handled = 0;
93 break;
94 }
95 return handled;
96 }
97
98 struct array {
99 int *arr;
100 int counter;
101 };
102 struct array wakeup = { NULL, 0 };
103
master_thread(void * arg)104 void *master_thread(void *arg)
105 {
106 int rc;
107 nsec_t start;
108
109 /* make sure children are started */
110 while (running_threads < rt_threads)
111 usleep(1000);
112 /* give the worker threads a chance to get to sleep in the kernel
113 * in the unlocked broadcast case. */
114 usleep(1000);
115
116 start = rt_gettime() - beginrun;
117
118 printf("%08lld us: Master thread about to wake the workers\n",
119 start / NS_PER_US);
120 /* start the children threads */
121 if (locked_broadcast)
122 rc = pthread_mutex_lock(&mutex);
123 rc = pthread_cond_broadcast(&cond);
124 if (locked_broadcast)
125 rc = pthread_mutex_unlock(&mutex);
126
127 return NULL;
128 }
129
worker_thread(void * arg)130 void *worker_thread(void *arg)
131 {
132 struct sched_param sched_param;
133 int policy;
134 int rc;
135 int mypri;
136 int j;
137 nsec_t start, wake;
138 j = (intptr_t) arg;
139
140 if (pthread_getschedparam(pthread_self(), &policy, &sched_param) != 0) {
141 printf
142 ("ERR: Couldn't get pthread info. Priority value wrong\n");
143 mypri = -1;
144 } else {
145 mypri = sched_param.sched_priority;
146 }
147
148 start = rt_gettime() - beginrun;
149 debug(0, "%08lld us: RealtimeThread-%03d pri %03d started\n",
150 start / NS_PER_US, j, mypri);
151
152 rc = pthread_mutex_lock(&mutex);
153 running_threads++;
154 rc = pthread_cond_wait(&cond, &mutex);
155
156 wake = rt_gettime() - beginrun;
157 running_threads--;
158 wakeup.arr[wakeup.counter++] = mypri;
159 debug(0, "%08lld us: RealtimeThread-%03d pri %03d awake\n",
160 wake / NS_PER_US, j, mypri);
161
162 rc = pthread_mutex_unlock(&mutex);
163
164 return NULL;
165 }
166
main(int argc,char * argv[])167 int main(int argc, char *argv[])
168 {
169 int threads_per_prio;
170 int numcpus;
171 int numprios;
172 int prio;
173 int i;
174 setup();
175
176 rt_init("hn:l:", parse_args, argc, argv);
177
178 if (rt_threads == 0) {
179 numcpus = sysconf(_SC_NPROCESSORS_ONLN);
180 rt_threads = numcpus;
181 }
182 wakeup.arr = malloc(rt_threads * sizeof(int));
183 wakeup.counter = 0;
184 printf("\n-----------------------\n");
185 printf("Priority Ordered Wakeup\n");
186 printf("-----------------------\n");
187 printf("Worker Threads: %d\n", rt_threads);
188 printf("Calling pthread_cond_broadcast() with mutex: %s\n\n",
189 locked_broadcast ? "LOCKED" : "UNLOCKED");
190
191 beginrun = rt_gettime();
192
193 init_pi_mutex(&mutex);
194
195 /* calculate the number of threads per priority */
196 /* we get num numprios -1 for the workers, leaving one for the master */
197 numprios = sched_get_priority_max(SCHED_FIFO) -
198 sched_get_priority_min(SCHED_FIFO);
199
200 threads_per_prio = rt_threads / numprios;
201 if (rt_threads % numprios)
202 threads_per_prio++;
203
204 /* start the worker threads */
205 prio = sched_get_priority_min(SCHED_FIFO);
206 for (i = rt_threads; i > 0; i--) {
207 if ((i != rt_threads && (i % threads_per_prio) == 0))
208 prio++;
209 create_fifo_thread(worker_thread, (void *)(intptr_t) i, prio);
210 }
211
212 /* start the master thread */
213 create_fifo_thread(master_thread, (void *)(intptr_t) i, ++prio);
214
215 /* wait for threads to complete */
216 join_threads();
217
218 pthread_mutex_destroy(&mutex);
219
220 printf("\nCriteria: Threads should be woken up in priority order\n");
221
222 for (i = 0; i < (wakeup.counter - 1); i++) {
223 if (wakeup.arr[i] < wakeup.arr[i + 1]) {
224 printf("FAIL: Thread %d woken before %d\n",
225 wakeup.arr[i], wakeup.arr[i + 1]);
226 ret++;
227 }
228 }
229 printf("Result: %s\n", ret ? "FAIL" : "PASS");
230 return ret;
231 }
232