Browse Source

fix: ensure `utilityProcess` only emits one 'exit' event (#44267)

fix: ensure utilityProcess only emits one exit

Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com>
Co-authored-by: Shelley Vohr <[email protected]>
trop[bot] 6 months ago
parent
commit
00e5d0ad56

+ 7 - 8
shell/browser/api/electron_api_utility_process.cc

@@ -235,12 +235,16 @@ void UtilityProcessWrapper::OnServiceProcessLaunch(
 }
 
 void UtilityProcessWrapper::HandleTermination(uint64_t exit_code) {
+  // HandleTermination is called from multiple callsites,
+  // we need to ensure we only process it for the first callsite.
+  if (terminated_)
+    return;
+  terminated_ = true;
+
   if (pid_ != base::kNullProcessId)
     GetAllUtilityProcessWrappers().Remove(pid_);
   CloseConnectorPort();
-
   EmitWithoutEvent("exit", exit_code);
-
   Unpin();
 }
 
@@ -279,13 +283,8 @@ void UtilityProcessWrapper::CloseConnectorPort() {
 }
 
 void UtilityProcessWrapper::Shutdown(uint64_t exit_code) {
-  if (pid_ != base::kNullProcessId)
-    GetAllUtilityProcessWrappers().Remove(pid_);
   node_service_remote_.reset();
-  CloseConnectorPort();
-  // Emit 'exit' event
-  EmitWithoutEvent("exit", exit_code);
-  Unpin();
+  HandleTermination(exit_code);
 }
 
 void UtilityProcessWrapper::PostMessage(gin::Arguments* args) {

+ 1 - 0
shell/browser/api/electron_api_utility_process.h

@@ -96,6 +96,7 @@ class UtilityProcessWrapper final
   int stdout_read_fd_ = -1;
   int stderr_read_fd_ = -1;
   bool connector_closed_ = false;
+  bool terminated_ = false;
   std::unique_ptr<mojo::Connector> connector_;
   blink::MessagePortDescriptor host_port_;
   mojo::Remote<node::mojom::NodeService> node_service_remote_;

+ 13 - 3
spec/api-utility-process-spec.ts

@@ -57,10 +57,20 @@ describe('utilityProcess module', () => {
       await once(child, 'spawn');
     });
 
-    it('emits \'exit\' when child process exits gracefully', async () => {
+    it('emits \'exit\' when child process exits gracefully', (done) => {
       const child = utilityProcess.fork(path.join(fixturesPath, 'empty.js'));
-      const [code] = await once(child, 'exit');
-      expect(code).to.equal(0);
+      child.on('exit', (code) => {
+        expect(code).to.equal(0);
+        done();
+      });
+    });
+
+    it('emits \'exit\' when the child process file does not exist', (done) => {
+      const child = utilityProcess.fork('nonexistent');
+      child.on('exit', (code) => {
+        expect(code).to.equal(1);
+        done();
+      });
     });
 
     ifit(!isWindows32Bit)('emits the correct error code when child process exits nonzero', async () => {