worker: rewrite error handling so os._exit covers all cases
Previously the worker error handling is like:
pid = os.fork() --+
if pid == 0: |
.... | problematic
.... --+
try: --+
.... | worker error handling
--+
If a signal arrives when Python is executing the "problematic" lines, an
external error handling (dispatch.py) will take over the control flow and
it's no longer guaranteed "os._exit" is called (see
86cd09bc13ba for why it
is necessary).
This patch rewrites the error handling so it covers all possible code paths
for a worker even during fork.
Note: "os.getpid() == parentpid" is used to test if the process is parent or
not intentionally, instead of checking "pid", because "pid = os.fork()" may
be not atomic - it's possible that that a signal hits the worker before the
assignment completes [1]. The newly added test replaces "os.fork" to
exercise that extreme case.
[1]: CPython compiles "pid = os.fork()" to 2 byte codes: "CALL_FUNCTION" and
"STORE_FAST", so it's probably not atomic:
def f():
pid = os.fork()
dis.dis(f)
2 0 LOAD_GLOBAL 0 (os)
3 LOAD_ATTR 1 (fork)
6 CALL_FUNCTION 0
9 STORE_FAST 0 (pid)
12 LOAD_CONST 0 (None)
15 RETURN_VALUE
HG = $(CURDIR)/../../hg
TARGET = chg
SRCS = chg.c hgclient.c procutil.c util.c
OBJS = $(SRCS:.c=.o)
CFLAGS ?= -O2 -Wall -Wextra -pedantic -g
CPPFLAGS ?= -D_FORTIFY_SOURCE=2
override CFLAGS += -std=gnu99
ifdef HGPATH
override CPPFLAGS += -DHGPATH=\"$(HGPATH)\"
endif
DESTDIR =
PREFIX = /usr/local
MANDIR = $(PREFIX)/share/man/man1
CHGSOCKDIR = /tmp/chg$(shell id -u)
CHGSOCKNAME = $(CHGSOCKDIR)/server
.PHONY: all
all: $(TARGET)
$(TARGET): $(OBJS)
$(CC) $(LDFLAGS) -o $@ $(OBJS)
chg.o: hgclient.h procutil.h util.h
hgclient.o: hgclient.h procutil.h util.h
procutil.o: procutil.h util.h
util.o: util.h
.PHONY: install
install: $(TARGET)
install -d $(DESTDIR)$(PREFIX)/bin
install -m 755 $(TARGET) $(DESTDIR)$(PREFIX)/bin
install -d $(DESTDIR)$(MANDIR)
install -m 644 chg.1 $(DESTDIR)$(MANDIR)
.PHONY: serve
serve:
[ -d $(CHGSOCKDIR) ] || ( umask 077; mkdir $(CHGSOCKDIR) )
$(HG) serve --cwd / --cmdserver chgunix \
--address $(CHGSOCKNAME) \
--config cmdserver.log=/dev/stderr
.PHONY: clean
clean:
$(RM) $(OBJS)
.PHONY: distclean
distclean:
$(RM) $(OBJS) $(TARGET)