Skip to content

Commit

Permalink
JOB CONTROL wryun#1: Create and delete process groups
Browse files Browse the repository at this point in the history
The $job-control variable allows es to claim its own process group.
Then the %make-job command groups child processes under their own pgrps.
The wait command is extended significantly to support waiting for pgrps
as well as pids.
  • Loading branch information
jpco committed Jan 30, 2024
1 parent 1d6963e commit f21dc85
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 51 deletions.
17 changes: 9 additions & 8 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -49,18 +49,18 @@ LIBS = $(ADDLIBS) @LIBS@

VPATH = $(srcdir)

HFILES = config.h es.h gc.h input.h prim.h print.h sigmsgs.h \
HFILES = config.h es.h gc.h input.h prim.h print.h proc.h sigmsgs.h \
stdenv.h syntax.h term.h var.h
CFILES = access.c closure.c conv.c dict.c eval.c except.c fd.c gc.c glob.c \
glom.c input.c heredoc.c list.c main.c match.c open.c opt.c \
prim-ctl.c prim-etc.c prim-io.c prim-sys.c prim.c print.c proc.c \
sigmsgs.c signal.c split.c status.c str.c syntax.c term.c token.c \
tree.c util.c var.c vec.c version.c y.tab.c dump.c
prim-ctl.c prim-etc.c prim-io.c prim-job.c prim-sys.c prim.c print.c \
proc.c sigmsgs.c signal.c split.c status.c str.c syntax.c term.c \
token.c tree.c util.c var.c vec.c version.c y.tab.c dump.c
OFILES = access.o closure.o conv.o dict.o eval.o except.o fd.o gc.o glob.o \
glom.o input.o heredoc.o list.o main.o match.o open.o opt.o \
prim-ctl.o prim-etc.o prim-io.o prim-sys.o prim.o print.o proc.o \
sigmsgs.o signal.o split.o status.o str.o syntax.o term.o token.o \
tree.o util.o var.o vec.o version.o y.tab.o
prim-ctl.o prim-etc.o prim-io.o prim-job.o prim-sys.o prim.o print.o \
proc.o sigmsgs.o signal.o split.o status.o str.o syntax.o term.o \
token.o tree.o util.o var.o vec.o version.o y.tab.o
OTHER = Makefile parse.y mksignal
GEN = esdump y.tab.c y.tab.h y.output token.h sigmsgs.c initial.c

Expand Down Expand Up @@ -135,9 +135,10 @@ prim.o : prim.c es.h config.h stdenv.h prim.h
prim-ctl.o : prim-ctl.c es.h config.h stdenv.h prim.h
prim-etc.o : prim-etc.c es.h config.h stdenv.h prim.h
prim-io.o : prim-io.c es.h config.h stdenv.h gc.h prim.h
prim-job.o : prim-job.c es.h config.h stdenv.h gc.h prim.h proc.h
prim-sys.o : prim-sys.c es.h config.h stdenv.h prim.h
print.o : print.c es.h config.h stdenv.h print.h
proc.o : proc.c es.h config.h stdenv.h prim.h
proc.o : proc.c es.h config.h stdenv.h prim.h proc.h
signal.o : signal.c es.h config.h stdenv.h sigmsgs.h
split.o : split.c es.h config.h stdenv.h gc.h
status.o : status.c es.h config.h stdenv.h term.h
Expand Down
17 changes: 17 additions & 0 deletions initial.es
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,22 @@ fn %pathsearch name { access -n $name -1e -xf $path }
if {~ <=$&primitives execfailure} {fn-%exec-failure = $&execfailure}


#
# Job control
#

# Job control in es is enabled through the use of two built-ins:
# the job-control variable and the %make-job built-in.

job-control = uninitialized
set-job-control = $&setjobcontrol

fn-%make-job = $&makejob
fn-%run = %make-job $&run
fn-%pipe = %make-job $&pipe
fn-%background = %make-job $&background


#
# Read-eval-print loops
#
Expand Down Expand Up @@ -631,6 +647,7 @@ fn-%batch-loop = $&batchloop
fn-%is-interactive = $&isinteractive

