aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Ankarström <john@ankarstrom.se>2022-08-22 01:02:34 +0200
committerJohn Ankarström <john@ankarstrom.se>2022-08-22 01:02:34 +0200
commit28b87e4d9c60b49d46b03a19b7b83e23e222087a (patch)
tree1f0a71ca09f04eac51fdabf2e8cfabc87a6e02a3
parent801c10f07d1c92e1b1b89d8a30cb0cf86745de31 (diff)
downloadEpisodeBrowser-28b87e4d9c60b49d46b03a19b7b83e23e222087a.tar.gz
Handle exceptions in fetching thread.
-rw-r--r--c/data.cpp22
-rw-r--r--c/data.h7
-rw-r--r--c/main.cpp35
-rw-r--r--c/test.cpp6
-rw-r--r--c/util.h6
-rw-r--r--c/win.cpp26
-rw-r--r--c/win.h2
7 files changed, 63 insertions, 41 deletions
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<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);
+ }
}
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<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();
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 <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>
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 <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))
{
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 <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. */