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
#require test-repo
$ . "$TESTDIR/helpers-testrepo.sh"
$ check_code="$TESTDIR"/../contrib/check-code.py
$ cd "$TESTDIR"/..
New errors are not allowed. Warnings are strongly discouraged.
(The writing "no-che?k-code" is for not skipping this file when checking.)
$ hg locate -X contrib/python-zstandard -X hgext/fsmonitor/pywatchman |
> sed 's-\\-/-g' | "$check_code" --warnings --per-file=0 - || false
contrib/perf.py:869:
> r.revision(r.node(x))
don't convert rev to node before passing to revision(nodeorrev)
Skipping i18n/polib.py it has no-che?k-code (glob)
mercurial/demandimport.py:313:
> if os.environ.get('HGDEMANDIMPORT') != 'disable':
use encoding.environ instead (py3)
mercurial/encoding.py:54:
> environ = os.environ
use encoding.environ instead (py3)
mercurial/encoding.py:56:
> environ = os.environb
use encoding.environ instead (py3)
mercurial/encoding.py:61:
> for k, v in os.environ.items())
use encoding.environ instead (py3)
mercurial/encoding.py:221:
> for k, v in os.environ.items())
use encoding.environ instead (py3)
Skipping mercurial/httpclient/__init__.py it has no-che?k-code (glob)
Skipping mercurial/httpclient/_readers.py it has no-che?k-code (glob)
mercurial/policy.py:46:
> if 'HGMODULEPOLICY' in os.environ:
use encoding.environ instead (py3)
mercurial/policy.py:47:
> policy = os.environ['HGMODULEPOLICY'].encode('utf-8')
use encoding.environ instead (py3)
mercurial/policy.py:49:
> policy = os.environ.get('HGMODULEPOLICY', policy)
use encoding.environ instead (py3)
Skipping mercurial/statprof.py it has no-che?k-code (glob)
Skipping tests/badserverext.py it has no-che?k-code (glob)
[1]
@commands in debugcommands.py should be in alphabetical order.
>>> import re
>>> commands = []
>>> with open('mercurial/debugcommands.py', 'rb') as fh:
... for line in fh:
... m = re.match("^@command\('([a-z]+)", line)
... if m:
... commands.append(m.group(1))
>>> scommands = list(sorted(commands))
>>> for i, command in enumerate(scommands):
... if command != commands[i]:
... print('commands in debugcommands.py not sorted; first differing '
... 'command is %s; expected %s' % (commands[i], command))
... break
Prevent adding new files in the root directory accidentally.
$ hg files 'glob:*'
.editorconfig
.hgignore
.hgsigs
.hgtags
CONTRIBUTING
CONTRIBUTORS
COPYING
Makefile
README
hg
hgeditor
hgweb.cgi
setup.py