Merge pull request #90358 from KoBeWi/finding_errors_in_other_apps

Add `OS.get_process_exit_code()` method
This commit is contained in:
Rémi Verschelde 2024-04-16 13:30:41 +02:00
commit 7210d6cb43
No known key found for this signature in database
GPG Key ID: C3336907360768E1
10 changed files with 118 additions and 3 deletions

View File

@ -338,6 +338,10 @@ bool OS::is_process_running(int p_pid) const {
return ::OS::get_singleton()->is_process_running(p_pid); return ::OS::get_singleton()->is_process_running(p_pid);
} }
int OS::get_process_exit_code(int p_pid) const {
return ::OS::get_singleton()->get_process_exit_code(p_pid);
}
int OS::get_process_id() const { int OS::get_process_id() const {
return ::OS::get_singleton()->get_process_id(); return ::OS::get_singleton()->get_process_id();
} }
@ -602,6 +606,7 @@ void OS::_bind_methods() {
ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open); ClassDB::bind_method(D_METHOD("shell_open", "uri"), &OS::shell_open);
ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true)); ClassDB::bind_method(D_METHOD("shell_show_in_file_manager", "file_or_dir_path", "open_folder"), &OS::shell_show_in_file_manager, DEFVAL(true));
ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running); ClassDB::bind_method(D_METHOD("is_process_running", "pid"), &OS::is_process_running);
ClassDB::bind_method(D_METHOD("get_process_exit_code", "pid"), &OS::get_process_exit_code);
ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id); ClassDB::bind_method(D_METHOD("get_process_id"), &OS::get_process_id);
ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment); ClassDB::bind_method(D_METHOD("has_environment", "variable"), &OS::has_environment);

View File

@ -164,6 +164,7 @@ public:
Error shell_show_in_file_manager(const String &p_path, bool p_open_folder = true); Error shell_show_in_file_manager(const String &p_path, bool p_open_folder = true);
bool is_process_running(int p_pid) const; bool is_process_running(int p_pid) const;
int get_process_exit_code(int p_pid) const;
int get_process_id() const; int get_process_id() const;
void set_restart_on_exit(bool p_restart, const Vector<String> &p_restart_arguments = Vector<String>()); void set_restart_on_exit(bool p_restart, const Vector<String> &p_restart_arguments = Vector<String>());

View File

@ -176,6 +176,7 @@ public:
virtual Error kill(const ProcessID &p_pid) = 0; virtual Error kill(const ProcessID &p_pid) = 0;
virtual int get_process_id() const; virtual int get_process_id() const;
virtual bool is_process_running(const ProcessID &p_pid) const = 0; virtual bool is_process_running(const ProcessID &p_pid) const = 0;
virtual int get_process_exit_code(const ProcessID &p_pid) const = 0;
virtual void vibrate_handheld(int p_duration_ms = 500) {} virtual void vibrate_handheld(int p_duration_ms = 500) {}
virtual Error shell_open(const String &p_uri); virtual Error shell_open(const String &p_uri);

View File

