mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-08 09:09:43 +00:00
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:
parent
4a731b3858
commit
3468a83e45
Notes:
github-actions[bot]
2024-11-19 22:28:29 +00:00
Author: https://github.com/stasoid
Commit: 3468a83e45
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2377
Reviewed-by: https://github.com/ADKaster ✅
3 changed files with 200 additions and 18 deletions
|
@ -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) {
|
||||||
|
|
|
@ -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
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
162
Libraries/LibCore/ProcessWindows.cpp
Normal file
162
Libraries/LibCore/ProcessWindows.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue