Skip to content

Commit 94f9aaf

Browse files
sithhellmsimberg
authored andcommitted
Fixing yet another race in future_data
Run the completion handlers as static functions. With this change, completion handlers can not have exceptions that escape the handler as this would lead to silently swallow the exceptions. Previously, this would have triggered a "promise_already_satisfied" error. This is wrong behavior, exceptions escaping completion handling should be considered a bug. always.
1 parent 182512f commit 94f9aaf

File tree

2 files changed

+35
-65
lines changed

2 files changed

+35
-65
lines changed

hpx/lcos/detail/future_data.hpp

+5-5
Original file line numberDiff line numberDiff line change
@@ -264,15 +264,15 @@ namespace detail
264264
// continuation support
265265

266266
// deferred execution of a given continuation
267-
bool run_on_completed(completed_callback_type&& on_completed,
268-
std::exception_ptr& ptr);
269-
bool run_on_completed(completed_callback_vector_type&& on_completed,
270-
std::exception_ptr& ptr);
267+
static void run_on_completed(
268+
completed_callback_type&& on_completed) noexcept;
269+
static void run_on_completed(
270+
completed_callback_vector_type&& on_completed) noexcept;
271271

272272
// make sure continuation invocation does not recurse deeper than
273273
// allowed
274274
template <typename Callback>
275-
void handle_on_completed(Callback&& on_completed);
275+
static void handle_on_completed(Callback&& on_completed);
276276

277277
/// Set the callback which needs to be invoked when the future becomes
278278
/// ready. If the future is ready the function will be invoked

src/lcos/detail/future_data.cpp

+30-60
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@ namespace hpx { namespace lcos { namespace detail
4747
};
4848

4949
///////////////////////////////////////////////////////////////////////////
50-
static bool run_on_completed_on_new_thread(
51-
util::unique_function_nonser<bool()> && f, error_code& ec)
50+
template <typename Callback>
51+
static void run_on_completed_on_new_thread(Callback&& f)
5252
{
53-
lcos::local::futures_factory<bool()> p(std::move(f));
53+
lcos::local::futures_factory<void()> p(std::forward<Callback>(f));
5454

5555
bool is_hpx_thread = nullptr != hpx::threads::get_self_ptr();
5656
hpx::launch policy = launch::fork;
@@ -61,25 +61,17 @@ namespace hpx { namespace lcos { namespace detail
6161
threads::thread_id_type tid = p.apply(
6262
policy, threads::thread_priority_boost,
6363
threads::thread_stacksize_current,
64-
threads::thread_schedule_hint(),
65-
ec);
66-
if (ec) return false;
64+
threads::thread_schedule_hint());
6765

6866
// wait for the task to run
6967
if (is_hpx_thread)
7068
{
7169
// make sure this thread is executed last
7270
hpx::this_thread::yield_to(thread::id(std::move(tid)));
73-
return p.get_future().get(ec);
74-
}
75-
else
76-
{
77-
// If we are not on a HPX thread, we need to return immediately, to
78-
// allow the newly spawned thread to execute. This might swallow
79-
// possible exceptions bubbling up from the completion handler (which
80-
// shouldn't happen anyway...
81-
return true;
71+
return p.get_future().get();
8272
}
73+
// If we are not on a HPX thread, we need to return immediately, to
74+
// allow the newly spawned thread to execute.
8375
}
8476

8577
///////////////////////////////////////////////////////////////////////////
@@ -146,37 +138,27 @@ namespace hpx { namespace lcos { namespace detail
146138
}
147139

148140
// deferred execution of a given continuation
149-
bool future_data_base<traits::detail::future_data_void>::
150-
run_on_completed(completed_callback_type && on_completed,
151-
std::exception_ptr& ptr)
141+
void future_data_base<traits::detail::future_data_void>::run_on_completed(
142+
completed_callback_type&& on_completed) noexcept
152143
{
153144
try {
154145
hpx::util::annotate_function annotate(on_completed);
155146
on_completed();
156147
}
157148
catch (...) {
158-
ptr = std::current_exception();
159-
return false;
149+
// If the completion handler throws an exception, there's nothing
150+
// we can do, report the exception and terminate.
151+
hpx::detail::report_exception_and_terminate(std::current_exception());
160152
}
161-
return true;
162153
}
163154

164-
bool future_data_base<traits::detail::future_data_void>::
165-
run_on_completed(completed_callback_vector_type && on_completed,
166-
std::exception_ptr& ptr)
155+
void future_data_base<traits::detail::future_data_void>::run_on_completed(
156+
completed_callback_vector_type&& on_completed) noexcept
167157
{
168-
try {
169-
for (auto& func: on_completed)
170-
{
171-
hpx::util::annotate_function annotate(func);
172-
func();
173-
}
174-
}
175-
catch (...) {
176-
ptr = std::current_exception();
177-
return false;
158+
for (auto&& func: on_completed)
159+
{
160+
run_on_completed(std::move(func));
178161
}
179-
return true;
180162
}
181163

182164
// make sure continuation invocation does not recurse deeper than
@@ -199,37 +181,25 @@ namespace hpx { namespace lcos { namespace detail
199181
if (!recurse_asynchronously)
200182
{
201183
// directly execute continuation on this thread
202-
std::exception_ptr ptr;
203-
if (!run_on_completed(std::forward<Callback>(on_completed), ptr))
204-
{
205-
error_code ec(lightweight);
206-
set_exception(hpx::detail::access_exception(ec));
207-
}
184+
run_on_completed(std::forward<Callback>(on_completed));
208185
}
209186
else
210187
{
211188
// re-spawn continuation on a new thread
212-
boost::intrusive_ptr<future_data_base> this_(this);
189+
void (*p)(Callback &&) = &future_data_base::run_on_completed;
213190

214-
bool (future_data_base::*p)(Callback&&, std::exception_ptr&) =
215-
&future_data_base::run_on_completed;
216-
217-
error_code ec(lightweight);
218-
std::exception_ptr ptr;
219-
if (!run_on_completed_on_new_thread(
220-
util::deferred_call(p, std::move(this_),
221-
std::forward<Callback>(on_completed), std::ref(ptr)),
222-
ec))
191+
try
192+
{
193+
run_on_completed_on_new_thread(
194+
util::deferred_call(
195+
p, std::forward<Callback>(on_completed)));
196+
}
197+
catch(...)
223198
{
224-
// thread creation went wrong
225-
if (ec) {
226-
set_exception(hpx::detail::access_exception(ec));
227-
return;
228-
}
229-
230-
// re-throw exception in this context
231-
HPX_ASSERT(ptr); // exception should have been set
232-
std::rethrow_exception(ptr);
199+
// If an exception while creating the new task or inside the
200+
// completion handler is thrown, there is nothing we can do...
201+
// ... but terminate and report the error
202+
hpx::detail::report_exception_and_terminate(std::current_exception());
233203
}
234204
}
235205
}

0 commit comments

Comments
 (0)