Catch exceptions from tasks #7072

Merged
phosit merged 1 commits from phosit/0ad:task_exception into main 2024-10-08 20:05:49 +02:00
Member

It's now possible te get an exception from a function in a task.
The interface is like std::future: if you call .Get() you will get the result (as before) or the exception will be thrown.

I don't know if this also does handle windows structured exceptions. See: https://learn.microsoft.com/en-us/cpp/cpp/transporting-exceptions-between-threads
Ref: #5874

Continuation of https://code.wildfiregames.com/D4956

It's now possible te get an exception from a function in a task. The interface is like std::future: if you call .Get() you will get the result (as before) or the exception will be thrown. I don't know if this also does handle windows structured exceptions. See: https://learn.microsoft.com/en-us/cpp/cpp/transporting-exceptions-between-threads Ref: #5874 Continuation of https://code.wildfiregames.com/D4956
phosit added 1 commit 2024-09-25 20:55:11 +02:00
Catch exceptions from tasks
Some checks reported warnings
checkrefs / checkrefs (pull_request) Successful in 1m3s
pre-commit / build (pull_request) Successful in 1m26s
0ad-freebsd/pipeline/pr-main This commit is unstable
0ad-windows/pipeline/pr-main This commit looks good
0ad-linux/pipeline/pr-main This commit looks good
0ad-macos/pipeline/pr-main This commit looks good
95e2c6e4a6
It's now possible te get an exception from a function in a task.
The interface is like std::future: if you call .Get() you will get the
result (as before) or the exception will be thrown.
phosit requested review from vladislavbelov 2024-09-25 20:55:11 +02:00
phosit requested review from wraitii 2024-09-25 20:55:11 +02:00
phosit requested review from Itms 2024-09-25 20:55:11 +02:00
vladislavbelov reviewed 2024-10-02 20:21:52 +02:00
@ -107,2 +105,3 @@
// The caller must ensure that this is only called if we have a result.
ENSURE(this->has_value());
if constexpr (!std::is_void_v<ResultType>)
ENSURE(std::get<0>(m_Outcome).has_value() || std::get<1>(m_Outcome));
Member

Isn't explicit typing more clear and safe?

ENSURE(std::get<ResultHolder<ResultType>>(m_Outcome).has_value() || std::get<std::exception_ptr>(m_Outcome));
Isn't explicit typing more clear and safe? ```cpp ENSURE(std::get<ResultHolder<ResultType>>(m_Outcome).has_value() || std::get<std::exception_ptr>(m_Outcome)); ```
Author
Member

Normally i use std::tuple only with indexes. But in this case it's cleaner with types.
The benefit will be more apperent when we switch to std::variant.

Normally i use `std::tuple` only with indexes. But in this case it's cleaner with types. The benefit will be more apperent when we switch to `std::variant`.
Member

Why do we need to switch to std::variant here?

Why do we need to switch to `std::variant` here?
Author
Member

A task can't result with a value and an exception.
An std::variant would clarify that.

A task can't result with a value and an exception. An `std::variant` would clarify that.
@ -133,0 +139,4 @@
using std::exception::exception;
};
{
Member

You might split for different test_exception_* cases for a more clear report.

You might split for different test_exception_* cases for a more clear report.
phosit marked this conversation as resolved
@ -802,2 +800,3 @@
// We're done, get the exceptions from the futures.
for (Future<void>& future : m_Futures)
future.CancelOrWait();
future.Get();
Member

Code seems a bit strange, why do we call Get if we don't use the result? The CancelOrWait version seems more clear.

Code seems a bit strange, why do we call Get if we don't use the result? The CancelOrWait version seems more clear.
Author
Member

Like the comment explains it's done to get the exceptions from the tasks.
The interface is like std::future.

Like the comment explains it's done to get the exceptions from the tasks. The interface is like `std::future`.
Member

Yeah, I read the comment but the naming is confusing for me. I'd prefer to have something more explicit like:

// We're done, wait till the end of the execution and get the exceptions from the futures.
for (Future<void>& future : m_Futures)
	future.WaitAndThrowIfNeeded();
Yeah, I read the comment but the naming is confusing for me. I'd prefer to have something more explicit like: ```cpp // We're done, wait till the end of the execution and get the exceptions from the futures. for (Future<void>& future : m_Futures) future.WaitAndThrowIfNeeded(); ```
Author
Member

Meh... I'd go with the semantics of std::future.
A Future<T>::Get call does wait for the task to finish, if the task ended in an exception rethrow that and in the other case return what the task returned.
A Future<void>::Get call does everything of the above.

The caller code is more consistent: A simple guideline could be "call Future<T>::Get on each Future". Where in your proposal it "call Future<T>::Get on each Future except if it's a Future<void> then you have to call Future<void>::WaitAndThrowIfNeeded."
The library is more consistent: There is less SFINAE.

Peoply might be confused that they think they have to call Future<T>::WaitAndThrowIfNeeded and Future<void>::Get.

Meh... I'd go with the semantics of `std::future`. A `Future<T>::Get` call does wait for the task to finish, if the task ended in an exception rethrow that and in the other case return what the task returned. A `Future<void>::Get` call does everything of the above. The caller code is more consistent: A simple guideline could be "call `Future<T>::Get` on each Future". Where in your proposal it "call `Future<T>::Get` on each Future except if it's a `Future<void>` then you have to call `Future<void>::WaitAndThrowIfNeeded`." The library is more consistent: There is less SFINAE. Peoply might be confused that they think they have to call `Future<T>::WaitAndThrowIfNeeded` and `Future<void>::Get`.
phosit force-pushed task_exception from 95e2c6e4a6 to 95a58ab083 2024-10-05 19:35:49 +02:00 Compare
phosit force-pushed task_exception from 95a58ab083 to bb87204917 2024-10-08 18:58:20 +02:00 Compare
phosit merged commit 0eed117e6d into main 2024-10-08 20:05:49 +02:00
phosit deleted branch task_exception 2024-10-08 20:05:55 +02:00
Sign in to join this conversation.
No reviewers
No Milestone
No project
No Assignees
2 Participants
Notifications
Due Date
The due date is invalid or out of range. Please use the format 'yyyy-mm-dd'.

No due date set.

Dependencies

No dependencies set.

Reference: 0ad/0ad#7072
No description provided.