fn %interactive-loop {
local (job-control = <=true)
let (result = <=true) {
catch @ e type msg {
if {~ $e eof} {
Expand Down
9 changes: 6 additions & 3 deletions prim-io.c
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "es.h"
#include "gc.h"
#include "prim.h"
#include "proc.h"

static const char *caller;

Expand Down Expand Up @@ -204,7 +205,7 @@ PRIM(pipe) {

for (;; list = list->next) {
int p[2], pid;

pid = (list->next == NULL) ? efork(TRUE) : pipefork(p, &inpipe);

if (pid == 0) { /* child */
Expand Down Expand Up @@ -232,12 +233,14 @@ PRIM(pipe) {
}

Ref(List *, result, NULL);
do {
if (forkjob != NULL) {
result = ewaitfor(pids[0]);
} else do {
Ref(List *, status, ewaitfor(pids[--n]));
printstatus(0, status);
result = append(status, result);
RefEnd(status);
} while (0 < n);
printstatus(0, result);
if (evalflags & eval_inchild)
exit(exitstatus(result));
RefReturn(result);
Expand Down
127 changes: 127 additions & 0 deletions prim-job.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/* prim-job.c -- job control primitives */

#include "es.h"
#include "prim.h"
#include "proc.h"

Boolean jobcontrol = FALSE;
jobcontext *forkjob = NULL;

static int initpgid = -1;

/* Highly redundant with $&newpgrp :( */
static int enablejobcontrol(void) {
int espgid, tcpgrp;
Sigeffect tstp, ttin, ttou;

while ((tcpgrp = tcgetpgrp(0)) != -1 && tcpgrp != (espgid = getpgrp()))
kill(-espgid, SIGTTIN);
if (tcpgrp == -1)
return errno;

espgid = getpid();
if (setpgrp(0, espgid) < 0)
return errno;

tstp = esignal(SIGTSTP, sig_ignore);
ttin = esignal(SIGTTIN, sig_ignore);
ttou = esignal(SIGTTOU, sig_ignore);

if (tcsetpgrp(0, espgid) < 0)
return errno;

esignal(SIGTSTP, tstp);
esignal(SIGTTIN, ttin);
esignal(SIGTTOU, ttou);

return 0;
}

static int disablejobcontrol(void) {
assert(initpgid >= 0);

/* use stderr: on exit, stdin may have EOF'd */
if (tcsetpgrp(2, initpgid) < 0)
return errno;

if (setpgrp(0, initpgid) < 0)
return errno;

return 0;
}

/* Enable or disable job control based on the truthiness of list.
* Note that enabling job control may be impossible due to an error or other
* factors, in which case the value returned here will instead be a stringified
* message describing why job control is disabled.
*
* if you aren't toggling the binary value of jobcontrol, then this will let
* you set whatever value you want. */

PRIM(setjobcontrol) {
int err;
Boolean enable = istrue(list);

if (initpgid == -1)
initpgid = getpgrp();

if (jobcontrol == enable)
return list;

if (enable)
if (isatty(0)) err = enablejobcontrol();
else return mklist(mkstr("stdin is not a terminal"), NULL);
else err = disablejobcontrol();

if (err != 0) {
/* unwinding enablement may fail too, but try it anyway. */
if (enable) disablejobcontrol();
return mklist(mkstr(esstrerror(err)), NULL);
}

jobcontrol = enable;
return list;
}

/* make a new "job" (pgrp) for processes to be added into. note that this
* function doesn't actually _make_ the Job, but just creates a signal to
* efork() that it will need to if it wants to make a new Proc */

PRIM(makejob) {
jobcontext jc, *prev = NULL;

if (jobcontrol) {
/* start with no Job in the context. this will be created when
* the first Proc is. */
jc.job = NULL;

/* we do a stack of jobcontexts, for hygiene reasons. */
prev = forkjob;
forkjob = &jc;
} else {
prev = forkjob;
forkjob = NULL;
}

ExceptionHandler

list = eval(list, NULL, evalflags);

CatchException (e)

forkjob = prev;
throw(e);

EndExceptionHandler

/* we don't clean up jc.job in here -- if it isn't null, then it was
* also added to joblist, where it's now somebody else's problem. */
forkjob = prev;
return list;
}

extern Dict *initprims_jobcontrol(Dict *primdict) {
X(setjobcontrol);
X(makejob);
return primdict;
}
6 changes: 4 additions & 2 deletions prim-sys.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include "es.h"
#include "prim.h"
#include "proc.h"

#ifdef HAVE_SETRLIMIT
# define BSD_LIMITS 1
Expand Down Expand Up @@ -46,8 +47,9 @@ PRIM(background) {
int pid = efork(TRUE);
if (pid == 0) {
#if JOB_PROTECT
/* job control safe version: put it in a new pgroup. */
setpgrp(0, getpid());
/* job control safe version: put it in a new pgroup if it isn't already. */
if (forkjob == NULL)
setpgrp(0, getpid());
#endif
mvfd(eopen("/dev/null", oOpen), 0);
exit(exitstatus(eval(list, NULL, evalflags | eval_inchild)));
Expand Down
1 change: 1 addition & 0 deletions prim.c
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ extern void initprims(void) {

prims = initprims_controlflow(prims);
prims = initprims_io(prims);
prims = initprims_jobcontrol(prims);
prims = initprims_etc(prims);
prims = initprims_sys(prims);
prims = initprims_proc(prims);
Expand Down
1 change: 1 addition & 0 deletions prim.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

extern Dict *initprims_controlflow(Dict *primdict); /* prim-ctl.c */
extern Dict *initprims_io(Dict *primdict); /* prim-io.c */
extern Dict *initprims_jobcontrol(Dict *primdict); /* prim-job.c */
extern Dict *initprims_etc(Dict *primdict); /* prim-etc.c */
extern Dict *initprims_sys(Dict *primdict); /* prim-sys.c */
extern Dict *initprims_proc(Dict *primdict); /* proc.c */
Expand Down
Loading

0 comments on commit f21dc85

Please sign in to comment.