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