Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remove most exception with this simple trick... #7730

Merged
merged 4 commits into from
Mar 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 6 additions & 4 deletions Utilities/StrFmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,11 @@
#include "BEType.h"
#include "StrUtil.h"
#include "cfmt.h"
#include "util/logs.hpp"

#include <algorithm>
#include <string_view>
#include "Thread.h"

#ifdef _WIN32
#include <Windows.h>
Expand Down Expand Up @@ -203,7 +205,7 @@ namespace fmt
{
void raw_error(const char* msg)
{
throw std::runtime_error{msg};
thread_ctrl::emergency_exit(msg);
}

void raw_verify_error(const char* msg, const fmt_type_info* sup, u64 arg)
Expand Down Expand Up @@ -236,7 +238,7 @@ namespace fmt
out += msg;
}

throw std::runtime_error{out};
thread_ctrl::emergency_exit(out);
}

void raw_narrow_error(const char* msg, const fmt_type_info* sup, u64 arg)
Expand All @@ -256,14 +258,14 @@ namespace fmt
out += msg;
}

throw std::range_error{out};
thread_ctrl::emergency_exit(out);
}

void raw_throw_exception(const char* fmt, const fmt_type_info* sup, const u64* args)
{
std::string out;
raw_append(out, fmt, sup, args);
throw std::runtime_error{out};
thread_ctrl::emergency_exit(out);
}

struct cfmt_src;
Expand Down
25 changes: 25 additions & 0 deletions Utilities/Thread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2018,6 +2018,31 @@ u64 thread_base::get_cycles()
}
}

void thread_ctrl::emergency_exit(std::string_view reason)
{
sig_log.fatal("Thread terminated due to fatal error: %s", reason);

if (const auto _this = g_tls_this_thread)
{
if (_this->finalize(0))
{
delete _this;
}

// Do some not very useful cleanup
thread_base::finalize();

#ifdef _WIN32
_endthreadex(0);
#else
pthread_exit(0);
#endif
}

// Assume main thread
report_fatal_error(std::string(reason));
}

void thread_ctrl::detect_cpu_layout()
{
if (!g_native_core_layout.compare_and_swap_test(native_core_arrangement::undefined, native_core_arrangement::generic))
Expand Down
3 changes: 3 additions & 0 deletions Utilities/Thread.h
Original file line number Diff line number Diff line change
Expand Up @@ -225,6 +225,9 @@ class thread_ctrl final
_wait_for(-1, true);
}

// Exit.
[[noreturn]] static void emergency_exit(std::string_view reason);

// Get current thread (may be nullptr)
static thread_base* get_current()
{
Expand Down
40 changes: 28 additions & 12 deletions rpcs3/Emu/CPU/CPUThread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,34 @@ void cpu_thread::operator()()
state += cpu_flag::wait;
g_cpu_suspend_lock.lock_unlock();

static thread_local struct thread_cleanup_t
{
cpu_thread* _this;
u64 slot;

thread_cleanup_t(cpu_thread* _this, u64 slot)
: _this(_this)
, slot(slot)
{
}

~thread_cleanup_t()
{
if (auto ptr = vm::g_tls_locked)
{
ptr->compare_and_swap(_this, nullptr);
}

// Unregister and wait if necessary
_this->state += cpu_flag::wait;
if (g_cpu_array[slot].exchange(nullptr) != _this)
sys_log.fatal("Inconsistency for array slot %u", slot);
g_cpu_array_bits[slot / 64] &= ~(1ull << (slot % 64));
g_cpu_array_sema--;
g_cpu_suspend_lock.lock_unlock();
}
} cleanup{this, array_slot};

// Check thread status
while (!(state & (cpu_flag::exit + cpu_flag::dbg_global_stop)) && thread_ctrl::state() != thread_state::aborting)
{
Expand All @@ -373,18 +401,6 @@ void cpu_thread::operator()()

thread_ctrl::wait();
}

if (auto ptr = vm::g_tls_locked)
{
ptr->compare_and_swap(this, nullptr);
}

// Unregister and wait if necessary
state += cpu_flag::wait;
verify("g_cpu_array[...] -> null" HERE), g_cpu_array[array_slot].exchange(nullptr) == this;
g_cpu_array_bits[array_slot / 64] &= ~(1ull << (array_slot % 64));
g_cpu_array_sema--;
g_cpu_suspend_lock.lock_unlock();
}

