diff --git a/runtime/bin/process_win.cc b/runtime/bin/process_win.cc index 36f1fabb080..4c27756e039 100644 --- a/runtime/bin/process_win.cc +++ b/runtime/bin/process_win.cc @@ -984,9 +984,9 @@ int Process::Exec(Namespace* namespc, f.Printf("Process::Exec - CreateJobObject failed %d\n", GetLastError()); return -1; } - JOBOBJECT_EXTENDED_LIMIT_INFORMATION info = { - {{{0, 0}}, {{0, 0}}, 0, 0, 0, 0, 0, 0, 0}, {0}, 0, 0, 0, 0}; + JOBOBJECT_EXTENDED_LIMIT_INFORMATION info; DWORD qresult; + memset(&info, 0, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION)); if (!QueryInformationJobObject(hjob, JobObjectExtendedLimitInformation, &info, sizeof(JOBOBJECT_EXTENDED_LIMIT_INFORMATION), &qresult)) { diff --git a/tests/standalone/io/process_child_script.dart b/tests/standalone/io/process_child_script.dart new file mode 100644 index 00000000000..3ebb8fe9b03 --- /dev/null +++ b/tests/standalone/io/process_child_script.dart @@ -0,0 +1,54 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'dart:io'; +import 'dart:convert'; + +Future main(List args) async { + if (args[0] == 'child') { + // Get the Dart script file and start the grandchild process. + var scriptFile = new File( + Platform.script.resolve("process_child_script.dart").toFilePath(), + ); + var args = []..addAll([scriptFile.path, 'grandchild']); + var process = await Process.start(Platform.executable, args); + + // Read the port number from the grandchild's stdout stream. + final portNumber = await process.stdout.transform(utf8.decoder).first; + + // Relay the port number to the parent process by printing it. + print('${portNumber.trim()}'); + + // The child process can exit now. + exit(0); + } else { + // Grand child process code. + + // Create a server socket and bind it to a random port. + final server = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0); + + // Print the port number to stdout so that it is relayed by the child + // to the parent process and it can read it. + print('${server.port}'); + + // Now listen for incoming connections, this will confirm to the parent + // that the grandchild is still alive after the child exits. + await for (var socket in server) { + // Listen for data from the client. + socket.listen( + (data) { + final message = utf8.decode(data); + }, + onDone: () { + server.close(); + exit(0); + }, + onError: (error) { + server.close(); + exit(1); + }, + ); + } + } +} diff --git a/tests/standalone/io/process_child_test.dart b/tests/standalone/io/process_child_test.dart new file mode 100644 index 00000000000..afe9b0e28fa --- /dev/null +++ b/tests/standalone/io/process_child_test.dart @@ -0,0 +1,58 @@ +// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +// OtherResources=process_child_script.dart + +import "package:expect/expect.dart"; +import 'package:path/path.dart'; + +import 'dart:io'; +import 'dart:convert'; + +Future main(List args) async { + // Get the Dart script file for the child process and start the child + // process. + var scriptFile = new File( + Platform.script.resolve("process_child_script.dart").toFilePath(), + ); + var args = []..addAll([scriptFile.path, 'child']); + var process = await Process.start(Platform.executable, args); + + // Listen to the child's stdout to get the relayed port number of + // the grand child process. + final portString = await process.stdout.transform(utf8.decoder).first; + final port = int.parse(portString); + + print('Received grandchild port $port from child. Child should now exit.'); + var exitCode = await process.exitCode; + Expect.equals(0, exitCode); + + // Now that the child has exited, connect to the grandchild to make + // sure it is still running and has not exited because the child exited. + try { + // Connect to the grandchild's server socket. + final socket = await Socket.connect(InternetAddress.loopbackIPv4, port); + print('Parent: Connected to grandchild.'); + + // Send messages to the grandchild. + print('Parent: Sending messages...'); + socket.writeln('Hello from the parent!'); + await Future.delayed(Duration(seconds: 1)); + socket.writeln('How are you?'); + await Future.delayed(Duration(seconds: 1)); + socket.writeln('Closing the connection.'); + + // Close the connection. + await socket.close(); + print('Parent: Disconnected from grandchild.'); + } on SocketException catch (e) { + // If we are unable to connect to the grandchild process it means + // the grandchild process has exited and so this would be an error. + print('Parent: Could not connect to grandchild: $e'); + Expect.equals(0, 1); // Force an error. + } + + print('Parent: Exiting.'); + exit(0); +} diff --git a/tests/standalone/standalone_kernel.status b/tests/standalone/standalone_kernel.status index bfb7dda033f..0a1813431c3 100644 --- a/tests/standalone/standalone_kernel.status +++ b/tests/standalone/standalone_kernel.status @@ -49,6 +49,7 @@ io/http_response_deadline_test: Skip # Flaky. io/http_reuse_server_port_test: Skip # Flaky. io/http_server_close_response_after_error_test: Skip # Flaky. io/http_shutdown_test: Skip # Flaky. +io/process_child_test: Skip # The test does not exercise the exec path on AOT io/raw_datagram_socket_test: Skip # Flaky. io/raw_secure_server_closing_test: Skip # Flaky io/raw_socket_test: Crash