sched.c revision 174294
1/*
2 * Copyright (c) 1997-2006 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 *
40 * File: am-utils/amd/sched.c
41 *
42 */
43
44/*
45 * Process scheduler
46 */
47
48#ifdef HAVE_CONFIG_H
49# include <config.h>
50#endif /* HAVE_CONFIG_H */
51#include <am_defs.h>
52#include <amd.h>
53
54
55typedef struct pjob pjob;
56
57struct pjob {
58  qelem hdr;		/* Linked list */
59  int pid;		/* Process ID of job */
60  cb_fun *cb_fun;	/* Callback function */
61  opaque_t cb_arg;	/* Argument for callback */
62  int w;		/* everyone these days uses int, not a "union wait" */
63  wchan_t wchan;	/* Wait channel */
64};
65
66/* globals */
67qelem proc_list_head = {&proc_list_head, &proc_list_head};
68qelem proc_wait_list = {&proc_wait_list, &proc_wait_list};
69int task_notify_todo;
70
71
72void
73ins_que(qelem *elem, qelem *pred)
74{
75  qelem *p = pred->q_forw;
76
77  elem->q_back = pred;
78  elem->q_forw = p;
79  pred->q_forw = elem;
80  p->q_back = elem;
81}
82
83
84void
85rem_que(qelem *elem)
86{
87  qelem *p = elem->q_forw;
88  qelem *p2 = elem->q_back;
89
90  p2->q_forw = p;
91  p->q_back = p2;
92}
93
94
95static pjob *
96sched_job(cb_fun *cf, opaque_t ca)
97{
98  pjob *p = ALLOC(struct pjob);
99
100  p->cb_fun = cf;
101  p->cb_arg = ca;
102
103  /*
104   * Now place on wait queue
105   */
106  ins_que(&p->hdr, &proc_wait_list);
107
108  return p;
109}
110
111
112/*
113 * tf: The task to execute (ta is its arguments)
114 * cf: Continuation function (ca is its arguments)
115 */
116void
117run_task(task_fun *tf, opaque_t ta, cb_fun *cf, opaque_t ca)
118{
119  pjob *p = sched_job(cf, ca);
120#ifdef HAVE_SIGACTION
121  sigset_t new, mask;
122#else /* not HAVE_SIGACTION */
123  int mask;
124#endif /* not HAVE_SIGACTION */
125
126  p->wchan = (wchan_t) p;
127
128#ifdef HAVE_SIGACTION
129  sigemptyset(&new);		/* initialize signal set we wish to block */
130  sigaddset(&new, SIGCHLD);	/* only block on SIGCHLD */
131  sigprocmask(SIG_BLOCK, &new, &mask);
132#else /* not HAVE_SIGACTION */
133  mask = sigblock(sigmask(SIGCHLD));
134#endif /* not HAVE_SIGACTION */
135
136  if ((p->pid = background())) {
137#ifdef HAVE_SIGACTION
138    sigprocmask(SIG_SETMASK, &mask, NULL);
139#else /* not HAVE_SIGACTION */
140    sigsetmask(mask);
141#endif /* not HAVE_SIGACTION */
142    return;
143  }
144
145  /* child code runs here, parent has returned to caller */
146
147  exit((*tf) (ta));
148  /* firewall... */
149  abort();
150}
151
152
153/*
154 * Schedule a task to be run when woken up
155 */
156void
157sched_task(cb_fun *cf, opaque_t ca, wchan_t wchan)
158{
159  /*
160   * Allocate a new task
161   */
162  pjob *p = sched_job(cf, ca);
163
164  dlog("SLEEP on %p", wchan);
165  p->wchan = wchan;
166  p->pid = 0;
167  p->w = 0;			/* was memset (when ->w was union) */
168}
169
170
171static void
172wakeupjob(pjob *p)
173{
174  rem_que(&p->hdr);
175  ins_que(&p->hdr, &proc_list_head);
176  task_notify_todo++;
177}
178
179
180void
181wakeup(wchan_t wchan)
182{
183  pjob *p, *p2;
184
185  if (!foreground)
186    return;
187
188  /*
189   * Can't use ITER() here because
190   * wakeupjob() juggles the list.
191   */
192  for (p = AM_FIRST(pjob, &proc_wait_list);
193       p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
194       p = p2) {
195    if (p->wchan == wchan) {
196      wakeupjob(p);
197    }
198  }
199}
200
201
202void
203wakeup_task(int rc, int term, wchan_t wchan)
204{
205  wakeup(wchan);
206}
207
208
209wchan_t
210get_mntfs_wchan(mntfs *mf)
211{
212  if (mf &&
213      mf->mf_ops &&
214      mf->mf_ops->get_wchan)
215    return mf->mf_ops->get_wchan(mf);
216  return mf;
217}
218
219
220/*
221 * Run any pending tasks.
222 * This must be called with SIGCHLD disabled
223 */
224void
225do_task_notify(void)
226{
227  /*
228   * Keep taking the first item off the list and processing it.
229   *
230   * Done this way because the callback can, quite reasonably,
231   * queue a new task, so no local reference into the list can be
232   * held here.
233   */
234  while (AM_FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
235    pjob *p = AM_FIRST(pjob, &proc_list_head);
236    rem_que(&p->hdr);
237    /*
238     * This job has completed
239     */
240    --task_notify_todo;
241
242    /*
243     * Do callback if it exists
244     */
245    if (p->cb_fun) {
246      /* these two trigraphs will ensure compatibility with strict POSIX.1 */
247      p->cb_fun(WIFEXITED(p->w)   ? WEXITSTATUS(p->w) : 0,
248		WIFSIGNALED(p->w) ? WTERMSIG(p->w)    : 0,
249		p->cb_arg);
250    }
251    XFREE(p);
252  }
253}
254
255
256RETSIGTYPE
257sigchld(int sig)
258{
259  int w;	/* everyone these days uses int, not a "union wait" */
260  int pid;
261
262#ifdef HAVE_WAITPID
263  while ((pid = waitpid((pid_t) -1,  &w, WNOHANG)) > 0) {
264#else /* not HAVE_WAITPID */
265  while ((pid = wait3( &w, WNOHANG, (struct rusage *) 0)) > 0) {
266#endif /* not HAVE_WAITPID */
267    pjob *p, *p2;
268
269    if (WIFSIGNALED(w))
270      plog(XLOG_ERROR, "Process %d exited with signal %d",
271	   pid, WTERMSIG(w));
272    else
273      dlog("Process %d exited with status %d",
274	   pid, WEXITSTATUS(w));
275
276    for (p = AM_FIRST(pjob, &proc_wait_list);
277	 p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
278	 p = p2) {
279      if (p->pid == pid) {
280	p->w = w;
281	wakeupjob(p);
282	break;
283      }
284    } /* end of for loop */
285
286    if (p == HEAD(pjob, &proc_wait_list))
287      dlog("can't locate task block for pid %d", pid);
288
289    /*
290     * Must count down children inside the while loop, otherwise we won't
291     * count them all, and NumChildren (and later backoff) will be set
292     * incorrectly. SH/RUNIT 940519.
293     */
294    if (--NumChildren < 0)
295      NumChildren = 0;
296  } /* end of "while wait..." loop */
297
298#ifdef REINSTALL_SIGNAL_HANDLER
299  signal(sig, sigchld);
300#endif /* REINSTALL_SIGNAL_HANDLER */
301
302  if (select_intr_valid)
303    longjmp(select_intr, sig);
304}
305