cpu_thread::~cpu_thread()
Expand Down
100 changes: 81 additions & 19 deletions rpcs3/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@
#include "Utilities/dynamic_library.h"
DYNAMIC_IMPORT("ntdll.dll", NtQueryTimerResolution, NTSTATUS(PULONG MinimumResolution, PULONG MaximumResolution, PULONG CurrentResolution));
DYNAMIC_IMPORT("ntdll.dll", NtSetTimerResolution, NTSTATUS(ULONG DesiredResolution, BOOLEAN SetResolution, PULONG CurrentResolution));
#else
#include <unistd.h>
#include <spawn.h>
#include <sys/wait.h>
#endif

#ifdef __linux__
Expand All @@ -46,23 +50,31 @@ inline auto tr(Args&&... args)
return QObject::tr(std::forward<Args>(args)...);
}

static semaphore<> s_init{0};
static semaphore<> s_qt_init{0};
static semaphore<> s_qt_mutex{};
static semaphore<> s_qt_init;

static atomic_t<char*> s_argv0;

#ifndef _WIN32
extern char **environ;
#endif

[[noreturn]] extern void report_fatal_error(const std::string& text)
{
s_qt_mutex.lock();
const bool local = s_qt_init.try_lock();

if (!s_qt_init.try_lock())
if (local)
{
s_init.lock();
static int argc = 1;
static char arg1[] = {"ERROR"};
static char* argv[] = {arg1};
static QApplication app0{argc, argv};
}

if (!local)
{
fprintf(stderr, "RPCS3: %s\n", text.c_str());
}

auto show_report = [](const std::string& text)
{
QMessageBox msg;
Expand All @@ -82,6 +94,7 @@ static semaphore<> s_qt_mutex{};
.arg(tr("Please, don't send incorrect reports. Thanks for understanding.")));
msg.layout()->setSizeConstraint(QLayout::SetFixedSize);
msg.exec();
std::exit(0);
};

#ifdef __APPLE__
Expand All @@ -93,19 +106,67 @@ static semaphore<> s_qt_mutex{};
else
#endif
{
show_report(text);
// If Qt is already initialized, spawn a new RPCS3 process with an --error argument
if (local)
{
show_report(text);
}
else
{
#ifdef _WIN32
wchar_t buffer[32767];
GetModuleFileNameW(nullptr, buffer, sizeof(buffer) / 2);
std::wstring arg(text.cbegin(), text.cend()); // ignore unicode for now
_wspawnl(_P_WAIT, buffer, buffer, L"--error", arg.c_str(), nullptr);
#else
pid_t pid;
std::vector<char> data(text.data(), text.data() + text.size() + 1);
std::string err_arg = "--error";
char* argv[] = {+s_argv0, err_arg.data(), data.data(), nullptr};
int ret = posix_spawn(&pid, +s_argv0, nullptr, nullptr, argv, environ);

if (ret == 0)
{
int status;
waitpid(pid, &status, 0);
}
else
{
fprintf(stderr, "posix_spawn() failed: %d\n", ret);
}
#endif
std::abort();
}
}

std::abort();
while (true)
{
std::this_thread::sleep_for(1s);
}
}

struct pause_on_fatal final : logs::listener
{
~pause_on_fatal() override = default;

void log(u64 /*stamp*/, const logs::message& msg, const std::string& /*prefix*/, const std::string& /*text*/) override
{
if (msg.sev <= logs::level::fatal)
{
// Pause emulation if fatal error encountered
Emu.Pause();
}
}
};

const char* arg_headless = "headless";
const char* arg_no_gui = "no-gui";
const char* arg_high_dpi = "hidpi";
const char* arg_rounding = "dpi-rounding";
const char* arg_styles = "styles";
const char* arg_style = "style";
const char* arg_stylesheet = "stylesheet";
const char* arg_error = "error";

int find_arg(std::string arg, int& argc, char* argv[])
{
Expand Down Expand Up @@ -204,6 +265,14 @@ QCoreApplication* createApplication(int& argc, char* argv[])

int main(int argc, char** argv)
{
s_argv0 = argv[0]; // Save for report_fatal_error

// Only run RPCS3 to display an error
if (int err_pos = find_arg(arg_error, argc, argv))
{
report_fatal_error(argv[err_pos + 1]);
}

const std::string lock_name = fs::get_cache_dir() + "RPCS3.buf";

fs::file instance_lock;
Expand All @@ -215,9 +284,6 @@ int main(int argc, char** argv)

if (!instance_lock)
{
QApplication app0{argc, argv};
s_qt_init.unlock();

if (fs::g_tls_error == fs::error::acces)
{
if (fs::exists(lock_name))
Expand Down Expand Up @@ -249,9 +315,6 @@ int main(int argc, char** argv)
fs::device_stat stats{};
if (!fs::statfs(fs::get_cache_dir(), stats) || stats.avail_free < 128 * 1024 * 1024)
{
QApplication app0{argc, argv};
s_qt_init.unlock();

report_fatal_error(fmt::format("Not enough free space (%f KB)", stats.avail_free / 1000000.));
return 1;
}
Expand All @@ -260,6 +323,9 @@ int main(int argc, char** argv)
log_file = logs::make_file_listener(fs::get_cache_dir() + "RPCS3.log", stats.avail_free / 4);
}

std::unique_ptr<logs::listener> log_pauser = std::make_unique<pause_on_fatal>();
logs::listener::add(log_pauser.get());

{
const std::string firmware_version = utils::get_firmware_version();
const std::string firmware_string = firmware_version.empty() ? "" : (" | Firmware version: " + firmware_version);
Expand Down Expand Up @@ -291,8 +357,7 @@ int main(int argc, char** argv)
setenv( "KDE_DEBUG", "1", 0 );
#endif

s_init.unlock();
s_qt_mutex.lock();
std::lock_guard qt_init(s_qt_init);

// The constructor of QApplication eats the --style and --stylesheet arguments.
// By checking for stylesheet().isEmpty() we could implicitly know if a stylesheet was passed,
Expand Down Expand Up @@ -388,9 +453,6 @@ int main(int argc, char** argv)
});
}

s_qt_init.unlock();
s_qt_mutex.unlock();

// run event loop (maybe only needed for the gui application)
return app->exec();
}
11 changes: 7 additions & 4 deletions rpcs3/rpcs3qt/update_manager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,12 @@ bool update_manager::handle_rpcs3(const QByteArray& rpcs3_data, bool /*automatic

std::string replace_path;

#ifdef _WIN32
// Get executable path
wchar_t orig_path[32767];
GetModuleFileNameW(nullptr, orig_path, sizeof(orig_path) / 2);
#endif

#ifdef __linux__

const char* appimage_path = ::getenv("APPIMAGE");
Expand Down Expand Up @@ -558,16 +564,13 @@ bool update_manager::handle_rpcs3(const QByteArray& rpcs3_data, bool /*automatic
error_free7z();
if (res)
return false;

replace_path = Emulator::GetEmuDir() + "rpcs3.exe";

#endif

m_progress_dialog->close();
QMessageBox::information(m_parent, tr("Auto-updater"), tr("Update successful!"));

#ifdef _WIN32
int ret = _execl(replace_path.c_str(), replace_path.c_str(), nullptr);
int ret = _wexecl(orig_path, orig_path, nullptr);
#else
int ret = execl(replace_path.c_str(), replace_path.c_str(), nullptr);
#endif
Expand Down