parallel_emerge: wrap Popen with a lock

Two simultaneous calls to Popen is believe to be the cause of crbug.com/433482.
Wrap Popen with a lock to avoid it.

BUG=chromium:433482
TEST=trybot run on daisy, link, gizmo, lumpy-incremental, chromiumos-sdk,
  beaglebone.
TEST=Add a print statement to the wrapper. The wrapped Popen is called
throughout the portage code.

Change-Id: Id2dc53078753ea6962ce68c348dd571817f385bd
Reviewed-on: https://chromium-review.googlesource.com/234193
Reviewed-by: Mike Frysinger <vapier@chromium.org>
Tested-by: Bertrand Simonnet <bsimonnet@chromium.org>
Commit-Queue: Bertrand Simonnet <bsimonnet@chromium.org>
diff --git a/scripts/parallel_emerge.py b/scripts/parallel_emerge.py
index 837bd41..4eee7bb 100644
--- a/scripts/parallel_emerge.py
+++ b/scripts/parallel_emerge.py
@@ -30,6 +30,7 @@
   # pylint: disable=F0401
   import queue as Queue
 import signal
+import subprocess
 import sys
 import tempfile
 import threading
@@ -56,6 +57,18 @@
   if homedir:
     os.environ["PORTAGE_USERNAME"] = os.path.basename(homedir)
 
+# Wrap Popen with a lock to ensure no two Popen are executed simultaneously in
+# the same process.
+# Two Popen call at the same time might be the cause for crbug.com/433482.
+_popen_lock = threading.Lock()
+_old_popen = subprocess.Popen
+
+def _LockedPopen(*args, **kwargs):
+  with _popen_lock:
+    return _old_popen(*args, **kwargs)
+
+subprocess.Popen = _LockedPopen
+
 # Portage doesn't expose dependency trees in its public API, so we have to
 # make use of some private APIs here. These modules are found under
 # /usr/lib/portage/pym/.