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