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