@ -408,11 +408,20 @@
[b]Note:[/b] On Web platforms, it is still possible to determine the host platform's OS with feature tags. See [method has_feature]. [b]Note:[/b] On Web platforms, it is still possible to determine the host platform's OS with feature tags. See [method has_feature].
</description> </description>
</method> </method>
<method name="get_process_exit_code" qualifiers="const">
<return type="int" />
<param index="0" name="pid" type="int" />
<description>
Returns the exit code of a spawned process once it has finished running (see [method is_process_running]).
Returns [code]-1[/code] if the [param pid] is not a PID of a spawned child process, the process is still running, or the method is not implemented for the current platform.
[b]Note:[/b] This method is implemented on Android, Linux, macOS and Windows.
</description>
</method>
<method name="get_process_id" qualifiers="const"> <method name="get_process_id" qualifiers="const">
<return type="int" /> <return type="int" />
<description> <description>
Returns the number used by the host machine to uniquely identify this application. Returns the number used by the host machine to uniquely identify this application.
[b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows. [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS, and Windows.
</description> </description>
</method> </method>
<method name="get_processor_count" qualifiers="const"> <method name="get_processor_count" qualifiers="const">
@ -592,7 +601,7 @@
<param index="0" name="pid" type="int" /> <param index="0" name="pid" type="int" />
<description> <description>
Returns [code]true[/code] if the child process ID ([param pid]) is still running or [code]false[/code] if it has terminated. [param pid] must be a valid ID generated from [method create_process]. Returns [code]true[/code] if the child process ID ([param pid]) is still running or [code]false[/code] if it has terminated. [param pid] must be a valid ID generated from [method create_process].
[b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS and Windows. [b]Note:[/b] This method is implemented on Android, iOS, Linux, macOS, and Windows.
</description> </description>
</method> </method>
<method name="is_restart_on_exit_set" qualifiers="const"> <method name="is_restart_on_exit_set" qualifiers="const">

View File

@ -168,11 +168,13 @@ void OS_Unix::initialize_core() {
NetSocketPosix::make_default(); NetSocketPosix::make_default();
IPUnix::make_default(); IPUnix::make_default();
process_map = memnew((HashMap<ProcessID, ProcessInfo>));
_setup_clock(); _setup_clock();
} }
void OS_Unix::finalize_core() { void OS_Unix::finalize_core() {
memdelete(process_map);
NetSocketPosix::cleanup(); NetSocketPosix::cleanup();
} }
@ -582,6 +584,11 @@ Dictionary OS_Unix::execute_with_pipe(const String &p_path, const List<String> &
err_pipe.instantiate(); err_pipe.instantiate();
err_pipe->open_existing(pipe_err[0], 0); err_pipe->open_existing(pipe_err[0], 0);
ProcessInfo pi;
process_map_mutex.lock();
process_map->insert(pid, pi);
process_map_mutex.unlock();
ret["stdio"] = main_pipe; ret["stdio"] = main_pipe;
ret["stderr"] = err_pipe; ret["stderr"] = err_pipe;
ret["pid"] = pid; ret["pid"] = pid;
@ -698,6 +705,11 @@ Error OS_Unix::create_process(const String &p_path, const List<String> &p_argume
raise(SIGKILL); raise(SIGKILL);
} }
ProcessInfo pi;
process_map_mutex.lock();
process_map->insert(pid, pi);
process_map_mutex.unlock();
if (r_child_id) { if (r_child_id) {
*r_child_id = pid; *r_child_id = pid;
} }
@ -720,14 +732,45 @@ int OS_Unix::get_process_id() const {
} }
bool OS_Unix::is_process_running(const ProcessID &p_pid) const { bool OS_Unix::is_process_running(const ProcessID &p_pid) const {
MutexLock lock(process_map_mutex);
const ProcessInfo *pi = process_map->getptr(p_pid);
if (pi && !pi->is_running) {
return false;
}
int status = 0; int status = 0;
if (waitpid(p_pid, &status, WNOHANG) != 0) { if (waitpid(p_pid, &status, WNOHANG) != 0) {
if (pi) {
pi->is_running = false;
pi->exit_code = status;
}
return false; return false;
} }
return true; return true;
} }
int OS_Unix::get_process_exit_code(const ProcessID &p_pid) const {
MutexLock lock(process_map_mutex);
const ProcessInfo *pi = process_map->getptr(p_pid);
if (pi && !pi->is_running) {
return pi->exit_code;
}
int status = 0;
if (waitpid(p_pid, &status, WNOHANG) != 0) {
status = WIFEXITED(status) ? WEXITSTATUS(status) : status;
if (pi) {
pi->is_running = false;
pi->exit_code = status;
}
return status;
}
return -1;
}
String OS_Unix::get_locale() const { String OS_Unix::get_locale() const {
if (!has_environment("LANG")) { if (!has_environment("LANG")) {
return "en"; return "en";

View File

@ -37,6 +37,13 @@
#include "drivers/unix/ip_unix.h" #include "drivers/unix/ip_unix.h"
class OS_Unix : public OS { class OS_Unix : public OS {
struct ProcessInfo {
mutable bool is_running = true;
mutable int exit_code = -1;
};
HashMap<ProcessID, ProcessInfo> *process_map = nullptr;
Mutex process_map_mutex;
protected: protected:
// UNIX only handles the core functions. // UNIX only handles the core functions.
// inheriting platforms under unix (eg. X11) should handle the rest // inheriting platforms under unix (eg. X11) should handle the rest
@ -81,6 +88,7 @@ public:
virtual Error kill(const ProcessID &p_pid) override; virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override; virtual int get_process_id() const override;
virtual bool is_process_running(const ProcessID &p_pid) const override; virtual bool is_process_running(const ProcessID &p_pid) const override;
virtual int get_process_exit_code(const ProcessID &p_pid) const override;
virtual bool has_environment(const String &p_var) const override; virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override; virtual String get_environment(const String &p_var) const override;

View File

@ -132,6 +132,10 @@ bool OS_Web::is_process_running(const ProcessID &p_pid) const {
return false; return false;
} }
int OS_Web::get_process_exit_code(const ProcessID &p_pid) const {
return -1;
}
int OS_Web::get_processor_count() const { int OS_Web::get_processor_count() const {
return godot_js_os_hw_concurrency_get(); return godot_js_os_hw_concurrency_get();
} }

View File

@ -85,6 +85,7 @@ public:
Error kill(const ProcessID &p_pid) override; Error kill(const ProcessID &p_pid) override;
int get_process_id() const override; int get_process_id() const override;
bool is_process_running(const ProcessID &p_pid) const override; bool is_process_running(const ProcessID &p_pid) const override;
int get_process_exit_code(const ProcessID &p_pid) const override;
int get_processor_count() const override; int get_processor_count() const override;
String get_unique_id() const override; String get_unique_id() const override;
int get_default_thread_pool_size() const override { return 1; } int get_default_thread_pool_size() const override { return 1; }

View File

@ -867,7 +867,9 @@ Dictionary OS_Windows::execute_with_pipe(const String &p_path, const List<String
CloseHandle(pipe_err[1]); CloseHandle(pipe_err[1]);
ProcessID pid = pi.pi.dwProcessId; ProcessID pid = pi.pi.dwProcessId;
process_map_mutex.lock();
process_map->insert(pid, pi); process_map->insert(pid, pi);
process_map_mutex.unlock();
Ref<FileAccessWindowsPipe> main_pipe; Ref<FileAccessWindowsPipe> main_pipe;
main_pipe.instantiate(); main_pipe.instantiate();
@ -1014,13 +1016,16 @@ Error OS_Windows::create_process(const String &p_path, const List<String> &p_arg
if (r_child_id) { if (r_child_id) {
*r_child_id = pid; *r_child_id = pid;
} }
process_map_mutex.lock();
process_map->insert(pid, pi); process_map->insert(pid, pi);
process_map_mutex.unlock();
return OK; return OK;
} }
Error OS_Windows::kill(const ProcessID &p_pid) { Error OS_Windows::kill(const ProcessID &p_pid) {
int ret = 0; int ret = 0;
MutexLock lock(process_map_mutex);
if (process_map->has(p_pid)) { if (process_map->has(p_pid)) {
const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi; const PROCESS_INFORMATION pi = (*process_map)[p_pid].pi;
process_map->erase(p_pid); process_map->erase(p_pid);
@ -1046,24 +1051,58 @@ int OS_Windows::get_process_id() const {
} }
bool OS_Windows::is_process_running(const ProcessID &p_pid) const { bool OS_Windows::is_process_running(const ProcessID &p_pid) const {
MutexLock lock(process_map_mutex);
if (!process_map->has(p_pid)) { if (!process_map->has(p_pid)) {
return false; return false;
} }
const PROCESS_INFORMATION &pi = (*process_map)[p_pid].pi; const ProcessInfo &info = (*process_map)[p_pid];
if (!info.is_running) {
return false;
}
const PROCESS_INFORMATION &pi = info.pi;
DWORD dw_exit_code = 0; DWORD dw_exit_code = 0;
if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) { if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) {
return false; return false;
} }
if (dw_exit_code != STILL_ACTIVE) { if (dw_exit_code != STILL_ACTIVE) {
info.is_running = false;
info.exit_code = dw_exit_code;
return false; return false;
} }
return true; return true;
} }
int OS_Windows::get_process_exit_code(const ProcessID &p_pid) const {
MutexLock lock(process_map_mutex);
if (!process_map->has(p_pid)) {
return -1;
}
const ProcessInfo &info = (*process_map)[p_pid];
if (!info.is_running) {
return info.exit_code;
}
const PROCESS_INFORMATION &pi = info.pi;
DWORD dw_exit_code = 0;
if (!GetExitCodeProcess(pi.hProcess, &dw_exit_code)) {
return -1;
}
if (dw_exit_code == STILL_ACTIVE) {
return -1;
}
info.is_running = false;
info.exit_code = dw_exit_code;
return dw_exit_code;
}
Error OS_Windows::set_cwd(const String &p_cwd) { Error OS_Windows::set_cwd(const String &p_cwd) {
if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) { if (_wchdir((LPCWSTR)(p_cwd.utf16().get_data())) != 0) {
return ERR_CANT_OPEN; return ERR_CANT_OPEN;

View File

@ -150,8 +150,11 @@ protected:
struct ProcessInfo { struct ProcessInfo {
STARTUPINFO si; STARTUPINFO si;
PROCESS_INFORMATION pi; PROCESS_INFORMATION pi;
mutable bool is_running = true;
mutable int exit_code = -1;
}; };
HashMap<ProcessID, ProcessInfo> *process_map = nullptr; HashMap<ProcessID, ProcessInfo> *process_map = nullptr;
Mutex process_map_mutex;
public: public:
virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override; virtual void alert(const String &p_alert, const String &p_title = "ALERT!") override;
@ -189,6 +192,7 @@ public:
virtual Error kill(const ProcessID &p_pid) override; virtual Error kill(const ProcessID &p_pid) override;
virtual int get_process_id() const override; virtual int get_process_id() const override;
virtual bool is_process_running(const ProcessID &p_pid) const override; virtual bool is_process_running(const ProcessID &p_pid) const override;
virtual int get_process_exit_code(const ProcessID &p_pid) const override;
virtual bool has_environment(const String &p_var) const override; virtual bool has_environment(const String &p_var) const override;
virtual String get_environment(const String &p_var) const override; virtual String get_environment(const String &p_var) const override;