From 1bbd34285a9006d73e14ea03be31e56998617559 Mon Sep 17 00:00:00 2001 From: isanae <14251494+isanae@users.noreply.github.com> Date: Mon, 9 Nov 2020 01:22:55 -0500 Subject: [PATCH] split threading --- src/commands.cpp | 1 + src/main.cpp | 1 + src/net.cpp | 1 + src/tasks/task.cpp | 1 + src/tasks/translations.cpp | 1 + src/tools/git.cpp | 1 + src/utility.cpp | 238 +----------------------------------- src/utility.h | 44 +------ src/utility/pch.h | 2 + src/utility/threading.cpp | 239 +++++++++++++++++++++++++++++++++++++ src/utility/threading.h | 49 ++++++++ vs/mob.vcxproj | 2 + vs/mob.vcxproj.filters | 9 ++ 13 files changed, 314 insertions(+), 275 deletions(-) create mode 100644 src/utility/pch.h create mode 100644 src/utility/threading.cpp create mode 100644 src/utility/threading.h diff --git a/src/commands.cpp b/src/commands.cpp index 43f1583..8aa49d7 100644 --- a/src/commands.cpp +++ b/src/commands.cpp @@ -5,6 +5,7 @@ #include "core/conf.h" #include "tasks/tasks.h" #include "tools/tools.h" +#include "utility/threading.h" namespace mob { diff --git a/src/main.cpp b/src/main.cpp index 1301fe4..14c2fb9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -6,6 +6,7 @@ #include "core/op.h" #include "tasks/tasks.h" #include "tools/tools.h" +#include "utility/threading.h" namespace mob { diff --git a/src/net.cpp b/src/net.cpp index ca240a2..bf27d38 100644 --- a/src/net.cpp +++ b/src/net.cpp @@ -4,6 +4,7 @@ #include "core/conf.h" #include "core/op.h" #include "core/context.h" +#include "utility/threading.h" namespace mob { diff --git a/src/tasks/task.cpp b/src/tasks/task.cpp index ad59d86..90c9e1b 100644 --- a/src/tasks/task.cpp +++ b/src/tasks/task.cpp @@ -3,6 +3,7 @@ #include "../core/conf.h" #include "../core/op.h" #include "../tools/tools.h" +#include "../utility/threading.h" namespace mob { diff --git a/src/tasks/translations.cpp b/src/tasks/translations.cpp index d0682c0..0bee069 100644 --- a/src/tasks/translations.cpp +++ b/src/tasks/translations.cpp @@ -1,5 +1,6 @@ #include "pch.h" #include "tasks.h" +#include "../utility/threading.h" namespace mob { diff --git a/src/tools/git.cpp b/src/tools/git.cpp index 59fe345..1f23d6d 100644 --- a/src/tools/git.cpp +++ b/src/tools/git.cpp @@ -1,6 +1,7 @@ #include "pch.h" #include "tools.h" #include "../core/conf.h" +#include "../utility/threading.h" namespace mob { diff --git a/src/utility.cpp b/src/utility.cpp index cd54b1d..c3e2366 100644 --- a/src/utility.cpp +++ b/src/utility.cpp @@ -14,179 +14,6 @@ static std::mutex g_output_mutex; extern u8stream u8cout(false); extern u8stream u8cerr(true); -constexpr std::size_t max_name_length = 1000; -constexpr std::size_t max_frames = 100; -const std::size_t exception_message_length = 5000; - -static void* frame_addresses[max_frames]; -static wchar_t undecorated_name[max_name_length + 1] = {}; -static unsigned char sym_buffer[sizeof(SYMBOL_INFOW) + max_name_length]; -static SYMBOL_INFOW* sym = (SYMBOL_INFOW*)sym_buffer; -static wchar_t exception_message[exception_message_length + 1] = {}; - -static LPTOP_LEVEL_EXCEPTION_FILTER g_previous_handler = nullptr; - -void dump_stacktrace(const wchar_t* what) -{ - std::scoped_lock lock(g_output_mutex); - - std::wcerr - << "\n\nmob has crashed\n" - << L"*****************************\n\n" - << what << L"\n\n"; - - - HANDLE process = INVALID_HANDLE_VALUE; - - DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), - GetCurrentProcess(), &process, 0, false, DUPLICATE_SAME_ACCESS); - - SymSetOptions(SymGetOptions() & (~SYMOPT_UNDNAME)); - SymInitializeW(process, NULL, TRUE); - - const std::size_t frame_count = CaptureStackBackTrace( - 0, max_frames, frame_addresses, nullptr); - - for (std::size_t i=0; i(frame_addresses[i]), - &disp, &line)) - { - std::wcerr << line.FileName << L":" << line.LineNumber << L" "; - } - - DWORD64 disp2 = 0; - - sym->MaxNameLen = max_name_length; - sym->SizeOfStruct = sizeof(SYMBOL_INFOW); - - if (SymFromAddrW(process, reinterpret_cast(frame_addresses[i]), &disp2, sym)) - { - const DWORD und_length = UnDecorateSymbolNameW( - sym->Name, undecorated_name, max_name_length, UNDNAME_COMPLETE); - - std::wcerr << undecorated_name; - } - - std::wcerr << L"\n"; - } - - if (IsDebuggerPresent()) - DebugBreak(); - else - TerminateProcess(GetCurrentProcess(), 0xffff); -} - -void terminate_handler() noexcept -{ - try - { - std::rethrow_exception(std::current_exception()); - } - catch(std::exception& e) - { - auto* p = exception_message; - std::size_t remaining = exception_message_length; - - const int n = _snwprintf( - p, remaining, L"%s", L"unhandled exception: "); - - if (n >= 0) - { - p += n; - remaining -=n ; - } - - ::MultiByteToWideChar( - CP_ACP, 0, e.what(), -1, p, static_cast(remaining)); - } - catch(...) - { - auto* p = exception_message; - std::size_t remaining = exception_message_length; - - _snwprintf( - p, remaining, L"%s", L"unhandled exception"); - } - - dump_stacktrace(exception_message); -} - -const wchar_t* error_code_name(DWORD code) -{ - switch (code) - { - case EXCEPTION_ACCESS_VIOLATION: return L"EXCEPTION_ACCESS_VIOLATION"; - case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return L"EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; - case EXCEPTION_BREAKPOINT: return L"EXCEPTION_BREAKPOINT"; - case EXCEPTION_DATATYPE_MISALIGNMENT: return L"EXCEPTION_DATATYPE_MISALIGNMENT"; - case EXCEPTION_FLT_DENORMAL_OPERAND: return L"EXCEPTION_FLT_DENORMAL_OPERAND"; - case EXCEPTION_FLT_DIVIDE_BY_ZERO: return L"EXCEPTION_FLT_DIVIDE_BY_ZERO"; - case EXCEPTION_FLT_INEXACT_RESULT: return L"EXCEPTION_FLT_INEXACT_RESULT"; - case EXCEPTION_FLT_INVALID_OPERATION: return L"EXCEPTION_FLT_INVALID_OPERATION"; - case EXCEPTION_FLT_OVERFLOW: return L"EXCEPTION_FLT_OVERFLOW"; - case EXCEPTION_FLT_STACK_CHECK: return L"EXCEPTION_FLT_STACK_CHECK"; - case EXCEPTION_FLT_UNDERFLOW: return L"EXCEPTION_FLT_UNDERFLOW"; - case EXCEPTION_ILLEGAL_INSTRUCTION: return L"EXCEPTION_ILLEGAL_INSTRUCTION"; - case EXCEPTION_IN_PAGE_ERROR: return L"EXCEPTION_IN_PAGE_ERROR"; - case EXCEPTION_INT_DIVIDE_BY_ZERO: return L"EXCEPTION_INT_DIVIDE_BY_ZERO"; - case EXCEPTION_INT_OVERFLOW: return L"EXCEPTION_INT_OVERFLOW"; - case EXCEPTION_INVALID_DISPOSITION: return L"EXCEPTION_INVALID_DISPOSITION"; - case EXCEPTION_NONCONTINUABLE_EXCEPTION: return L"EXCEPTION_NONCONTINUABLE_EXCEPTION"; - case EXCEPTION_PRIV_INSTRUCTION: return L"EXCEPTION_PRIV_INSTRUCTION"; - case EXCEPTION_SINGLE_STEP: return L"EXCEPTION_SINGLE_STEP"; - case EXCEPTION_STACK_OVERFLOW: return L"EXCEPTION_STACK_OVERFLOW"; - default: return L"unknown exception" ; - } -} - -LONG WINAPI unhandled_exception_handler(LPEXCEPTION_POINTERS ep) noexcept -{ - if (ep->ExceptionRecord->ExceptionCode == 0xE06D7363) - { - if (g_previous_handler) - return g_previous_handler(ep);; - } - - wchar_t* p = exception_message; - std::size_t remaining = exception_message_length; - - const auto n = GetModuleFileNameW( - GetModuleHandleW(nullptr), exception_message, - exception_message_length); - - p += n; - remaining -= n; - - const auto n2 = _snwprintf( - p, remaining, L": exception thrown at 0x%p: 0x%lX %s", - ep->ExceptionRecord->ExceptionAddress, - ep->ExceptionRecord->ExceptionCode, - error_code_name(ep->ExceptionRecord->ExceptionCode)); - - p += n2; - remaining -= n2; - - - dump_stacktrace(exception_message); - return EXCEPTION_CONTINUE_SEARCH; -} - -void set_thread_exception_handlers() -{ - g_previous_handler = SetUnhandledExceptionFilter( - mob::unhandled_exception_handler); - - std::set_terminate(mob::terminate_handler); -} - static bool stdout_console = [] { @@ -224,6 +51,12 @@ void set_std_streams() _setmode(_fileno(stderr), _O_U16TEXT); } +std::mutex& global_output_mutex() +{ + return g_output_mutex; +} + + void u8stream::do_output(const std::string& s) { std::scoped_lock lock(g_output_mutex); @@ -967,63 +800,4 @@ std::string path_to_utf8(fs::path p) return utf16_to_utf8(p.native()); } - - -thread_pool::thread_pool(std::size_t count) - : count_(std::max(1, count)) -{ - for (std::size_t i=0; i()); -} - -thread_pool::~thread_pool() -{ - join(); -} - -void thread_pool::join() -{ - for (auto&& t : threads_) - { - if (t->thread.joinable()) - t->thread.join(); - } -} - -void thread_pool::add(fun thread_fun) -{ - for (;;) - { - if (try_add(thread_fun)) - break; - - std::this_thread::sleep_for(std::chrono::milliseconds(1)); - } -} - -bool thread_pool::try_add(fun thread_fun) -{ - for (auto& t : threads_) - { - if (!t->running) - { - if (t->thread.joinable()) - t->thread.join(); - - t->running = true; - t->thread_fun = thread_fun; - - t->thread = std::thread([&] - { - t->thread_fun(); - t->running = false; - }); - - return true; - } - } - - return false; -} - } // namespace diff --git a/src/utility.h b/src/utility.h index b56ea3d..5f227f1 100644 --- a/src/utility.h +++ b/src/utility.h @@ -51,18 +51,6 @@ inline void mob_assert( mob_assertion_failed(nullptr, exp, file, line, func); } -void set_thread_exception_handlers(); - -template -std::thread start_thread(F&& f) -{ - return std::thread([f] - { - set_thread_exception_handlers(); - f(); - }); -} - enum class encodings { @@ -395,6 +383,7 @@ extern u8stream u8cout; extern u8stream u8cerr; void set_std_streams(); +std::mutex& global_output_mutex(); template @@ -590,37 +579,6 @@ auto map(const std::vector& v, F&& f) } -class thread_pool -{ -public: - typedef std::function fun; - - thread_pool(std::size_t count=std::thread::hardware_concurrency()); - ~thread_pool(); - - // non-copyable - thread_pool(const thread_pool&) = delete; - thread_pool& operator=(const thread_pool&) = delete; - - void add(fun f); - void join(); - -private: - struct thread_info - { - std::atomic running = false; - fun thread_fun; - std::thread thread; - }; - - - const std::size_t count_; - std::vector> threads_; - - bool try_add(fun thread_fun); -}; - - // see https://github.com/isanae/mob/issues/4 // // this restores the original console font if it changed diff --git a/src/utility/pch.h b/src/utility/pch.h new file mode 100644 index 0000000..d486962 --- /dev/null +++ b/src/utility/pch.h @@ -0,0 +1,2 @@ +// for intellisense +#include "../pch.h" diff --git a/src/utility/threading.cpp b/src/utility/threading.cpp new file mode 100644 index 0000000..a58a628 --- /dev/null +++ b/src/utility/threading.cpp @@ -0,0 +1,239 @@ +#include "pch.h" +#include "threading.h" +#include "../utility.h" + +namespace mob +{ + +constexpr std::size_t max_name_length = 1000; +constexpr std::size_t max_frames = 100; +const std::size_t exception_message_length = 5000; + +static void* frame_addresses[max_frames]; +static wchar_t undecorated_name[max_name_length + 1] = {}; +static unsigned char sym_buffer[sizeof(SYMBOL_INFOW) + max_name_length]; +static SYMBOL_INFOW* sym = (SYMBOL_INFOW*)sym_buffer; +static wchar_t exception_message[exception_message_length + 1] = {}; + +static LPTOP_LEVEL_EXCEPTION_FILTER g_previous_handler = nullptr; + +void dump_stacktrace(const wchar_t* what) +{ + std::scoped_lock lock(global_output_mutex()); + + std::wcerr + << "\n\nmob has crashed\n" + << L"*****************************\n\n" + << what << L"\n\n"; + + + HANDLE process = INVALID_HANDLE_VALUE; + + DuplicateHandle(GetCurrentProcess(), GetCurrentProcess(), + GetCurrentProcess(), &process, 0, false, DUPLICATE_SAME_ACCESS); + + SymSetOptions(SymGetOptions() & (~SYMOPT_UNDNAME)); + SymInitializeW(process, NULL, TRUE); + + const std::size_t frame_count = CaptureStackBackTrace( + 0, max_frames, frame_addresses, nullptr); + + for (std::size_t i=0; i(frame_addresses[i]), + &disp, &line)) + { + std::wcerr << line.FileName << L":" << line.LineNumber << L" "; + } + + DWORD64 disp2 = 0; + + sym->MaxNameLen = max_name_length; + sym->SizeOfStruct = sizeof(SYMBOL_INFOW); + + if (SymFromAddrW(process, reinterpret_cast(frame_addresses[i]), &disp2, sym)) + { + const DWORD und_length = UnDecorateSymbolNameW( + sym->Name, undecorated_name, max_name_length, UNDNAME_COMPLETE); + + std::wcerr << undecorated_name; + } + + std::wcerr << L"\n"; + } + + if (IsDebuggerPresent()) + DebugBreak(); + else + TerminateProcess(GetCurrentProcess(), 0xffff); +} + +void terminate_handler() noexcept +{ + try + { + std::rethrow_exception(std::current_exception()); + } + catch(std::exception& e) + { + auto* p = exception_message; + std::size_t remaining = exception_message_length; + + const int n = _snwprintf( + p, remaining, L"%s", L"unhandled exception: "); + + if (n >= 0) + { + p += n; + remaining -=n ; + } + + ::MultiByteToWideChar( + CP_ACP, 0, e.what(), -1, p, static_cast(remaining)); + } + catch(...) + { + auto* p = exception_message; + std::size_t remaining = exception_message_length; + + _snwprintf( + p, remaining, L"%s", L"unhandled exception"); + } + + dump_stacktrace(exception_message); +} + +const wchar_t* error_code_name(DWORD code) +{ + switch (code) + { + case EXCEPTION_ACCESS_VIOLATION: return L"EXCEPTION_ACCESS_VIOLATION"; + case EXCEPTION_ARRAY_BOUNDS_EXCEEDED: return L"EXCEPTION_ARRAY_BOUNDS_EXCEEDED"; + case EXCEPTION_BREAKPOINT: return L"EXCEPTION_BREAKPOINT"; + case EXCEPTION_DATATYPE_MISALIGNMENT: return L"EXCEPTION_DATATYPE_MISALIGNMENT"; + case EXCEPTION_FLT_DENORMAL_OPERAND: return L"EXCEPTION_FLT_DENORMAL_OPERAND"; + case EXCEPTION_FLT_DIVIDE_BY_ZERO: return L"EXCEPTION_FLT_DIVIDE_BY_ZERO"; + case EXCEPTION_FLT_INEXACT_RESULT: return L"EXCEPTION_FLT_INEXACT_RESULT"; + case EXCEPTION_FLT_INVALID_OPERATION: return L"EXCEPTION_FLT_INVALID_OPERATION"; + case EXCEPTION_FLT_OVERFLOW: return L"EXCEPTION_FLT_OVERFLOW"; + case EXCEPTION_FLT_STACK_CHECK: return L"EXCEPTION_FLT_STACK_CHECK"; + case EXCEPTION_FLT_UNDERFLOW: return L"EXCEPTION_FLT_UNDERFLOW"; + case EXCEPTION_ILLEGAL_INSTRUCTION: return L"EXCEPTION_ILLEGAL_INSTRUCTION"; + case EXCEPTION_IN_PAGE_ERROR: return L"EXCEPTION_IN_PAGE_ERROR"; + case EXCEPTION_INT_DIVIDE_BY_ZERO: return L"EXCEPTION_INT_DIVIDE_BY_ZERO"; + case EXCEPTION_INT_OVERFLOW: return L"EXCEPTION_INT_OVERFLOW"; + case EXCEPTION_INVALID_DISPOSITION: return L"EXCEPTION_INVALID_DISPOSITION"; + case EXCEPTION_NONCONTINUABLE_EXCEPTION: return L"EXCEPTION_NONCONTINUABLE_EXCEPTION"; + case EXCEPTION_PRIV_INSTRUCTION: return L"EXCEPTION_PRIV_INSTRUCTION"; + case EXCEPTION_SINGLE_STEP: return L"EXCEPTION_SINGLE_STEP"; + case EXCEPTION_STACK_OVERFLOW: return L"EXCEPTION_STACK_OVERFLOW"; + default: return L"unknown exception" ; + } +} + +LONG WINAPI unhandled_exception_handler(LPEXCEPTION_POINTERS ep) noexcept +{ + if (ep->ExceptionRecord->ExceptionCode == 0xE06D7363) + { + if (g_previous_handler) + return g_previous_handler(ep);; + } + + wchar_t* p = exception_message; + std::size_t remaining = exception_message_length; + + const auto n = GetModuleFileNameW( + GetModuleHandleW(nullptr), exception_message, + exception_message_length); + + p += n; + remaining -= n; + + const auto n2 = _snwprintf( + p, remaining, L": exception thrown at 0x%p: 0x%lX %s", + ep->ExceptionRecord->ExceptionAddress, + ep->ExceptionRecord->ExceptionCode, + error_code_name(ep->ExceptionRecord->ExceptionCode)); + + p += n2; + remaining -= n2; + + + dump_stacktrace(exception_message); + return EXCEPTION_CONTINUE_SEARCH; +} + +void set_thread_exception_handlers() +{ + g_previous_handler = SetUnhandledExceptionFilter( + mob::unhandled_exception_handler); + + std::set_terminate(mob::terminate_handler); +} + + +thread_pool::thread_pool(std::size_t count) + : count_(std::max(1, count)) +{ + for (std::size_t i=0; i()); +} + +thread_pool::~thread_pool() +{ + join(); +} + +void thread_pool::join() +{ + for (auto&& t : threads_) + { + if (t->thread.joinable()) + t->thread.join(); + } +} + +void thread_pool::add(fun thread_fun) +{ + for (;;) + { + if (try_add(thread_fun)) + break; + + std::this_thread::sleep_for(std::chrono::milliseconds(1)); + } +} + +bool thread_pool::try_add(fun thread_fun) +{ + for (auto& t : threads_) + { + if (!t->running) + { + if (t->thread.joinable()) + t->thread.join(); + + t->running = true; + t->thread_fun = thread_fun; + + t->thread = std::thread([&] + { + t->thread_fun(); + t->running = false; + }); + + return true; + } + } + + return false; +} + +} // namespace diff --git a/src/utility/threading.h b/src/utility/threading.h new file mode 100644 index 0000000..91ce733 --- /dev/null +++ b/src/utility/threading.h @@ -0,0 +1,49 @@ +#pragma once + +namespace mob +{ + +void set_thread_exception_handlers(); + +template +std::thread start_thread(F&& f) +{ + return std::thread([f] + { + set_thread_exception_handlers(); + f(); + }); +} + + +class thread_pool +{ +public: + typedef std::function fun; + + thread_pool(std::size_t count=std::thread::hardware_concurrency()); + ~thread_pool(); + + // non-copyable + thread_pool(const thread_pool&) = delete; + thread_pool& operator=(const thread_pool&) = delete; + + void add(fun f); + void join(); + +private: + struct thread_info + { + std::atomic running = false; + fun thread_fun; + std::thread thread; + }; + + + const std::size_t count_; + std::vector> threads_; + + bool try_add(fun thread_fun); +}; + +} // namespace diff --git a/vs/mob.vcxproj b/vs/mob.vcxproj index fc990ad..6a16259 100644 --- a/vs/mob.vcxproj +++ b/vs/mob.vcxproj @@ -106,6 +106,7 @@ + @@ -120,6 +121,7 @@ + diff --git a/vs/mob.vcxproj.filters b/vs/mob.vcxproj.filters index 2420968..775607c 100644 --- a/vs/mob.vcxproj.filters +++ b/vs/mob.vcxproj.filters @@ -13,6 +13,9 @@ {6e4d31d5-100c-4a4d-8e7d-4c48939819bd} + + {78dd6066-d407-4450-b520-649454c15ee9} + @@ -150,6 +153,9 @@ src\core + + src\utility + @@ -188,5 +194,8 @@ src\core + + src\utility + \ No newline at end of file