Bug 1290598: Manage and terminate Windows subprocess trees as a single job object. r?mhowell draft
authorKris Maglione <maglione.k@gmail.com>
Fri, 29 Jul 2016 23:34:26 -0700
changeset 394686 df7e30ac15ad8b6a63e9061ee9f33032bb55d5b8
parent 394509 88190e4232c2c89da612def0a77aa05a910a2d9a
child 394687 12ce3e486fbad367b30b595657e51f18d10b6810
push id24624
push usermaglione.k@gmail.com
push dateSat, 30 Jul 2016 19:15:18 +0000
reviewersmhowell
bugs1290598
milestone50.0a1
Bug 1290598: Manage and terminate Windows subprocess trees as a single job object. r?mhowell MozReview-Commit-ID: 80K5YyDWmn3
toolkit/modules/subprocess/subprocess_shared_win.js
toolkit/modules/subprocess/subprocess_worker_win.js
--- a/toolkit/modules/subprocess/subprocess_shared_win.js
+++ b/toolkit/modules/subprocess/subprocess_shared_win.js
@@ -59,16 +59,17 @@ Object.assign(win32, {
 
 Object.assign(win32, {
   LPCSTR: win32.LPSTR,
   LPCWSTR: win32.LPWSTR,
   LPCVOID: win32.LPVOID,
 });
 
 Object.assign(win32, {
+  CREATE_SUSPENDED: 0x00000004,
   CREATE_NEW_CONSOLE: 0x00000010,
   CREATE_UNICODE_ENVIRONMENT: 0x00000400,
   EXTENDED_STARTUPINFO_PRESENT: 0x00080000,
   CREATE_NO_WINDOW: 0x08000000,
 
   STARTF_USESTDHANDLES: 0x0100,
 
   DUPLICATE_CLOSE_SOURCE: 0x01,
@@ -151,16 +152,23 @@ Object.assign(win32, {
   STARTUPINFOEXW: new ctypes.StructType("STARTUPINFOEXW", [
     {"StartupInfo": win32.STARTUPINFOW},
     {"lpAttributeList": win32.LPPROC_THREAD_ATTRIBUTE_LIST},
   ]),
 });
 
 
 var libc = new Library("libc", LIBC_CHOICES, {
+  AssignProcessToJobObject: [
+    win32.WINAPI,
+    win32.BOOL,
+    win32.HANDLE, /* hJob */
+    win32.HANDLE, /* hProcess */
+  ],
+
   CloseHandle: [
     win32.WINAPI,
     win32.BOOL,
     win32.HANDLE, /* hObject */
   ],
 
   CreateEventW: [
     win32.WINAPI,
@@ -178,16 +186,23 @@ var libc = new Library("libc", LIBC_CHOI
     win32.DWORD, /* dwDesiredAccess */
     win32.DWORD, /* dwShareMode */
     win32.SECURITY_ATTRIBUTES.ptr, /* opt lpSecurityAttributes */
     win32.DWORD, /* dwCreationDisposition */
     win32.DWORD, /* dwFlagsAndAttributes */
     win32.HANDLE, /* opt hTemplateFile */
   ],
 
+  CreateJobObjectW: [
+    win32.WINAPI,
+    win32.HANDLE,
+    win32.SECURITY_ATTRIBUTES.ptr, /* opt lpJobAttributes */
+    win32.LPWSTR, /* lpName */
+  ],
+
   CreateNamedPipeW: [
     win32.WINAPI,
     win32.HANDLE,
     win32.LPWSTR, /* lpName */
     win32.DWORD, /* dwOpenMode */
     win32.DWORD, /* dwPipeMode */
     win32.DWORD, /* nMaxInstances */
     win32.DWORD, /* nOutBufferSize */
@@ -312,16 +327,29 @@ var libc = new Library("libc", LIBC_CHOI
   ReleaseSemaphore: [
     win32.WINAPI,
     win32.BOOL,
     win32.HANDLE, /* hSemaphore */
     win32.LONG, /* lReleaseCount */
     win32.LONG.ptr, /* opt out lpPreviousCount */
   ],
 
+  ResumeThread: [
+    win32.WINAPI,
+    win32.DWORD,
+    win32.HANDLE, /* hThread */
+  ],
+
+  TerminateJobObject: [
+    win32.WINAPI,
+    win32.BOOL,
+    win32.HANDLE, /* hJob */
+    win32.UINT, /* uExitCode */
+  ],
+
   TerminateProcess: [
     win32.WINAPI,
     win32.BOOL,
     win32.HANDLE, /* hProcess */
     win32.UINT, /* uExitCode */
   ],
 
   UpdateProcThreadAttribute: [
--- a/toolkit/modules/subprocess/subprocess_worker_win.js
+++ b/toolkit/modules/subprocess/subprocess_worker_win.js
@@ -331,17 +331,17 @@ class Process extends BaseProcess {
     return this.handle;
   }
 
   /**
    * Forcibly terminates the process.
    */
   kill() {
     this.killed = true;
-    libc.TerminateProcess(this.handle, TERMINATE_EXIT_CODE);
+    libc.TerminateJobObject(this.jobHandle, TERMINATE_EXIT_CODE);
   }
 
   /**
    * Initializes the IO pipes for use as standard input, output, and error
    * descriptors in the spawned process.
    *
    * @returns {win32.Handle[]}
    *          The array of file handles belonging to the spawned process.
@@ -443,16 +443,17 @@ class Process extends BaseProcess {
 
     args = args.map(arg => this.quoteString(arg));
 
     let envp = this.stringList(options.environment);
 
     let handles = this.initPipes(options);
 
     let processFlags = win32.CREATE_NO_WINDOW
+                     | win32.CREATE_SUSPENDED
                      | win32.CREATE_UNICODE_ENVIRONMENT;
 
     let startupInfoEx = new win32.STARTUPINFOEXW();
     let startupInfo = startupInfoEx.StartupInfo;
 
     startupInfo.cb = win32.STARTUPINFOW.size;
     startupInfo.dwFlags = win32.STARTF_USESTDHANDLES;
 
@@ -494,20 +495,24 @@ class Process extends BaseProcess {
 
     if (!ok) {
       for (let pipe of this.pipes) {
         pipe.close();
       }
       throw new Error("Failed to create process");
     }
 
-    libc.CloseHandle(procInfo.hThread);
-
     this.handle = win32.Handle(procInfo.hProcess);
     this.pid = procInfo.dwProcessId;
+
+    this.jobHandle = win32.Handle(libc.CreateJobObjectW(null, null));
+    libc.AssignProcessToJobObject(this.jobHandle, this.handle);
+
+    libc.ResumeThread(procInfo.hThread);
+    libc.CloseHandle(procInfo.hThread);
   }
 
   /**
    * Called when our process handle is signaled as active, meaning the process
    * has exited.
    */
   onReady() {
     this.wait();
@@ -537,16 +542,20 @@ class Process extends BaseProcess {
       }
 
       this.resolveExit(exitCode);
       this.exitCode = exitCode;
 
       this.handle.dispose();
       this.handle = null;
 
+      libc.TerminateJobObject(this.jobHandle, TERMINATE_EXIT_CODE);
+      this.jobHandle.dispose();
+      this.jobHandle = null;
+
       for (let pipe of this.pipes) {
         pipe.maybeClose();
       }
 
       io.updatePollEvents();
 
       return exitCode;
     }