Skip to content

Commit 2ae685f

Browse files
committed
refactor: replace SystemError with generic StacktraceError for improved error handling
1 parent a074cec commit 2ae685f

15 files changed

Lines changed: 74 additions & 71 deletions

File tree

README.md

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ namespace {
198198
const auto handle = CreateEventA(nullptr, false, false, "Global\\AsyncIOBacktraceEvent");
199199

200200
if (!handle)
201-
throw zero::error::SystemError{
201+
throw zero::error::StacktraceError<std::system_error>{
202202
std::error_code{static_cast<int>(GetLastError()), std::system_category()}
203203
};
204204

@@ -212,10 +212,12 @@ namespace {
212212
co_await asyncio::toThread(
213213
[&, &cancelled = std::as_const(cancelled)] {
214214
if (WaitForSingleObject(*event, INFINITE) != WAIT_OBJECT_0)
215-
throw zero::error::SystemError{static_cast<int>(GetLastError()), std::system_category()};
215+
throw zero::error::StacktraceError<std::system_error>{
216+
static_cast<int>(GetLastError()), std::system_category()
217+
};
216218

217219
if (cancelled)
218-
throw zero::error::SystemError{asyncio::task::Error::CANCELLED};
220+
throw zero::error::StacktraceError<std::system_error>{asyncio::task::Error::CANCELLED};
219221
},
220222
[&](std::thread::native_handle_type) -> std::expected<void, std::error_code> {
221223
cancelled = true;

doc/overview.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
asyncio::task::Task<void> test1() {
99
while (true) {
1010
if (co_await task::cancelled)
11-
throw zero::error::SystemError{task::Error::CANCELLED};
11+
throw zero::error::StacktraceError<std::system_error>{task::Error::CANCELLED};
1212

1313
zero::error::guard(co_await asyncio::sleep(1s));
1414
fmt::print("Hello world\n");

doc/overview_zh.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
asyncio::task::Task<void> test1() {
99
while (true) {
1010
if (co_await task::cancelled)
11-
throw zero::error::SystemError{task::Error::CANCELLED};
11+
throw zero::error::StacktraceError<std::system_error>{task::Error::CANCELLED};
1212

1313
zero::error::guard(co_await asyncio::sleep(1s));
1414
fmt::print("Hello world\n");

include/asyncio/error.h

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -6,22 +6,19 @@
66
#include <fmt/ranges.h>
77

88
namespace asyncio::error {
9-
class SystemError final : public std::system_error {
10-
SystemError(const std::error_code ec, const std::vector<std::source_location> &stacktrace)
11-
: std::system_error{ec},
12-
mMessage{
13-
fmt::format(
14-
"{} {}",
15-
std::system_error::what(),
16-
to_string(fmt::join(stacktrace | std::views::drop(1), "\n"))
17-
)
18-
} {
9+
template<typename T>
10+
requires std::derived_from<T, std::exception>
11+
class StacktraceError final : public T {
12+
public:
13+
template<typename... Args>
14+
explicit StacktraceError(const std::vector<std::source_location> &stacktrace, Args &&... args)
15+
: T{std::forward<Args>(args)...},
16+
mMessage{fmt::format("{} {}", T::what(), to_string(fmt::join(stacktrace | std::views::drop(1), "\n")))} {
1917
}
2018

21-
public:
2219
template<typename... Args>
23-
static task::Task<SystemError> make(Args &&... args) {
24-
co_return SystemError{{std::forward<Args>(args)...}, co_await task::backtrace};
20+
static task::Task<StacktraceError> make(Args &&... args) {
21+
co_return StacktraceError{co_await task::backtrace, std::forward<Args>(args)...};
2522
}
2623

2724
[[nodiscard]] const char *what() const noexcept override {
@@ -36,7 +33,7 @@ namespace asyncio::error {
3633
requires std::is_convertible_v<E, std::error_code>
3734
task::Task<T> guard(std::expected<T, E> expected) {
3835
if (!expected)
39-
throw co_await SystemError::make(expected.error());
36+
throw co_await StacktraceError<std::system_error>::make(expected.error());
4037

4138
if constexpr (std::is_void_v<T>)
4239
co_return;
@@ -50,7 +47,7 @@ namespace asyncio::error {
5047
auto result = co_await task;
5148

5249
if (!result)
53-
throw co_await SystemError::make(result.error());
50+
throw co_await StacktraceError<std::system_error>::make(result.error());
5451

5552
if constexpr (std::is_void_v<T>)
5653
co_return;

include/asyncio/promise.h

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ namespace asyncio {
2626
void resolve(Args &&... args) {
2727
assert(this->mCore);
2828
assert(!this->mCore->result);
29-
assert(this->mCore->state != zero::async::promise::State::ONLY_RESULT);
30-
assert(this->mCore->state != zero::async::promise::State::DONE);
29+
assert(this->mCore->state != zero::async::promise::State::OnlyResult);
30+
assert(this->mCore->state != zero::async::promise::State::Done);
3131

3232
if constexpr (std::is_void_v<T>)
3333
this->mCore->result.emplace();
@@ -36,15 +36,17 @@ namespace asyncio {
3636

3737
auto state = this->mCore->state.load();
3838

39-
if (state == zero::async::promise::State::PENDING &&
40-
this->mCore->state.compare_exchange_strong(state, zero::async::promise::State::ONLY_RESULT)) {
39+
if (state == zero::async::promise::State::Pending &&
40+
this->mCore->state.compare_exchange_strong(state, zero::async::promise::State::OnlyResult)) {
4141
this->mCore->event.set();
4242
return;
4343
}
4444

45-
if (state != zero::async::promise::State::ONLY_CALLBACK ||
46-
!this->mCore->state.compare_exchange_strong(state, zero::async::promise::State::DONE))
47-
throw std::logic_error{fmt::format("Unexpected promise state: {}", std::to_underlying(state))};
45+
if (state != zero::async::promise::State::OnlyCallback ||
46+
!this->mCore->state.compare_exchange_strong(state, zero::async::promise::State::Done))
47+
throw zero::error::StacktraceError<std::logic_error>{
48+
fmt::format("Unexpected promise state: {}", std::to_underlying(state))
49+
};
4850

4951
this->mCore->event.set();
5052

@@ -57,21 +59,23 @@ namespace asyncio {
5759
void reject(Args &&... args) {
5860
assert(this->mCore);
5961
assert(!this->mCore->result);
60-
assert(this->mCore->state != zero::async::promise::State::ONLY_RESULT);
61-
assert(this->mCore->state != zero::async::promise::State::DONE);
62+
assert(this->mCore->state != zero::async::promise::State::OnlyResult);
63+
assert(this->mCore->state != zero::async::promise::State::Done);
6264

6365
this->mCore->result.emplace(std::unexpected<E>(std::in_place, std::forward<Args>(args)...));
6466
auto state = this->mCore->state.load();
6567

66-
if (state == zero::async::promise::State::PENDING &&
67-
this->mCore->state.compare_exchange_strong(state, zero::async::promise::State::ONLY_RESULT)) {
68+
if (state == zero::async::promise::State::Pending &&
69+
this->mCore->state.compare_exchange_strong(state, zero::async::promise::State::OnlyResult)) {
6870
this->mCore->event.set();
6971
return;
7072
}
7173

72-
if (state != zero::async::promise::State::ONLY_CALLBACK ||
73-
!this->mCore->state.compare_exchange_strong(state, zero::async::promise::State::DONE))
74-
throw std::logic_error{fmt::format("Unexpected promise state: {}", std::to_underlying(state))};
74+
if (state != zero::async::promise::State::OnlyCallback ||
75+
!this->mCore->state.compare_exchange_strong(state, zero::async::promise::State::Done))
76+
throw zero::error::StacktraceError<std::logic_error>{
77+
fmt::format("Unexpected promise state: {}", std::to_underlying(state))
78+
};
7579

7680
this->mCore->event.set();
7781

include/asyncio/time.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ namespace asyncio {
7676
co_return *std::move(*result);
7777
}
7878

79-
throw co_await error::SystemError::make(TimeoutError::Elapsed);
79+
throw co_await error::StacktraceError<std::system_error>::make(TimeoutError::Elapsed);
8080
}
8181

8282
std::ignore = timer.cancel();

sample/ws/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ asyncio::task::Task<void> asyncMain(const int argc, char *argv[]) {
1818

1919
if (!message) {
2020
if (const auto &error = message.error(); error != asyncio::http::ws::CloseCode::NormalClosure)
21-
throw co_await asyncio::error::SystemError::make(error);
21+
throw co_await asyncio::error::StacktraceError<std::system_error>::make(error);
2222

2323
break;
2424
}

src/http/request.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,7 @@ asyncio::http::Requests asyncio::http::Requests::make(Options options) {
316316
std::unique_ptr<CURLM, decltype(&curl_multi_cleanup)> ptr{curl_multi_init(), curl_multi_cleanup};
317317

318318
if (!ptr)
319-
throw zero::error::SystemError{errno, std::generic_category()};
319+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
320320

321321
auto timer = std::make_unique<uv_timer_t>();
322322

@@ -466,7 +466,7 @@ asyncio::http::Requests::prepare(std::string method, const URL &url, const std::
466466
std::unique_ptr<CURL, decltype(&curl_easy_cleanup)> ptr{curl_easy_init(), curl_easy_cleanup};
467467

468468
if (!ptr)
469-
throw zero::error::SystemError{errno, std::generic_category()};
469+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
470470

471471
const auto [
472472
proxy,
@@ -587,7 +587,7 @@ asyncio::http::Requests::prepare(std::string method, const URL &url, const std::
587587
const auto l = curl_slist_append(list, fmt::format("{}: {}", k, v).c_str());
588588

589589
if (!l)
590-
throw zero::error::SystemError{errno, std::generic_category()};
590+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
591591

592592
list = l;
593593
}
@@ -769,13 +769,13 @@ asyncio::http::Requests::request(
769769
};
770770

771771
if (!form)
772-
throw zero::error::SystemError{errno, std::generic_category()};
772+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
773773

774774
for (const auto &[k, v]: payload) {
775775
const auto field = curl_mime_addpart(form.get());
776776

777777
if (!field)
778-
throw zero::error::SystemError{errno, std::generic_category()};
778+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
779779

780780
zero::error::guard(expected([&] {
781781
return curl_mime_name(field, k.c_str());

src/http/url.cpp

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ std::string asyncio::http::urlEscape(const std::string &str) {
77
};
88

99
if (!ptr)
10-
throw zero::error::SystemError{errno, std::generic_category()};
10+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
1111

1212
return ptr.get();
1313
}
@@ -21,7 +21,7 @@ std::string asyncio::http::urlUnescape(const std::string &str) {
2121
};
2222

2323
if (!ptr)
24-
throw zero::error::SystemError{errno, std::generic_category()};
24+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
2525

2626
return std::string{ptr.get(), static_cast<std::size_t>(length)};
2727
}
@@ -31,7 +31,7 @@ asyncio::http::URL::URL(std::unique_ptr<CURLU, decltype(&curl_url_cleanup)> url)
3131

3232
asyncio::http::URL::URL(const URL &rhs) : mURL{curl_url_dup(rhs.mURL.get()), curl_url_cleanup} {
3333
if (!mURL)
34-
throw zero::error::SystemError{errno, std::generic_category()};
34+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
3535
}
3636

3737
asyncio::http::URL::URL(URL &&rhs) noexcept : mURL{std::move(rhs.mURL)} {
@@ -44,7 +44,7 @@ asyncio::http::URL &asyncio::http::URL::operator=(const URL &rhs) {
4444
mURL = {curl_url_dup(rhs.mURL.get()), curl_url_cleanup};
4545

4646
if (!mURL)
47-
throw zero::error::SystemError{errno, std::generic_category()};
47+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
4848

4949
return *this;
5050
}
@@ -58,7 +58,7 @@ std::expected<asyncio::http::URL, std::error_code> asyncio::http::URL::from(cons
5858
std::unique_ptr<CURLU, decltype(&curl_url_cleanup)> url{curl_url(), curl_url_cleanup};
5959

6060
if (!url)
61-
throw zero::error::SystemError{errno, std::generic_category()};
61+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
6262

6363
Z_EXPECT(expected([&] {
6464
return curl_url_set(url.get(), CURLUPART_URL, str.c_str(), CURLU_NON_SUPPORT_SCHEME);
@@ -94,7 +94,7 @@ std::optional<std::string> asyncio::http::URL::user() const {
9494
return curl_url_get(mURL.get(), CURLUPART_USER, &user, CURLU_URLDECODE);
9595
}); !result) {
9696
if (const auto &error = result.error(); error != static_cast<Error>(CURLUE_NO_USER))
97-
throw zero::error::SystemError{error};
97+
throw zero::error::StacktraceError<std::system_error>{error};
9898

9999
return std::nullopt;
100100
}
@@ -109,7 +109,7 @@ std::optional<std::string> asyncio::http::URL::password() const {
109109
return curl_url_get(mURL.get(), CURLUPART_PASSWORD, &password, CURLU_URLDECODE);
110110
}); !result) {
111111
if (const auto &error = result.error(); error != static_cast<Error>(CURLUE_NO_PASSWORD))
112-
throw zero::error::SystemError{error};
112+
throw zero::error::StacktraceError<std::system_error>{error};
113113

114114
return std::nullopt;
115115
}
@@ -124,7 +124,7 @@ std::optional<std::string> asyncio::http::URL::host() const {
124124
return curl_url_get(mURL.get(), CURLUPART_HOST, &host, 0);
125125
}); !result) {
126126
if (const auto &error = result.error(); error != static_cast<Error>(CURLUE_NO_HOST))
127-
throw zero::error::SystemError{error};
127+
throw zero::error::StacktraceError<std::system_error>{error};
128128

129129
return std::nullopt;
130130
}
@@ -159,7 +159,7 @@ std::optional<std::string> asyncio::http::URL::query() const {
159159
return curl_url_get(mURL.get(), CURLUPART_QUERY, &query, CURLU_URLDECODE);
160160
}); !result) {
161161
if (const auto &error = result.error(); error != static_cast<Error>(CURLUE_NO_QUERY))
162-
throw zero::error::SystemError{error};
162+
throw zero::error::StacktraceError<std::system_error>{error};
163163

164164
return std::nullopt;
165165
}
@@ -174,7 +174,7 @@ std::optional<std::string> asyncio::http::URL::rawQuery() const {
174174
return curl_url_get(mURL.get(), CURLUPART_QUERY, &query, 0);
175175
}); !result) {
176176
if (const auto &error = result.error(); error != static_cast<Error>(CURLUE_NO_QUERY))
177-
throw zero::error::SystemError{error};
177+
throw zero::error::StacktraceError<std::system_error>{error};
178178

179179
return std::nullopt;
180180
}
@@ -189,7 +189,7 @@ std::optional<std::string> asyncio::http::URL::fragment() const {
189189
return curl_url_get(mURL.get(), CURLUPART_FRAGMENT, &fragment, CURLU_URLDECODE);
190190
}); !result) {
191191
if (const auto &error = result.error(); error != static_cast<Error>(CURLUE_NO_FRAGMENT))
192-
throw zero::error::SystemError{error};
192+
throw zero::error::StacktraceError<std::system_error>{error};
193193

194194
return std::nullopt;
195195
}
@@ -204,7 +204,7 @@ std::optional<std::uint16_t> asyncio::http::URL::port() const {
204204
return curl_url_get(mURL.get(), CURLUPART_PORT, &port, CURLU_DEFAULT_PORT);
205205
}); !result) {
206206
if (const auto &error = result.error(); error != static_cast<Error>(CURLUE_NO_PORT))
207-
throw zero::error::SystemError{error};
207+
throw zero::error::StacktraceError<std::system_error>{error};
208208

209209
return std::nullopt;
210210
}

src/main.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ asyncio::task::Task<void> asyncMain(int argc, char *argv[]);
77
int main(const int argc, char *argv[]) {
88
#if defined(__unix__) || defined(__APPLE__)
99
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR)
10-
throw zero::error::SystemError{errno, std::generic_category()};
10+
throw zero::error::StacktraceError<std::system_error>{errno, std::generic_category()};
1111
#endif
1212

1313
const auto result = asyncio::run([=] {

0 commit comments

Comments
 (0)