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