1/*	$OpenBSD: sched.c,v 1.18 2014/10/26 03:28:41 guenther Exp $	*/
2
3/*
4 * Copyright (c) 1990 Jan-Simon Pendry
5 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
6 * Copyright (c) 1990, 1993
7 *	The Regents of the University of California.  All rights reserved.
8 *
9 * This code is derived from software contributed to Berkeley by
10 * Jan-Simon Pendry at Imperial College, London.
11 *
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
14 * are met:
15 * 1. Redistributions of source code must retain the above copyright
16 *    notice, this list of conditions and the following disclaimer.
17 * 2. Redistributions in binary form must reproduce the above copyright
18 *    notice, this list of conditions and the following disclaimer in the
19 *    documentation and/or other materials provided with the distribution.
20 * 3. Neither the name of the University nor the names of its contributors
21 *    may be used to endorse or promote products derived from this software
22 *    without specific prior written permission.
23 *
24 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 * SUCH DAMAGE.
35 *
36 *	from: @(#)sched.c	8.1 (Berkeley) 6/6/93
37 *	$Id: sched.c,v 1.18 2014/10/26 03:28:41 guenther Exp $
38 */
39
40/*
41 * Process scheduler
42 */
43
44#include "am.h"
45#include <signal.h>
46#include <sys/wait.h>
47#include <setjmp.h>
48extern jmp_buf select_intr;
49extern int select_intr_valid;
50
51typedef struct pjob pjob;
52struct pjob {
53	qelem hdr;			/* Linked list */
54	pid_t pid;			/* Process ID of job */
55	cb_fun cb_fun;			/* Callback function */
56	void *cb_closure;		/* Closure for callback */
57	int w;				/* Status filled in by sigchld */
58	void *wchan;			/* Wait channel */
59};
60
61extern qelem proc_list_head;
62qelem proc_list_head = { &proc_list_head, &proc_list_head };
63extern qelem proc_wait_list;
64qelem proc_wait_list = { &proc_wait_list, &proc_wait_list };
65
66int task_notify_todo;
67
68void
69ins_que(qelem *elem, qelem *pred)
70{
71	qelem *p = pred->q_forw;
72	elem->q_back = pred;
73	elem->q_forw = p;
74	pred->q_forw = elem;
75	p->q_back = elem;
76}
77
78void
79rem_que(qelem *elem)
80{
81	qelem *p = elem->q_forw;
82	qelem *p2 = elem->q_back;
83	p2->q_forw = p;
84	p->q_back = p2;
85}
86
87static pjob *
88sched_job(cb_fun cf, void *ca)
89{
90	pjob *p = ALLOC(pjob);
91
92	p->cb_fun = cf;
93	p->cb_closure = ca;
94
95	/*
96	 * Now place on wait queue
97	 */
98	ins_que(&p->hdr, &proc_wait_list);
99
100	return p;
101}
102
103void
104run_task(task_fun tf, void *ta, cb_fun cf, void *ca)
105{
106	pjob *p = sched_job(cf, ca);
107	sigset_t mask, omask;
108
109	p->wchan = p;
110
111	sigemptyset(&mask);
112	sigaddset(&mask, SIGCHLD);
113	sigprocmask(SIG_BLOCK, &mask, &omask);
114
115	if ((p->pid = background())) {
116		sigprocmask(SIG_SETMASK, &omask, NULL);
117		return;
118	}
119
120	exit((*tf)(ta));
121	/* firewall... */
122	abort();
123}
124
125/*
126 * Schedule a task to be run when woken up
127 */
128void
129sched_task(cb_fun cf, void *ca, void *wchan)
130{
131	/*
132	 * Allocate a new task
133	 */
134	pjob *p = sched_job(cf, ca);
135#ifdef DEBUG_SLEEP
136	dlog("SLEEP on %#x", wchan);
137#endif
138	p->wchan = wchan;
139	p->pid = 0;
140	bzero(&p->w, sizeof(p->w));
141}
142
143static void
144wakeupjob(pjob *p)
145{
146	rem_que(&p->hdr);
147	ins_que(&p->hdr, &proc_list_head);
148	task_notify_todo++;
149}
150
151void
152wakeup(void *wchan)
153{
154	pjob *p, *p2;
155#ifdef DEBUG_SLEEP
156	int done = 0;
157#endif
158	if (!foreground)
159		return;
160
161#ifdef DEBUG_SLEEP
162	/*dlog("wakeup(%#x)", wchan);*/
163#endif
164	/*
165	 * Can't user ITER() here because
166	 * wakeupjob() juggles the list.
167	 */
168	for (p = FIRST(pjob, &proc_wait_list);
169			p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
170			p = p2) {
171		if (p->wchan == wchan) {
172#ifdef DEBUG_SLEEP
173			done = 1;
174#endif
175			wakeupjob(p);
176		}
177	}
178
179#ifdef DEBUG_SLEEP
180	if (!done)
181		dlog("Nothing SLEEPing on %#x", wchan);
182#endif
183}
184
185void
186wakeup_task(int rc, int term, void *cl)
187{
188	wakeup(cl);
189}
190
191
192void
193sigchld(int sig)
194{
195	int w;
196	int save_errno = errno;
197	pid_t pid;
198
199	while ((pid = waitpid((pid_t)-1, &w, WNOHANG)) > 0) {
200		pjob *p, *p2;
201
202		if (WIFSIGNALED(w))
203			plog(XLOG_ERROR, "Process %ld exited with signal %d",
204				(long)pid, WTERMSIG(w));
205#ifdef DEBUG
206		else
207			dlog("Process %ld exited with status %d",
208				(long)pid, WEXITSTATUS(w));
209#endif /* DEBUG */
210
211		for (p = FIRST(pjob, &proc_wait_list);
212		     p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
213		     p = p2) {
214			if (p->pid == pid) {
215				p->w = w;
216				wakeupjob(p);
217				break;
218			}
219		}
220
221#ifdef DEBUG
222		if (p == NULL)
223			dlog("can't locate task block for pid %ld", (long)pid);
224#endif /* DEBUG */
225	}
226
227	if (select_intr_valid)
228		longjmp(select_intr, sig);
229	errno = save_errno;
230}
231
232/*
233 * Run any pending tasks.
234 * This must be called with SIGCHLD disabled
235 */
236void
237do_task_notify(void)
238{
239	/*
240	 * Keep taking the first item off the list and processing it.
241	 *
242	 * Done this way because the callback can, quite reasonably,
243	 * queue a new task, so no local reference into the list can be
244	 * held here.
245	 */
246	while (FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
247		pjob *p = FIRST(pjob, &proc_list_head);
248		rem_que(&p->hdr);
249		/*
250		 * This job has completed
251		 */
252		--task_notify_todo;
253
254		/*
255		 * Do callback if it exists
256		 */
257		if (p->cb_fun)
258			(*p->cb_fun)(WIFEXITED(p->w) ? WEXITSTATUS(p->w) : 0,
259				WIFSIGNALED(p->w) ? WTERMSIG(p->w) : 0,
260				p->cb_closure);
261
262		free(p);
263	}
264}
265