From 28b87e4d9c60b49d46b03a19b7b83e23e222087a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?John=20Ankarstr=C3=B6m?= Date: Mon, 22 Aug 2022 01:02:34 +0200 Subject: Handle exceptions in fetching thread. --- c/data.cpp | 22 +++++++++++++++++----- c/data.h | 7 ++++--- c/main.cpp | 35 ++++++----------------------------- c/test.cpp | 6 ++---- c/util.h | 6 ++++++ c/win.cpp | 26 ++++++++++++++++++++++++++ c/win.h | 2 ++ 7 files changed, 63 insertions(+), 41 deletions(-) (limited to 'c') diff --git a/c/data.cpp b/c/data.cpp index 4bded44..771e6fb 100644 --- a/c/data.cpp +++ b/c/data.cpp @@ -70,7 +70,7 @@ static inline void XmlFree(void* p) { xmlFree(p); } using XmlCharPtr = XmlPtr; template -bool WcharsFromXmlchars(wchar_t (&dst)[N], XmlCharPtr utf8) +bool WcharsFromXmlchars(wchar_t (&dst)[N], XmlCharPtr utf8) noexcept { /* Truncate if source is larger than destination. */ int lenUtf8 = xmlStrlen(utf8); @@ -94,7 +94,7 @@ bool WcharsFromXmlchars(wchar_t (&dst)[N], XmlCharPtr utf8) return MultiByteToWideChar(CP_UTF8, 0, src, cchNarrow, dst, cchWide); } -void FetchData(bool* bDone) +void FetchData() { LIBXML_TEST_VERSION; @@ -123,7 +123,9 @@ void FetchData(bool* bDone) xmlNodeSetPtr nodes = xpathObj->nodesetval; int cNodes = nodes? nodes->nodeNr: 0; - printf("%d nodes\n", cNodes); + if (!cNodes) + throw std::runtime_error("could not find remote episode information"); + for (int i = 0; i < cNodes; i++) { extern FileView g_fvElv; extern FileView g_fvDlv; @@ -153,8 +155,18 @@ void FetchData(bool* bDone) /* Get wiki URL. */ const xmlNodePtr nodeLink = xmlFirstElementChild(nodeTitle); if (nodeLink) - WcharsFromXmlchars(d.wiki, xmlGetProp(nodeLink, (const xmlChar*)"href")); + WcharsFromXmlchars(d.wiki, + xmlGetProp(nodeLink, reinterpret_cast("href"))); } +} - *bDone = true; +void WaitFetchData(bool* bDone) noexcept +{ + try { + FetchData(); + *bDone = true; + } catch (...) { + *bDone = true; + ShowException(L"Remote data could not be fetched due to %s: %s", L"Error", MB_ICONWARNING); + } } diff --git a/c/data.h b/c/data.h index 4e0d97a..23c9e5f 100644 --- a/c/data.h +++ b/c/data.h @@ -9,6 +9,10 @@ #include "wcharptr.h" #include "win.h" +/* Fetch data from the web. */ +void FetchData(); +void WaitFetchData(bool* bDone) noexcept; + /* The structs ending with A are written as-is to disk. As such, they * should be regarded as immutable. If the format needs to be changed * in the future, then new structs ending with B should be added. */ @@ -162,9 +166,6 @@ struct FileView size_t c; }; -/* Fetch data from the web. */ -void FetchData(bool* bDone); - inline int FromWeb(const int iEp, ElvDataA& e, DlvDataA& d) noexcept { WcharPtr title, wiki, date, source, hint; diff --git a/c/main.cpp b/c/main.cpp index d6e3027..73a89d4 100644 --- a/c/main.cpp +++ b/c/main.cpp @@ -24,7 +24,7 @@ /* Exit gracefully on uncaught exception. */ static void OnTerminate() noexcept; -static auto unused = std::set_terminate(OnTerminate); +static auto UNUSED = std::set_terminate(OnTerminate); /* main.cpp defines all global (non-template) variables used in the * program. `extern' is used to access them from other files, when @@ -81,29 +81,7 @@ static void UpdateTheme(); void OnTerminate() noexcept { - const wchar_t* what = L"an exception"; - WcharPtr why; - - try { - std::rethrow_exception(std::current_exception()); - } catch (const term_t& t) { - what = L"a Prolog exception"; - try { why = PlString(t); } catch (...) {} - } catch (const Win32Error& e) { - what = L"a Windows error"; - try { why = WcharPtr::Copy(e.What()); } catch (...) {} - } catch (const std::exception& e) { - try { why = WcharPtr::FromNarrow(e.what()); } catch (...) {} - } catch (...) {} - - wchar_t msg[256] = {0}; - if (why) - Swprintf(msg, L"Episode Browser was terminated due to %s: %s", - what, static_cast(why)); - else - Swprintf(msg, L"Episode Browser was terminated due to %s.", what); - - MessageBox(g_hWnd, msg, L"Fatal Error", MB_ICONERROR); + ShowException(L"Episode Browser was terminated due to %s: %s", L"Fatal Error", MB_ICONERROR); _Exit(1); } @@ -402,7 +380,7 @@ void HandleMainMenu(const HWND hWnd, const WORD command) case IDM_FILE_FETCH_DATA: { - WaitFor(FetchData); + WaitFor(WaitFetchData); break; } @@ -456,13 +434,11 @@ void HandleMainMenu(const HWND hWnd, const WORD command) void WaitFor(void (*f)(bool*)) { - /* WaitFor uses a thread on the Prolog side to execute a - * predicate asynchronously. */ - static bool bActive = false; static bool bDone = false; static UINT_PTR iTimer; + /* Ensure that only a single thread is waited on. */ if (bActive) { if (EBMessageBox(L"Another task is active. " L"Do you want to cancel the existing task and start a new one?", @@ -476,7 +452,6 @@ void WaitFor(void (*f)(bool*)) /* The timer procedure animates an ellipsis in the status bar * while the thread is running. */ - static auto proc = [](HWND, UINT, UINT_PTR, DWORD) -> void { static int i = 0; @@ -494,6 +469,8 @@ void WaitFor(void (*f)(bool*)) } }; + /* The waited-on function signals its completion by setting a + * shared boolean value to true. */ bDone = false; bActive = true; std::thread{f, &bDone}.detach(); diff --git a/c/test.cpp b/c/test.cpp index d784367..7d6b3ed 100644 --- a/c/test.cpp +++ b/c/test.cpp @@ -16,9 +16,7 @@ struct Test Test(const char* name) : name(name) {} }; -#define CAT(a, b) a##b -#define APPLY(a, ...) a(__VA_ARGS__) -#define TESTS struct APPLY(CAT, tests_, __COUNTER__) +#define TESTS struct UNUSED #define TEST(id) }; struct id : public Test { id() : Test(#id) #define FAIL(...) do { Sprintf(error, __VA_ARGS__); return; } while (0) @@ -226,7 +224,7 @@ TESTS TEST(Fetch) { bool bDone = false; - std::thread{FetchData, &bDone}.detach(); + std::thread{WaitFetchData, &bDone}.detach(); } }; diff --git a/c/util.h b/c/util.h index d233ce1..1d8d222 100644 --- a/c/util.h +++ b/c/util.h @@ -4,6 +4,12 @@ #include #include #include +#include +#include + +#define CAT(a, b) a##b +#define APPLY(a, ...) a(__VA_ARGS__) +#define UNUSED APPLY(CAT, unused_, __COUNTER__) /* Format static wide string. */ template diff --git a/c/win.cpp b/c/win.cpp index 28dba27..ea47fb0 100644 --- a/c/win.cpp +++ b/c/win.cpp @@ -1,7 +1,33 @@ #include +#include #include +#include "pl.h" +#include "util.h" #include "win.h" +#include "wcharptr.h" + +void ShowException(const wchar_t* const fmt, const wchar_t* const title, const UINT uType) noexcept +{ + const wchar_t* what = L"an exception"; + WcharPtr why; + + try { + std::rethrow_exception(std::current_exception()); + } catch (const term_t& t) { + what = L"a Prolog exception"; + try { why = PlString(t); } catch (...) {} + } catch (const Win32Error& e) { + what = L"a Windows error"; + try { why = WcharPtr::Copy(e.What()); } catch (...) {} + } catch (const std::exception& e) { + try { why = WcharPtr::FromNarrow(e.what()); } catch (...) {} + } catch (...) {} + + wchar_t msg[512]; + Swprintf(msg, fmt, what, static_cast(why)); + EBMessageBox(msg, title, uType); +} void WithNextWindow(void (*proc)(HWND)) { diff --git a/c/win.h b/c/win.h index 93b1416..3bb0d6e 100644 --- a/c/win.h +++ b/c/win.h @@ -5,6 +5,8 @@ #include #include +/* Show message box for current exception. */ +void ShowException(const wchar_t* fmt, const wchar_t* title, UINT uType = MB_ICONWARNING) noexcept; /* Run given procedure at creation of next window. */ void WithNextWindow(void (*proc)(HWND)); /* Display message box centered in main window. */ -- cgit v1.2.3