#include "am.h"
#include <signal.h>
#include <sys/wait.h>
#include <setjmp.h>
extern jmp_buf select_intr;
extern int select_intr_valid;
typedef struct pjob pjob;
struct pjob {
qelem hdr;
pid_t pid;
cb_fun cb_fun;
void *cb_closure;
int w;
void *wchan;
};
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;
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 = p;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, &omask);
if ((p->pid = background())) {
sigprocmask(SIG_SETMASK, &omask, NULL);
return;
}
exit((*tf)(ta));
abort();
}
void
sched_task(cb_fun cf, void *ca, void *wchan)
{
pjob *p = sched_job(cf, ca);
#ifdef DEBUG_SLEEP
dlog("SLEEP on %#x", wchan);
#endif
p->wchan = wchan;
p->pid = 0;
bzero(&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
#endif
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);
}
void
sigchld(int sig)
{
int w;
int save_errno = errno;
pid_t pid;
while ((pid = waitpid((pid_t)-1, &w, WNOHANG)) > 0) {
pjob *p, *p2;
if (WIFSIGNALED(w))
plog(XLOG_ERROR, "Process %ld exited with signal %d",
(long)pid, WTERMSIG(w));
#ifdef DEBUG
else
dlog("Process %ld exited with status %d",
(long)pid, WEXITSTATUS(w));
#endif
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
}
if (select_intr_valid)
longjmp(select_intr, sig);
errno = save_errno;
}
void
do_task_notify(void)
{
while (FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
pjob *p = FIRST(pjob, &proc_list_head);
rem_que(&p->hdr);
--task_notify_todo;
if (p->cb_fun)
(*p->cb_fun)(WIFEXITED(p->w) ? WEXITSTATUS(p->w) : 0,
WIFSIGNALED(p->w) ? WTERMSIG(p->w) : 0,
p->cb_closure);
free(p);
}
}