LibCore: Port Process to Windows

Windows doesn't have a concept of zombie children, hence:
* `disown` not needed
* we need a process handle because otherwise if the process have ended
  by the time `wait_for_termination` is called
  its pid may be reassigned to other process
This commit is contained in:
stasoid 2024-11-17 22:07:43 +05:00 committed by Andrew Kaster
commit 3468a83e45
Notes: github-actions[bot] 2024-11-19 22:28:29 +00:00
3 changed files with 200 additions and 18 deletions

View file

@ -61,6 +61,24 @@ struct ArgvList {
} }
}; };
Process::Process(Process&& other)
: m_pid(exchange(other.m_pid, 0))
, m_should_disown(exchange(other.m_should_disown, false))
{
}
Process& Process::operator=(Process&& other)
{
m_pid = exchange(other.m_pid, 0);
m_should_disown = exchange(other.m_should_disown, false);
return *this;
}
Process::~Process()
{
(void)disown();
}
Process Process::current() Process Process::current()
{ {
auto p = Process { getpid() }; auto p = Process { getpid() };
@ -297,6 +315,11 @@ void Process::wait_for_debugger_and_break()
} }
} }
pid_t Process::pid() const
{
return m_pid;
}
ErrorOr<void> Process::disown() ErrorOr<void> Process::disown()
{ {
if (m_pid != 0 && m_should_disown) { if (m_pid != 0 && m_should_disown) {

View file

@ -53,23 +53,9 @@ public:
No No
}; };
Process(Process&& other) Process(Process&& other);
: m_pid(exchange(other.m_pid, 0)) Process& operator=(Process&& other);
, m_should_disown(exchange(other.m_should_disown, false)) ~Process();
{
}
Process& operator=(Process&& other)
{
m_pid = exchange(other.m_pid, 0);
m_should_disown = exchange(other.m_should_disown, false);
return *this;
}
~Process()
{
(void)disown();
}
static ErrorOr<Process> spawn(ProcessSpawnOptions const& options); static ErrorOr<Process> spawn(ProcessSpawnOptions const& options);
static Process current(); static Process current();
@ -87,14 +73,17 @@ public:
static void wait_for_debugger_and_break(); static void wait_for_debugger_and_break();
static ErrorOr<bool> is_being_debugged(); static ErrorOr<bool> is_being_debugged();
pid_t pid() const { return m_pid; } pid_t pid() const;
#ifndef AK_OS_WINDOWS
ErrorOr<void> disown(); ErrorOr<void> disown();
#endif
// FIXME: Make it return an exit code. // FIXME: Make it return an exit code.
ErrorOr<bool> wait_for_termination(); ErrorOr<bool> wait_for_termination();
private: private:
#ifndef AK_OS_WINDOWS
Process(pid_t pid = -1) Process(pid_t pid = -1)
: m_pid(pid) : m_pid(pid)
, m_should_disown(true) , m_should_disown(true)
@ -103,6 +92,14 @@ private:
pid_t m_pid; pid_t m_pid;
bool m_should_disown; bool m_should_disown;
#else
Process(void* handle = 0)
: m_handle(handle)
{
}
void* m_handle;
#endif
}; };
} }

View file

@ -0,0 +1,162 @@
/*
* Copyright (c) 2021, Andreas Kling <andreas@ladybird.org>
* Copyright (c) 2022-2023, MacDue <macdue@dueutil.tech>
* Copyright (c) 2023-2024, Sam Atkins <atkinssj@serenityos.org>
* Copyright (c) 2024, Tim Flynn <trflynn89@serenityos.org>
* Copyright (c) 2024, stasoid <stasoid@yahoo.com>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#include <AK/String.h>
#include <AK/Utf16View.h>
#include <LibCore/Process.h>
#include <windows.h>
namespace Core {
Process::Process(Process&& other)
: m_handle(exchange(other.m_handle, nullptr))
{
}
Process& Process::operator=(Process&& other)
{
m_handle = exchange(other.m_handle, nullptr);
return *this;
}
Process::~Process()
{
if (m_handle)
CloseHandle(m_handle);
}
Process Process::current()
{
return GetCurrentProcess();
}
ErrorOr<Process> Process::spawn(ProcessSpawnOptions const& options)
{
// file actions are not supported
VERIFY(options.file_actions.is_empty());
char const* program_path = 0;
StringBuilder builder;
if (options.search_for_executable_in_path)
builder.appendff("\"{}\" ", options.executable);
else
program_path = options.executable.characters();
builder.join(' ', options.arguments);
builder.append('\0');
ByteBuffer command_line = TRY(builder.to_byte_buffer());
auto curdir = options.working_directory.has_value() ? options.working_directory->characters() : 0;
STARTUPINFO startup_info = {};
PROCESS_INFORMATION process_info = {};
BOOL result = CreateProcess(
program_path,
(char*)command_line.data(),
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent's environment
curdir,
&startup_info,
&process_info);
if (!result)
return Error::from_windows_error(GetLastError());
CloseHandle(process_info.hThread);
return Process(process_info.hProcess);
}
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<ByteString> arguments, ByteString working_directory, KeepAsChild)
{
return spawn({
.executable = path,
.arguments = Vector<ByteString> { arguments },
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : working_directory,
});
}
ErrorOr<Process> Process::spawn(StringView path, ReadonlySpan<StringView> arguments, ByteString working_directory, KeepAsChild)
{
Vector<ByteString> backing_strings;
backing_strings.ensure_capacity(arguments.size());
for (auto argument : arguments)
backing_strings.append(argument);
return spawn({
.executable = path,
.arguments = backing_strings,
.working_directory = working_directory.is_empty() ? Optional<ByteString> {} : working_directory,
});
}
// Get the full path of the executable file of the current process
ErrorOr<String> Process::get_name()
{
wchar_t path[MAX_PATH] = {};
DWORD length = GetModuleFileNameW(NULL, path, MAX_PATH);
if (!length)
return Error::from_windows_error(GetLastError());
return String::from_utf16(Utf16View { { (u16*)path, length } });
}
ErrorOr<void> Process::set_name(StringView, SetThreadName)
{
// Process::set_name() probably cannot be implemented on Windows.
return {};
}
ErrorOr<bool> Process::is_being_debugged()
{
return IsDebuggerPresent();
}
// Forces the process to sleep until a debugger is attached, then breaks.
void Process::wait_for_debugger_and_break()
{
bool print_message = true;
for (;;) {
if (IsDebuggerPresent()) {
DebugBreak();
return;
}
if (print_message) {
dbgln("Process {} with pid {} is sleeping, waiting for debugger.", Process::get_name(), GetCurrentProcessId());
print_message = false;
}
Sleep(100);
}
}
pid_t Process::pid() const
{
return GetProcessId(m_handle);
}
ErrorOr<bool> Process::wait_for_termination()
{
auto result = WaitForSingleObject(m_handle, INFINITE);
if (result == WAIT_FAILED)
return Error::from_windows_error(GetLastError());
DWORD exit_code = 0;
if (!GetExitCodeProcess(m_handle, &exit_code))
return Error::from_windows_error(GetLastError());
return !exit_code;
}
}