Repository: lutris/lutris

Issue:
EVE Online interacts poorly Process Monitoring
I haven't been able to get EVE Online, installed via lutris.net script, using Lutris 0.4.23, to launch a client with Process Monitoring enabled. I speculate it may have something to do with the fact that "eve.exe" immediately launches another executable "luancher/evelauncher.exe" in its place.

Launching lutris interactively on the command line doesn't provide much more information about what's going on, the game exits on the "Thread aborting now" line.

```
INFO     2018-12-15 05:10:00,570 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/system32/dxgi with DXVK version
INFO     2018-12-15 05:10:00,571 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/system32/d3d11 with DXVK version
INFO     2018-12-15 05:10:00,571 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/system32/d3d10core with DXVK version
INFO     2018-12-15 05:10:00,571 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/system32/d3d10_1 with DXVK version
INFO     2018-12-15 05:10:00,571 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/system32/d3d10 with DXVK version
INFO     2018-12-15 05:10:00,571 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/syswow64/dxgi with DXVK version
INFO     2018-12-15 05:10:00,571 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/syswow64/d3d11 with DXVK version
INFO     2018-12-15 05:10:00,572 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/syswow64/d3d10core with DXVK version
INFO     2018-12-15 05:10:00,572 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/syswow64/d3d10_1 with DXVK version
INFO     2018-12-15 05:10:00,572 [dxvk]:Replacing /home/aopfer/Games/eve-online/drive_c/windows/syswow64/d3d10 with DXVK version
wine: cannot find L"C:\\windows\\system32\\winemenubuilder.exe"
[43:44:1215/051001.879:ERROR:network_change_notifier_win.cc(157)] WSALookupServiceBegin failed with: 8
[43:44:1215/051001.945:ERROR:gpu_process_transport_factory.cc(1019)] Lost UI shared context.
WARNING  2018-12-15 05:10:14,778 [thread]:Thread aborting now
/usr/lib/python3/dist-packages/lutris/thread.py:248: Warning: Source ID 3959 was not found when attempting to remove it
  GLib.source_remove(self.stdout_monitor)
```


Discussion:

Resolving patch:
diff --git a/lutris/thread.py b/lutris/thread.py
--- a/lutris/thread.py
+++ b/lutris/thread.py
@@ -11,6 +11,8 @@
 from collections import defaultdict
 from itertools import chain
 
+import ctypes
+from ctypes.util import find_library
 
 from textwrap import dedent
 from gi.repository import GLib
@@ -35,6 +37,9 @@
 ]
 
 
+PR_SET_CHILD_SUBREAPER = 36
+
+
 class LutrisThread(threading.Thread):
     """Run the game in a separate thread."""
     debug_output = True
@@ -125,6 +130,9 @@ def run(self):
         """Run the thread."""
         logger.debug("Command env: %s", self.env_string)
         logger.debug("Running command: %s", self.command_string)
+        result = ctypes.CDLL(find_library('c')).prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0, 0)
+        if result == -1:
+            logger.warning("PR_SET_CHILD_SUBREAPER failed, process watching may fail")
 
         if self.terminal and system.find_executable(self.terminal):
             self.game_process = self.run_in_terminal()
@@ -275,24 +283,17 @@ def killall(self):
         if killed_processes:
             logger.debug("Killed processes: %s", ', '.join(killed_processes))
 
-    def is_zombie(self):
-        return all([
-            p.endswith('Z')
-            for p in chain(*[
-                self.monitored_processes[key]
-                for key in self.monitored_processes
-                if key != 'external'
-            ])
-        ])
-
     def get_processes(self):
         process = Process(self.rootpid)
         num_children = 0
         num_watched_children = 0
-        terminated_children = 0
         passed_terminal_procs = False
         processes = defaultdict(list)
         for child in self.iter_children(process):
+            if child.state == 'Z':
+                os.wait3(os.WNOHANG)
+                continue
+
             # Exclude terminal processes
             if self.terminal:
                 if child.name == "run_in_term.sh":
@@ -310,13 +311,11 @@ def get_processes(self):
                 continue
             num_watched_children += 1
             processes['monitored'].append(str(child))
-            if child.state == 'Z':
-                terminated_children += 1
         for child in self.monitored_processes['monitored']:
             if child not in processes['monitored']:
                 num_children += 1
                 num_watched_children += 1
-        return processes, num_children, num_watched_children, terminated_children
+        return processes, num_children, num_watched_children
 
     def watch_children(self):
         """Poke at the running process(es).
@@ -336,7 +335,7 @@ def watch_children(self):
             self.cycles_without_children = 0
             return True
 
-        processes, num_children, num_watched_children, terminated_children = self.get_processes()
+        processes, num_children, num_watched_children = self.get_processes()
         if num_watched_children > 0 and not self.monitoring_started:
             logger.debug("Start process monitoring")
             self.monitoring_started = True
@@ -377,18 +376,9 @@ def watch_children(self):
                 self.game_process.communicate()
             else:
                 logger.debug('%d processes are still active', num_children)
-                if self.is_zombie():
-                    logger.warning('Zombie process detected, killing game process')
-                    self.game_process.kill()
             self.return_code = self.game_process.returncode
             return False
 
-        if terminated_children and terminated_children == num_watched_children:
-            try:
-                # Really not sure if that's necessary...
-                self.game_process.wait(2)
-            except subprocess.TimeoutExpired:
-                logger.warning("Processes are still running")
         return True
 
 def exec_in_thread(command):


Test patch: