diff options
author | John Ankarström <john@ankarstrom.se> | 2022-08-22 01:02:34 +0200 |
---|---|---|
committer | John Ankarström <john@ankarstrom.se> | 2022-08-22 01:02:34 +0200 |
commit | 28b87e4d9c60b49d46b03a19b7b83e23e222087a (patch) | |
tree | 1f0a71ca09f04eac51fdabf2e8cfabc87a6e02a3 | |
parent | 801c10f07d1c92e1b1b89d8a30cb0cf86745de31 (diff) | |
download | EpisodeBrowser-28b87e4d9c60b49d46b03a19b7b83e23e222087a.tar.gz |
Handle exceptions in fetching thread.
-rw-r--r-- | c/data.cpp | 22 | ||||
-rw-r--r-- | c/data.h | 7 | ||||
-rw-r--r-- | c/main.cpp | 35 | ||||
-rw-r--r-- | c/test.cpp | 6 | ||||
-rw-r--r-- | c/util.h | 6 | ||||
-rw-r--r-- | c/win.cpp | 26 | ||||
-rw-r--r-- | c/win.h | 2 |
7 files changed, 63 insertions, 41 deletions
@@ -70,7 +70,7 @@ static inline void XmlFree(void* p) { xmlFree(p); } using XmlCharPtr = XmlPtr<XmlFree, xmlChar*>; template <size_t N> -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<ElvDataA> g_fvElv; extern FileView<DlvDataA> 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<const xmlChar*>("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); + } } @@ -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; @@ -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<wchar_t*>(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(); @@ -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(); } }; @@ -4,6 +4,12 @@ #include <algorithm> #include <cstring> #include <memory> +#include <SWI-Prolog.h> +#include <windows.h> + +#define CAT(a, b) a##b +#define APPLY(a, ...) a(__VA_ARGS__) +#define UNUSED APPLY(CAT, unused_, __COUNTER__) /* Format static wide string. */ template<size_t N, typename... T> @@ -1,7 +1,33 @@ #include <utility> +#include <SWI-Prolog.h> #include <windows.h> +#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<wchar_t*>(why)); + EBMessageBox(msg, title, uType); +} void WithNextWindow(void (*proc)(HWND)) { @@ -5,6 +5,8 @@ #include <string_view> #include <windows.h> +/* 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. */ |