spawn: setup _exit finally block before fork
authorZac Medico <zmedico@gentoo.org>
Mon, 8 Oct 2012 15:43:21 +0000 (08:43 -0700)
committerZac Medico <zmedico@gentoo.org>
Mon, 8 Oct 2012 15:43:21 +0000 (08:43 -0700)
This is the most reliable way to handle the race condition.

pym/portage/process.py

index a3461f4742c1266b54ba961e57939654f5da1ea3..fbfbde04916cd64882407a317f3160c618a59bba 100644 (file)
@@ -256,24 +256,33 @@ def spawn(mycommand, env={}, opt_name=None, fd_pipes=None, returnpid=False,
                fd_pipes[1] = pw
                fd_pipes[2] = pw
 
-       pid = os.fork()
+       parent_pid = os.getpid()
+       pid = None
+       try:
+               pid = os.fork()
 
-       if pid == 0:
-               try:
-                       _exec(binary, mycommand, opt_name, fd_pipes,
-                             env, gid, groups, uid, umask, pre_exec)
-               except SystemExit:
-                       raise
-               except Exception as e:
-                       # We need to catch _any_ exception so that it doesn't
-                       # propagate out of this function and cause exiting
-                       # with anything other than os._exit()
-                       writemsg("%s:\n   %s\n" % (e, " ".join(mycommand)), noiselevel=-1)
-                       traceback.print_exc()
-                       sys.stderr.flush()
-               finally:
-                       # Call os._exit() from finally block, in order to suppress any
-                       # finally blocks from earlier in the call stack. See bug #345289.
+               if pid == 0:
+                       try:
+                               _exec(binary, mycommand, opt_name, fd_pipes,
+                                       env, gid, groups, uid, umask, pre_exec)
+                       except SystemExit:
+                               raise
+                       except Exception as e:
+                               # We need to catch _any_ exception so that it doesn't
+                               # propagate out of this function and cause exiting
+                               # with anything other than os._exit()
+                               writemsg("%s:\n   %s\n" % (e, " ".join(mycommand)),
+                                       noiselevel=-1)
+                               traceback.print_exc()
+                               sys.stderr.flush()
+
+       finally:
+               if pid == 0 or (pid is None and os.getpid() != parent_pid):
+                       # Call os._exit() from a finally block in order
+                       # to suppress any finally blocks from earlier
+                       # in the call stack (see bug #345289). This
+                       # finally block has to be setup before the fork
+                       # in order to avoid a race condition.
                        os._exit(1)
 
        if not isinstance(pid, int):