Skip to content

Commit

Permalink
Exception handling improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
alabuzhev committed Jan 16, 2025
1 parent 2458e9b commit 30aa1eb
Show file tree
Hide file tree
Showing 12 changed files with 83 additions and 160 deletions.
31 changes: 0 additions & 31 deletions far/PluginA.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1510,37 +1510,6 @@ struct FAR_SEARCH_A_CALLBACK_PARAM

static const char* GetPluginMsg(const Plugin& PluginInstance, int MsgId);

// BUGBUG duplicate
template<typename callable_type>
auto cpp_try(callable_type const& Callable, source_location const& Location = source_location::current())
{
using return_type = typename function_traits<callable_type>::result_type;

const auto handle_exception = [&] /*[[noreturn]]*/ (const auto& Handler, auto&&... Args)
{
if (Handler(FWD(Args)..., nullptr, Location))
if (use_terminate_handler())
os::process::terminate_by_user(EXIT_FAILURE);

throw;
};

return cpp_try(
Callable,
[&](source_location const&) -> return_type
{
handle_exception(handle_unknown_exception);
std::unreachable();
},
[&](std::exception const& e, source_location const&) -> return_type
{
handle_exception(handle_std_exception, e);
std::unreachable();
},
Location
);
}

namespace oldpluginapi
{
static void WINAPI qsort(void *base, size_t nelem, size_t width, comparer cmp) noexcept
Expand Down
5 changes: 5 additions & 0 deletions far/changelog
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
--------------------------------------------------------------------------------
drkns 2025-01-16 18:07:41+00:00 - build 6416

1. Exception handling improvements.

--------------------------------------------------------------------------------
skipik 2025-01-14 16:52:43+03:00 - build 6415

Expand Down
6 changes: 0 additions & 6 deletions far/ctrlobj.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,12 +114,6 @@ ControlObject::~ControlObject() = default;

void ControlObject::close()
{
if (Global->CriticalInternalError)
{
Global->WindowManager->CloseAll();
return;
}

// dummy_panel indicates /v or /e mode
if (FPanels && FPanels->ActivePanel() && !std::dynamic_pointer_cast<dummy_panel>(FPanels->ActivePanel()))
{
Expand Down
75 changes: 35 additions & 40 deletions far/exception_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,8 +128,6 @@ static bool HandleCppExceptions = true;
static bool HandleSehExceptions = true;
static bool ForceStderrExceptionUI = false;

static std::atomic_bool UseTerminateHandler = false;

void disable_exception_handling()
{
if (!HandleCppExceptions && !HandleSehExceptions)
Expand Down Expand Up @@ -809,7 +807,7 @@ enum class handler_result
continue_search,
};

static handler_result ExcDialog(bool const CanContinue, string const& ReportLocation, string const& PluginInformation)
static handler_result ExcDialog(DWORD const ExceptionCode, bool const CanContinue, string const& ReportLocation, string const& PluginInformation)
{
// TODO: Far Dialog is not the best choice for exception reporting
// replace with something trivial
Expand Down Expand Up @@ -854,10 +852,7 @@ static handler_result ExcDialog(bool const CanContinue, string const& ReportLoca
const auto Result = Builder.ShowDialogEx();

if (Result == TerminateId)
{
UseTerminateHandler = true;
return handler_result::execute_handler;
}
os::process::terminate(ExceptionCode);

if (Result == UnloadId)
return handler_result::execute_handler;
Expand Down Expand Up @@ -911,7 +906,7 @@ static void print_exception_message(string const& ReportLocation, string const&
Separator << Eol;
}

static handler_result ExcConsole(bool const CanContinue, string const& ReportLocation, string const& PluginInformation)
static handler_result ExcConsole(DWORD const ExceptionCode, bool const CanContinue, string const& ReportLocation, string const& PluginInformation)
{
string Message;
string Keys;
Expand Down Expand Up @@ -943,10 +938,7 @@ static handler_result ExcConsole(bool const CanContinue, string const& ReportLoc
intptr_t const Result = ConsoleChoice(Message, Keys, TerminateId, [&]{ print_exception_message(ReportLocation, PluginInformation); });

if (Result == TerminateId)
{
UseTerminateHandler = true;
return handler_result::execute_handler;
}
os::process::terminate(ExceptionCode);

if (Result == UnloadId)
return handler_result::execute_handler;
Expand Down Expand Up @@ -1789,7 +1781,7 @@ static handler_result handle_generic_exception(
return handler_result::continue_search;

if (s_ExceptionHandlingInprogress)
return handler_result::execute_handler;
os::process::terminate(Context.code());

s_ExceptionHandlingInprogress = true;
SCOPE_EXIT{ s_ExceptionHandlingInprogress = false; };
Expand Down Expand Up @@ -1846,23 +1838,22 @@ static handler_result handle_generic_exception(

const auto Result = AnythingOnDisk || ReportInClipboard?
(UseDialog? ExcDialog : ExcConsole)(
Context.code(),
CanContinue,
AnythingOnDisk?
ReportLocation :
msg(lng::MExceptionDialogClipboard),
PluginInfo
) :
// Should never happen - neither the filesystem nor clipboard are writable, so just dump it to the screen:
ExcConsole(CanContinue, BugReport, PluginInfo);
ExcConsole(Context.code(), CanContinue, BugReport, PluginInfo);

switch (Result)
{
case handler_result::continue_execution:
break;

case handler_result::execute_handler:
if (!PluginModule && Global)
Global->CriticalInternalError = true;
break;

case handler_result::continue_search:
Expand Down Expand Up @@ -1985,9 +1976,15 @@ static bool handle_std_exception(
return handle_generic_exception(Context, Location, Module, Type, What, LastError) == handler_result::execute_handler;
}

bool handle_std_exception(const std::exception& e, const Plugin* const Module, source_location const& Location)
void handle_std_exception(const std::exception& e, const Plugin* const Module, source_location const& Location)
{
if (!handle_std_exception(exception_context(os::debug::exception_information()), e, Module, Location))
throw;
}

void handle_std_exception(const std::exception& e, source_location const& Location)
{
return handle_std_exception(exception_context(os::debug::exception_information()), e, Module, Location);
handle_std_exception(e, {}, Location);
}

class seh_exception::seh_exception_impl
Expand Down Expand Up @@ -2068,14 +2065,15 @@ static handler_result handle_seh_exception(
return handle_generic_exception(Context, Location, PluginModule, {}, {}, LastError);
}

bool handle_unknown_exception(const Plugin* const Module, source_location const& Location)
void handle_unknown_exception(const Plugin* const Module, source_location const& Location)
{
return handle_seh_exception(exception_context(os::debug::exception_information()), Module, Location) == handler_result::execute_handler;
if (handle_seh_exception(exception_context(os::debug::exception_information()), Module, Location) != handler_result::execute_handler)
throw;
}

bool use_terminate_handler()
void handle_unknown_exception(source_location const& Location)
{
return UseTerminateHandler;
handle_unknown_exception({}, Location);
}

static void abort_handler_impl()
Expand All @@ -2101,8 +2099,11 @@ static void abort_handler_impl()
// If it's a SEH or a C++ exception implemented in terms of SEH (and not a fake for GCC) it's better to handle it as SEH
if (const auto Info = os::debug::exception_information(); Info.ContextRecord && Info.ExceptionRecord && !is_fake_cpp_exception(*Info.ExceptionRecord))
{
if (handle_seh_exception(exception_context(Info), {}, Location) == handler_result::execute_handler)
os::process::terminate_by_user();
if (handle_seh_exception(exception_context(Info), {}, Location) == handler_result::continue_search)
{
restore_system_exception_handler();
return;
}
}

// It's a C++ exception, implemented in some other way (GCC)
Expand All @@ -2114,33 +2115,28 @@ static void abort_handler_impl()
}
catch (std::exception const& e)
{
if (handle_std_exception(e, {}, Location))
os::process::terminate_by_user();
handle_std_exception(e, Location);
}
catch (...)
{
if (handle_unknown_exception({}, Location))
os::process::terminate_by_user();
handle_unknown_exception(Location);
}
}

// No exception in flight, must be a direct call
exception_context const Context{ os::debug::fake_exception_information(STATUS_FAR_ABORT) };
error_state_ex const LastError{ os::last_error(), {}, errno };

if (handle_generic_exception(Context, Location, {}, {}, L"Abnormal termination"sv, LastError) == handler_result::execute_handler)
os::process::terminate_by_user();

restore_system_exception_handler();
if (handle_generic_exception(Context, Location, {}, {}, L"Abnormal termination"sv, LastError) == handler_result::continue_search)
{
restore_system_exception_handler();
return;
}
}

static LONG WINAPI unhandled_exception_filter_impl(EXCEPTION_POINTERS* const Pointers)
{
const auto Result = detail::seh_filter(Pointers, {});
if (Result == EXCEPTION_EXECUTE_HANDLER)
os::process::terminate_by_user(Pointers->ExceptionRecord->ExceptionCode);

return Result;
return detail::seh_filter(Pointers, {});
}

unhandled_exception_filter::unhandled_exception_filter():
Expand Down Expand Up @@ -2243,7 +2239,7 @@ static void invalid_parameter_handler_impl(const wchar_t* const Expression, cons
))
{
case handler_result::execute_handler:
os::process::terminate_by_user();
break;

case handler_result::continue_execution:
return;
Expand Down Expand Up @@ -2271,8 +2267,7 @@ static LONG NTAPI vectored_exception_handler_impl(EXCEPTION_POINTERS* const Poin
if (static_cast<NTSTATUS>(Pointers->ExceptionRecord->ExceptionCode) == STATUS_HEAP_CORRUPTION)
{
// VEH handlers shouldn't do this in general, but it's not like we can make things much worse at this point anyways.
if (detail::seh_filter(Pointers, {}) == EXCEPTION_EXECUTE_HANDLER)
os::process::terminate_by_user(Pointers->ExceptionRecord->ExceptionCode);
return detail::seh_filter(Pointers, {});
}

return EXCEPTION_CONTINUE_SEARCH;
Expand Down
29 changes: 26 additions & 3 deletions far/exception_handler.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,9 +56,13 @@ void force_stderr_exception_ui(bool Force);

class Plugin;

bool handle_std_exception(const std::exception& e, const Plugin* Module = nullptr, source_location const& Location = source_location::current());
bool handle_unknown_exception(const Plugin* Module = nullptr, source_location const& Location = source_location::current());
bool use_terminate_handler();
// These handlers can terminate the process, rethrow the exception, or return control if the user chooses to unload the module
void handle_std_exception(const std::exception& e, const Plugin* Module, source_location const& Location = source_location::current());
void handle_unknown_exception(const Plugin* Module, source_location const& Location = source_location::current());

// These handlers can terminate the process or rethrow the exception
[[noreturn]] void handle_std_exception(const std::exception& e, source_location const& Location = source_location::current());
[[noreturn]] void handle_unknown_exception(source_location const& Location = source_location::current());

class unhandled_exception_filter
{
Expand Down Expand Up @@ -200,6 +204,25 @@ WARNING_POP()
}
}

template<typename callable_type>
auto cpp_try(callable_type const& Callable, source_location const& Location = source_location::current())
{
using return_type = typename function_traits<callable_type>::result_type;

return cpp_try(
Callable,
[](source_location const& Location) -> return_type
{
handle_unknown_exception(Location);
},
[](std::exception const& e, source_location const& Location) -> return_type
{
handle_std_exception(e, Location);
},
Location
);
}

std::exception_ptr wrap_current_exception(source_location const& Location);

class save_exception_to
Expand Down
1 change: 0 additions & 1 deletion far/global.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,6 @@ class global
bool DirectRT{};
#endif
class SaveScreen *GlobalSaveScrPtr{};
bool CriticalInternalError{};
int Macro_DskShowPosType{}; // для какой панели вызывали меню выбора дисков (0 - ничерта не вызывали, 1 - левая (AltF1), 2 - правая (AltF2))
#ifndef NO_WRAPPER
string strRegUser;
Expand Down
41 changes: 12 additions & 29 deletions far/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -484,15 +484,6 @@ static std::optional<int> ProcessServiceModes(std::span<const wchar_t* const> co
return {};
}

[[noreturn]]
static void handle_exception(function_ref<bool()> const Handler)
{
if (Handler())
os::process::terminate_by_user();

throw;
}

#ifdef _M_IX86
std::pair<string_view, DWORD> get_hook_wow64_error();

Expand Down Expand Up @@ -944,13 +935,13 @@ static int mainImpl(std::span<const wchar_t* const> const Args)
{
return MainProcess(strEditName, strViewName, DestNames[0], DestNames[1], StartLine, StartChar);
},
[&](source_location const& Location) -> int
[](source_location const& Location) -> int
{
handle_exception([&]{ return handle_unknown_exception({}, Location); });
handle_unknown_exception(Location);
},
[&](std::exception const& e, source_location const& Location) -> int
[](std::exception const& e, source_location const& Location) -> int
{
handle_exception([&]{ return handle_std_exception(e, {}, Location); });
handle_std_exception(e, Location);
});
}

Expand All @@ -977,16 +968,6 @@ static void configure_exception_handling(std::span<wchar_t const* const> const A
}
}

[[noreturn]]
static void handle_exception_final(function_ref<bool()> const Handler)
{
if (Handler())
os::process::terminate_by_user();

restore_system_exception_handler();
throw;
}

#ifdef _DEBUG
static void premain();
#endif
Expand Down Expand Up @@ -1037,13 +1018,15 @@ static int wmain_seh()
return EXIT_FAILURE;
}
},
[&](source_location const& Location) -> int
[](source_location const& Location) -> int
{
handle_exception_final([&]{ return handle_unknown_exception({}, Location); });
SCOPE_FAIL{ restore_system_exception_handler(); };
handle_unknown_exception(Location);
},
[&](std::exception const& e, source_location const& Location) -> int
[](std::exception const& e, source_location const& Location) -> int
{
handle_exception_final([&]{ return handle_std_exception(e, {}, Location); });
SCOPE_FAIL{ restore_system_exception_handler(); };
handle_std_exception(e, Location);
});
}

Expand All @@ -1055,9 +1038,9 @@ int main()
os::debug::set_thread_name(L"Main Thread");
return wmain_seh();
},
[](DWORD const ExceptionCode) -> int
[](DWORD const) -> int
{
os::process::terminate_by_user(ExceptionCode);
std::unreachable();
});
}

Expand Down
Loading

0 comments on commit 30aa1eb

Please sign in to comment.