##// END OF EJS Templates
worker: rewrite error handling so os._exit covers all cases...
Jun Wu -
r32112:31763785 default
parent child Browse files
Show More
@@ -134,37 +134,43 b' def _posixworker(ui, func, staticargs, a'
134 killworkers()
134 killworkers()
135 oldchldhandler = signal.signal(signal.SIGCHLD, sigchldhandler)
135 oldchldhandler = signal.signal(signal.SIGCHLD, sigchldhandler)
136 ui.flush()
136 ui.flush()
137 parentpid = os.getpid()
137 for pargs in partition(args, workers):
138 for pargs in partition(args, workers):
138 pid = os.fork()
139 # make sure we use os._exit in all worker code paths. otherwise the
139 if pid == 0:
140 # worker may do some clean-ups which could cause surprises like
140 signal.signal(signal.SIGINT, oldhandler)
141 # deadlock. see sshpeer.cleanup for example.
141 signal.signal(signal.SIGCHLD, oldchldhandler)
142 # override error handling *before* fork. this is necessary because
142
143 # exception (signal) may arrive after fork, before "pid =" assignment
143 def workerfunc():
144 # completes, and other exception handler (dispatch.py) can lead to
144 os.close(rfd)
145 # unexpected code path without os._exit.
145 for i, item in func(*(staticargs + (pargs,))):
146 ret = -1
146 os.write(wfd, '%d %s\n' % (i, item))
147 try:
147 return 0
148 pid = os.fork()
149 if pid == 0:
150 signal.signal(signal.SIGINT, oldhandler)
151 signal.signal(signal.SIGCHLD, oldchldhandler)
148
152
149 # make sure we use os._exit in all code paths. otherwise the worker
153 def workerfunc():
150 # may do some clean-ups which could cause surprises like deadlock.
154 os.close(rfd)
151 # see sshpeer.cleanup for example.
155 for i, item in func(*(staticargs + (pargs,))):
152 ret = 0
156 os.write(wfd, '%d %s\n' % (i, item))
153 try:
157 return 0
158
159 ret = scmutil.callcatch(ui, workerfunc)
160 except: # parent re-raises, child never returns
161 if os.getpid() == parentpid:
162 raise
163 exctype = sys.exc_info()[0]
164 force = not issubclass(exctype, KeyboardInterrupt)
165 ui.traceback(force=force)
166 finally:
167 if os.getpid() != parentpid:
154 try:
168 try:
155 ret = scmutil.callcatch(ui, workerfunc)
156 finally:
157 ui.flush()
169 ui.flush()
158 except KeyboardInterrupt:
170 except: # never returns, no re-raises
159 os._exit(255)
171 pass
160 except: # never return, therefore no re-raises
161 try:
162 ui.traceback(force=True)
163 ui.flush()
164 finally:
172 finally:
165 os._exit(255)
173 os._exit(ret & 255)
166 else:
167 os._exit(ret & 255)
168 pids.add(pid)
174 pids.add(pid)
169 os.close(wfd)
175 os.close(wfd)
170 fp = os.fdopen(rfd, pycompat.sysstr('rb'), 0)
176 fp = os.fdopen(rfd, pycompat.sysstr('rb'), 0)
@@ -91,4 +91,36 b' Traceback must be printed for unknown ex'
91 > test 100000.0 exc 2>&1 | grep '^Traceback'
91 > test 100000.0 exc 2>&1 | grep '^Traceback'
92 Traceback (most recent call last):
92 Traceback (most recent call last):
93
93
94 Workers should not do cleanups in all cases
95
96 $ cat > $TESTTMP/detectcleanup.py <<EOF
97 > from __future__ import absolute_import
98 > import atexit
99 > import os
100 > import time
101 > oldfork = os.fork
102 > count = 0
103 > parentpid = os.getpid()
104 > def delayedfork():
105 > global count
106 > count += 1
107 > pid = oldfork()
108 > # make it easier to test SIGTERM hitting other workers when they have
109 > # not set up error handling yet.
110 > if count > 1 and pid == 0:
111 > time.sleep(0.1)
112 > return pid
113 > os.fork = delayedfork
114 > def cleanup():
115 > if os.getpid() != parentpid:
116 > os.write(1, 'should never happen\n')
117 > atexit.register(cleanup)
118 > EOF
119
120 $ hg --config "extensions.t=$abspath" --config worker.numcpus=8 --config \
121 > "extensions.d=$TESTTMP/detectcleanup.py" test 100000 abort
122 start
123 abort: known exception
124 [255]
125
94 #endif
126 #endif
General Comments 0
You need to be logged in to leave comments. Login now