1/*-
2 * SPDX-License-Identifier: BSD-4-Clause
3 *
4 * Copyright (c) 1996-1999
5 *	HD Associates, Inc.  All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 *    notice, this list of conditions and the following disclaimer in the
14 *    documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 *    must display the following acknowledgement:
17 *	This product includes software developed by HD Associates, Inc
18 * 4. Neither the name of the author nor the names of any co-contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY HD ASSOCIATES AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL HD ASSOCIATES OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 * $FreeBSD$
34 *
35 */
36#include <sys/types.h>
37#include <unistd.h>
38#include <stdlib.h>
39#include <stdio.h>
40#include <errno.h>
41#include <err.h>
42#include <fcntl.h>
43#include <sys/types.h>
44#include <sys/mman.h>
45#include <sched.h>
46#include <stdlib.h>
47#include <sys/wait.h>
48
49#include "prutil.h"
50
51/* buzz: busy wait a random amount of time.
52 */
53static void buzz(int n)
54{
55	volatile int i;
56	int m = random() & 0x0ffff;
57	for (i = 0; i < m; i++)
58		;
59}
60
61/* Yield: Verify that "sched_yield" works for the FIFO case.
62 * This runs several processes and verifies that the yield seems
63 * to permit the next one on the ready queue to run.
64 */
65int yield(int argc, char *argv[])
66{
67	volatile int *p;
68	int i;
69	int nslaves, n;
70	int master, slave;
71	pid_t youngest = !0;	/* Our youngest child */
72	struct sched_param set, got;
73	int nloops = 1000;
74
75	errno = 0;
76
77	set.sched_priority = sched_get_priority_max(SCHED_FIFO);
78	if (set.sched_priority == -1 && errno) {
79		perror("sched_get_priority_max");
80		exit(errno);
81	}
82
83	if (argc == 1)
84		n = nslaves = 10;
85
86	else if (argc != 2) {
87		fprintf(stderr, "usage: prog [n_instances]\n");
88		exit(-1);
89	}
90	else
91		n = nslaves = atoi(argv[1]);
92
93	p = (int *)mmap(0, sizeof(int),
94	PROT_READ|PROT_WRITE, MAP_ANON|MAP_SHARED, -1, 0);
95
96	if (p == (int *)-1)
97		err(errno, "mmap");
98
99	*p = 0;
100
101	if (sched_setscheduler(0, SCHED_FIFO, &set) == -1)
102		err(errno, "sched_setscheduler");
103
104	/* I better still be SCHED_FIFO and RT_PRIO_MAX:
105	 */
106	(void)sched_is(__LINE__, &got, SCHED_FIFO);
107	if (got.sched_priority != set.sched_priority) {
108		fprintf(stderr, "line %d: scheduler screwup\n",
109		__LINE__);
110		exit(-1);
111	}
112
113	slave = 0;
114	master = 1;
115
116	/* Fork off the slaves.
117	 */
118	for (i = 0; i < nslaves; i++) {
119		if ((youngest = fork()) == 0) {
120			/* I better still be SCHED_FIFO and RT_PRIO_MAX:
121			 */
122			(void)sched_is(__LINE__, &got, SCHED_FIFO);
123
124			if (got.sched_priority != set.sched_priority) {
125				fprintf(stderr, "line %d: scheduler screwup\n",
126				__LINE__);
127				exit(-1);
128			}
129
130			master = 0;	/* I'm a slave */
131			slave = i + 1;	/* With this flag */
132			*p = slave;	/* And I live */
133			break;
134		}
135	}
136
137	if (master) {
138		/* If we conform the slave processes haven't run yet.
139		 * The master must yield to let the first slave run.
140		 */
141		if (*p != 0) {
142			fprintf(stderr,
143			"Error at line %d: Writer %d has run\n", __LINE__, *p);
144			exit(-1);
145		}
146	}
147
148	/* Now the master yields, the first slave runs, and yields,
149	 * next runs, yields, ...
150	 *
151	 * So the master should get through this first.
152	 */
153
154	if (sched_yield() == -1)
155		err(errno, "sched_yield");
156
157	if (master) {
158		int status;
159
160		/* The final slave process should be the last one started.
161		 */
162		if (*p != nslaves) {
163			fprintf(stderr,
164			"Error at line %d: Final slave is %d not %d.\n",
165			__LINE__, *p, nslaves);
166			exit(-1);
167		}
168
169		/* Wait for our youngest to exit:
170		 */
171		waitpid(youngest, &status, 0);
172
173		exit(WEXITSTATUS(status));	/* Let the slaves continue */
174	}
175
176	/* Now the first one has started up.
177	 */
178	for (i = 0; i < nloops; i++) {
179		if (((*p) % nslaves) !=
180		((slave + nslaves - 1) % nslaves)) {
181			fprintf(stderr, "%d ran before %d on iteration %d.\n",
182			*p, slave, i);
183			exit(-1);
184		}
185		*p = slave;
186
187		/* Delay some random amount of time.
188		 */
189		buzz(slave);
190
191		if (sched_yield() == -1)
192			err(errno, "sched_yield");
193	}
194
195	exit(0);
196}
197#ifdef STANDALONE_TESTS
198int main(int argc, char *argv[]) { return yield(argc, argv); }
199#endif
200