/* $OpenBSD: sched.c,v 1.13 2004/10/21 20:57:08 millert Exp $ */ /* * Copyright (c) 1990 Jan-Simon Pendry * Copyright (c) 1990 Imperial College of Science, Technology & Medicine * Copyright (c) 1990, 1993 * The Regents of the University of California. All rights reserved. * * This code is derived from software contributed to Berkeley by * Jan-Simon Pendry at Imperial College, London. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * 3. Neither the name of the University nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * from: @(#)sched.c 8.1 (Berkeley) 6/6/93 * $Id: sched.c,v 1.13 2004/10/21 20:57:08 millert Exp $ */ /* * Process scheduler */ #include "am.h" #include #include WAIT #include extern jmp_buf select_intr; extern int select_intr_valid; typedef struct pjob pjob; struct pjob { qelem hdr; /* Linked list */ pid_t pid; /* Process ID of job */ cb_fun cb_fun; /* Callback function */ void *cb_closure; /* Closure for callback */ union wait w; /* Status filled in by sigchld */ void *wchan; /* Wait channel */ }; extern qelem proc_list_head; qelem proc_list_head = { &proc_list_head, &proc_list_head }; extern qelem proc_wait_list; qelem proc_wait_list = { &proc_wait_list, &proc_wait_list }; int task_notify_todo; void ins_que(qelem *elem, qelem *pred) { qelem *p = pred->q_forw; elem->q_back = pred; elem->q_forw = p; pred->q_forw = elem; p->q_back = elem; } void rem_que(qelem *elem) { qelem *p = elem->q_forw; qelem *p2 = elem->q_back; p2->q_forw = p; p->q_back = p2; } static pjob * sched_job(cb_fun cf, void *ca) { pjob *p = ALLOC(pjob); p->cb_fun = cf; p->cb_closure = ca; /* * Now place on wait queue */ ins_que(&p->hdr, &proc_wait_list); return p; } void run_task(task_fun tf, void *ta, cb_fun cf, void *ca) { pjob *p = sched_job(cf, ca); sigset_t mask, omask; p->wchan = (void *)p; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigprocmask(SIG_BLOCK, &mask, &omask); if ((p->pid = background())) { sigprocmask(SIG_SETMASK, &omask, NULL); return; } exit((*tf)(ta)); /* firewall... */ abort(); } /* * Schedule a task to be run when woken up */ void sched_task(cb_fun cf, void *ca, void *wchan) { /* * Allocate a new task */ pjob *p = sched_job(cf, ca); #ifdef DEBUG_SLEEP dlog("SLEEP on %#x", wchan); #endif p->wchan = wchan; p->pid = 0; bzero((void *)&p->w, sizeof(p->w)); } static void wakeupjob(pjob *p) { rem_que(&p->hdr); ins_que(&p->hdr, &proc_list_head); task_notify_todo++; } void wakeup(void *wchan) { pjob *p, *p2; #ifdef DEBUG_SLEEP int done = 0; #endif if (!foreground) return; #ifdef DEBUG_SLEEP /*dlog("wakeup(%#x)", wchan);*/ #endif /* * Can't user ITER() here because * wakeupjob() juggles the list. */ for (p = FIRST(pjob, &proc_wait_list); p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list); p = p2) { if (p->wchan == wchan) { #ifdef DEBUG_SLEEP done = 1; #endif wakeupjob(p); } } #ifdef DEBUG_SLEEP if (!done) dlog("Nothing SLEEPing on %#x", wchan); #endif } void wakeup_task(int rc, int term, void *cl) { wakeup(cl); } /*ARGSUSED*/ void sigchld(int sig) { union wait w; int save_errno = errno; pid_t pid; #ifdef SYS5_SIGNALS if ((pid = wait(&w)) > 0) { #else while ((pid = wait3((int *) &w, WNOHANG, (struct rusage *) 0)) > 0) { #endif /* SYS5_SIGNALS */ pjob *p, *p2; if (WIFSIGNALED(w)) plog(XLOG_ERROR, "Process %ld exited with signal %ld", (long)pid, w.w_termsig); #ifdef DEBUG else dlog("Process %ld exited with status %ld", (long)pid, w.w_retcode); #endif /* DEBUG */ for (p = FIRST(pjob, &proc_wait_list); p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list); p = p2) { if (p->pid == pid) { p->w = w; wakeupjob(p); break; } } #ifdef DEBUG if (p == NULL) dlog("can't locate task block for pid %ld", (long)pid); #endif /* DEBUG */ } #ifdef SYS5_SIGNALS signal(sig, sigchld); #endif /* SYS5_SIGNALS */ if (select_intr_valid) longjmp(select_intr, sig); errno = save_errno; } /* * Run any pending tasks. * This must be called with SIGCHLD disabled */ void do_task_notify(void) { /* * Keep taking the first item off the list and processing it. * * Done this way because the callback can, quite reasonably, * queue a new task, so no local reference into the list can be * held here. */ while (FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) { pjob *p = FIRST(pjob, &proc_list_head); rem_que(&p->hdr); /* * This job has completed */ --task_notify_todo; /* * Do callback if it exists */ if (p->cb_fun) (*p->cb_fun)(p->w.w_retcode, p->w.w_termsig, p->cb_closure); free((void *)p); } }