sched.c revision 82794
138494Sobrien/*
282794Sobrien * Copyright (c) 1997-2001 Erez Zadok
338494Sobrien * Copyright (c) 1990 Jan-Simon Pendry
438494Sobrien * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
538494Sobrien * Copyright (c) 1990 The Regents of the University of California.
638494Sobrien * All rights reserved.
738494Sobrien *
838494Sobrien * This code is derived from software contributed to Berkeley by
938494Sobrien * Jan-Simon Pendry at Imperial College, London.
1038494Sobrien *
1138494Sobrien * Redistribution and use in source and binary forms, with or without
1238494Sobrien * modification, are permitted provided that the following conditions
1338494Sobrien * are met:
1438494Sobrien * 1. Redistributions of source code must retain the above copyright
1538494Sobrien *    notice, this list of conditions and the following disclaimer.
1638494Sobrien * 2. Redistributions in binary form must reproduce the above copyright
1738494Sobrien *    notice, this list of conditions and the following disclaimer in the
1838494Sobrien *    documentation and/or other materials provided with the distribution.
1938494Sobrien * 3. All advertising materials mentioning features or use of this software
2042629Sobrien *    must display the following acknowledgment:
2138494Sobrien *      This product includes software developed by the University of
2238494Sobrien *      California, Berkeley and its contributors.
2338494Sobrien * 4. Neither the name of the University nor the names of its contributors
2438494Sobrien *    may be used to endorse or promote products derived from this software
2538494Sobrien *    without specific prior written permission.
2638494Sobrien *
2738494Sobrien * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
2838494Sobrien * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
2938494Sobrien * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
3038494Sobrien * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
3138494Sobrien * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
3238494Sobrien * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
3338494Sobrien * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
3438494Sobrien * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
3538494Sobrien * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
3638494Sobrien * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
3738494Sobrien * SUCH DAMAGE.
3838494Sobrien *
3938494Sobrien *      %W% (Berkeley) %G%
4038494Sobrien *
4182794Sobrien * $Id: sched.c,v 1.4.2.2 2001/01/10 03:23:12 ezk Exp $
4238494Sobrien *
4338494Sobrien */
4438494Sobrien
4538494Sobrien/*
4638494Sobrien * Process scheduler
4738494Sobrien */
4838494Sobrien
4938494Sobrien#ifdef HAVE_CONFIG_H
5038494Sobrien# include <config.h>
5138494Sobrien#endif /* HAVE_CONFIG_H */
5238494Sobrien#include <am_defs.h>
5338494Sobrien#include <amd.h>
5438494Sobrien
5538494Sobrien
5638494Sobrientypedef struct pjob pjob;
5738494Sobrien
5838494Sobrienstruct pjob {
5938494Sobrien  qelem hdr;			/* Linked list */
6038494Sobrien  int pid;			/* Process ID of job */
6138494Sobrien  cb_fun cb_fun;		/* Callback function */
6238494Sobrien  voidp cb_closure;		/* Closure for callback */
6338494Sobrien  int w;		/* everyone these days uses int, not a "union wait" */
6438494Sobrien  voidp wchan;			/* Wait channel */
6538494Sobrien};
6638494Sobrien
6738494Sobrien/* globals */
6838494Sobrienqelem proc_list_head = {&proc_list_head, &proc_list_head};
6938494Sobrienqelem proc_wait_list = {&proc_wait_list, &proc_wait_list};
7038494Sobrienint task_notify_todo;
7138494Sobrien
7238494Sobrien
7338494Sobrienvoid
7438494Sobrienins_que(qelem *elem, qelem *pred)
7538494Sobrien{
7638494Sobrien  qelem *p = pred->q_forw;
7738494Sobrien
7838494Sobrien  elem->q_back = pred;
7938494Sobrien  elem->q_forw = p;
8038494Sobrien  pred->q_forw = elem;
8138494Sobrien  p->q_back = elem;
8238494Sobrien}
8338494Sobrien
8438494Sobrien
8538494Sobrienvoid
8638494Sobrienrem_que(qelem *elem)
8738494Sobrien{
8838494Sobrien  qelem *p = elem->q_forw;
8938494Sobrien  qelem *p2 = elem->q_back;
9038494Sobrien
9138494Sobrien  p2->q_forw = p;
9238494Sobrien  p->q_back = p2;
9338494Sobrien}
9438494Sobrien
9538494Sobrien
9638494Sobrienstatic pjob *
9738494Sobriensched_job(cb_fun cf, voidp ca)
9838494Sobrien{
9938494Sobrien  pjob *p = ALLOC(struct pjob);
10038494Sobrien
10138494Sobrien  p->cb_fun = cf;
10238494Sobrien  p->cb_closure = ca;
10338494Sobrien
10438494Sobrien  /*
10538494Sobrien   * Now place on wait queue
10638494Sobrien   */
10738494Sobrien  ins_que(&p->hdr, &proc_wait_list);
10838494Sobrien
10938494Sobrien  return p;
11038494Sobrien}
11138494Sobrien
11238494Sobrien
11338494Sobrien/*
11438494Sobrien * tf: The task to execute (ta is its arguments)
11538494Sobrien * cf: Continuation function (ca is its arguments)
11638494Sobrien */
11738494Sobrienvoid
11838494Sobrienrun_task(task_fun tf, voidp ta, cb_fun cf, voidp ca)
11938494Sobrien{
12038494Sobrien  pjob *p = sched_job(cf, ca);
12138494Sobrien#ifdef HAVE_SIGACTION
12238494Sobrien  sigset_t new, mask;
12338494Sobrien#else /* not HAVE_SIGACTION */
12438494Sobrien  int mask;
12538494Sobrien#endif /* not HAVE_SIGACTION */
12638494Sobrien
12738494Sobrien  p->wchan = (voidp) p;
12838494Sobrien
12938494Sobrien#ifdef HAVE_SIGACTION
13042629Sobrien  sigemptyset(&new);		/* initialize signal set we wish to block */
13138494Sobrien  sigaddset(&new, SIGCHLD);	/* only block on SIGCHLD */
13238494Sobrien  sigprocmask(SIG_BLOCK, &new, &mask);
13338494Sobrien#else /* not HAVE_SIGACTION */
13438494Sobrien  mask = sigblock(sigmask(SIGCHLD));
13538494Sobrien#endif /* not HAVE_SIGACTION */
13638494Sobrien
13738494Sobrien  if ((p->pid = background())) {
13838494Sobrien#ifdef HAVE_SIGACTION
13938494Sobrien    sigprocmask(SIG_SETMASK, &mask, NULL);
14038494Sobrien#else /* not HAVE_SIGACTION */
14138494Sobrien    sigsetmask(mask);
14238494Sobrien#endif /* not HAVE_SIGACTION */
14338494Sobrien    return;
14438494Sobrien  }
14538494Sobrien
14638494Sobrien  /* child code runs here, parent have returned to caller */
14738494Sobrien
14838494Sobrien  exit((*tf) (ta));
14938494Sobrien  /* firewall... */
15038494Sobrien  abort();
15138494Sobrien}
15238494Sobrien
15338494Sobrien
15438494Sobrien/*
15538494Sobrien * Schedule a task to be run when woken up
15638494Sobrien */
15738494Sobrienvoid
15838494Sobriensched_task(cb_fun cf, voidp ca, voidp wchan)
15938494Sobrien{
16038494Sobrien  /*
16138494Sobrien   * Allocate a new task
16238494Sobrien   */
16338494Sobrien  pjob *p = sched_job(cf, ca);
16438494Sobrien
16538494Sobrien#ifdef DEBUG
16651292Sobrien  dlog("SLEEP on %#lx", (unsigned long) wchan);
16738494Sobrien#endif /* DEBUG */
16838494Sobrien  p->wchan = wchan;
16938494Sobrien  p->pid = 0;
17038494Sobrien  memset((voidp) &p->w, 0, sizeof(p->w));
17138494Sobrien}
17238494Sobrien
17338494Sobrien
17438494Sobrienstatic void
17538494Sobrienwakeupjob(pjob *p)
17638494Sobrien{
17738494Sobrien  rem_que(&p->hdr);
17838494Sobrien  ins_que(&p->hdr, &proc_list_head);
17938494Sobrien  task_notify_todo++;
18038494Sobrien}
18138494Sobrien
18238494Sobrien
18338494Sobrienvoid
18438494Sobrienwakeup(voidp wchan)
18538494Sobrien{
18638494Sobrien  pjob *p, *p2;
18738494Sobrien
18838494Sobrien  if (!foreground)
18938494Sobrien    return;
19038494Sobrien
19138494Sobrien  /*
19238494Sobrien   * Can't user ITER() here because
19338494Sobrien   * wakeupjob() juggles the list.
19438494Sobrien   */
19538494Sobrien  for (p = AM_FIRST(pjob, &proc_wait_list);
19638494Sobrien       p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
19738494Sobrien       p = p2) {
19838494Sobrien    if (p->wchan == wchan) {
19938494Sobrien      wakeupjob(p);
20038494Sobrien    }
20138494Sobrien  }
20238494Sobrien}
20338494Sobrien
20438494Sobrien
20538494Sobrienvoid
20638494Sobrienwakeup_task(int rc, int term, voidp cl)
20738494Sobrien{
20838494Sobrien  wakeup(cl);
20938494Sobrien}
21038494Sobrien
21138494Sobrien
21238494Sobrien/*
21338494Sobrien * Run any pending tasks.
21438494Sobrien * This must be called with SIGCHLD disabled
21538494Sobrien */
21638494Sobrienvoid
21738494Sobriendo_task_notify(void)
21838494Sobrien{
21938494Sobrien  /*
22038494Sobrien   * Keep taking the first item off the list and processing it.
22138494Sobrien   *
22282794Sobrien   * Done this way because the callback can, quite reasonably,
22338494Sobrien   * queue a new task, so no local reference into the list can be
22438494Sobrien   * held here.
22538494Sobrien   */
22638494Sobrien  while (AM_FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
22738494Sobrien    pjob *p = AM_FIRST(pjob, &proc_list_head);
22838494Sobrien    rem_que(&p->hdr);
22938494Sobrien    /*
23038494Sobrien     * This job has completed
23138494Sobrien     */
23238494Sobrien    --task_notify_todo;
23338494Sobrien
23438494Sobrien    /*
23538494Sobrien     * Do callback if it exists
23638494Sobrien     */
23738494Sobrien    if (p->cb_fun) {
23842629Sobrien      /* these two trigraphs will ensure compatibility with strict POSIX.1 */
23938494Sobrien      (*p->cb_fun) (WIFEXITED(p->w)   ? WEXITSTATUS(p->w) : 0,
24038494Sobrien		    WIFSIGNALED(p->w) ? WTERMSIG(p->w)	  : 0,
24138494Sobrien		    p->cb_closure);
24238494Sobrien    }
24338494Sobrien    XFREE(p);
24438494Sobrien  }
24538494Sobrien}
24638494Sobrien
24738494Sobrien
24838494SobrienRETSIGTYPE
24938494Sobriensigchld(int sig)
25038494Sobrien{
25138494Sobrien  int w;	/* everyone these days uses int, not a "union wait" */
25238494Sobrien  int pid;
25338494Sobrien
25438494Sobrien#ifdef HAVE_WAITPID
25538494Sobrien  while ((pid = waitpid((pid_t) -1,  &w, WNOHANG)) > 0) {
25638494Sobrien#else /* not HAVE_WAITPID */
25738494Sobrien  while ((pid = wait3( &w, WNOHANG, (struct rusage *) 0)) > 0) {
25838494Sobrien#endif /* not HAVE_WAITPID */
25938494Sobrien    pjob *p, *p2;
26038494Sobrien
26138494Sobrien    if (WIFSIGNALED(w))
26238494Sobrien      plog(XLOG_ERROR, "Process %d exited with signal %d",
26338494Sobrien	   pid, WTERMSIG(w));
26438494Sobrien#ifdef DEBUG
26538494Sobrien    else
26638494Sobrien      dlog("Process %d exited with status %d",
26738494Sobrien	   pid, WEXITSTATUS(w));
26838494Sobrien#endif /* DEBUG */
26938494Sobrien
27038494Sobrien    for (p = AM_FIRST(pjob, &proc_wait_list);
27138494Sobrien	 p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
27238494Sobrien	 p = p2) {
27338494Sobrien      if (p->pid == pid) {
27438494Sobrien	p->w = w;
27538494Sobrien	wakeupjob(p);
27638494Sobrien	break;
27738494Sobrien      }
27838494Sobrien    } /* end of for loop */
27938494Sobrien
28038494Sobrien#ifdef DEBUG
28138494Sobrien    if (!p)
28238494Sobrien      dlog("can't locate task block for pid %d", pid);
28338494Sobrien#endif /* DEBUG */
28438494Sobrien
28538494Sobrien    /*
28638494Sobrien     * Must count down children inside the while loop, otherwise we won't
28738494Sobrien     * count them all, and NumChild (and later backoff) will be set
28838494Sobrien     * incorrectly. SH/RUNIT 940519.
28938494Sobrien     */
29038494Sobrien    if (--NumChild < 0)
29138494Sobrien      NumChild = 0;
29238494Sobrien  } /* end of "while wait..." loop */
29338494Sobrien
29438494Sobrien#ifdef REINSTALL_SIGNAL_HANDLER
29538494Sobrien  signal(sig, sigchld);
29638494Sobrien#endif /* REINSTALL_SIGNAL_HANDLER */
29738494Sobrien
29838494Sobrien  if (select_intr_valid)
29938494Sobrien    longjmp(select_intr, sig);
30038494Sobrien}
301