Skip to content

Commit fb390c5

Browse files
authored
Fix resource_limited_node exception safety (#1997)
1 parent 03c286d commit fb390c5

File tree

2 files changed

+69
-38
lines changed

2 files changed

+69
-38
lines changed

include/oneapi/tbb/detail/_flow_graph_resource_limiting.h

Lines changed: 11 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121
#error Do not #include this internal file directly; use public TBB headers instead.
2222
#endif
2323

24-
#include <chrono>
2524
#include <unordered_map>
2625
#include <forward_list>
2726
#include <functional>
@@ -37,29 +36,21 @@ template <typename ResourceHandle>
3736
class resource_consumer_base;
3837

3938
class request_id {
40-
using clock_type = std::chrono::high_resolution_clock;
41-
using time_point = clock_type::time_point;
42-
43-
time_point m_time_point;
4439
std::uint64_t m_unique_integer;
4540
public:
4641
request_id(const std::uint64_t& unique_integer)
47-
: m_time_point(clock_type::now())
48-
, m_unique_integer(unique_integer)
42+
: m_unique_integer(unique_integer)
4943
{}
5044

51-
struct hash {
52-
std::hash<std::uint64_t> m_hash;
53-
45+
struct hash : protected std::hash<std::uint64_t> {
5446
std::size_t operator()(request_id id) const {
55-
return m_hash(id.m_unique_integer);
47+
return std::hash<std::uint64_t>::operator()(id.m_unique_integer);
5648
}
5749
};
5850

59-
struct equal {
51+
struct equal : protected std::equal_to<std::uint64_t> {
6052
bool operator()(request_id lhs, request_id rhs) const {
61-
return lhs.m_time_point == rhs.m_time_point &&
62-
lhs.m_unique_integer == rhs.m_unique_integer;
53+
return std::equal_to<std::uint64_t>::operator()(lhs.m_unique_integer, rhs.m_unique_integer);
6354
}
6455
};
6556
}; // class request_id
@@ -495,9 +486,12 @@ class resource_limited_body_leaf
495486
void try_acquire_resources_and_execute(request_id id, request_data_type& req_data) {
496487
if (try_acquire_resources(id, req_data)) {
497488
// Access to all resources is granted
498-
call_body(req_data.input_message, req_data.output_ports, req_data.handles);
499-
release_resources(id, req_data);
500-
remove_request(id);
489+
try_call([&] {
490+
call_body(req_data.input_message, req_data.output_ports, req_data.handles);
491+
}).on_completion([&] {
492+
release_resources(id, req_data);
493+
remove_request(id);
494+
});
501495
}
502496
}
503497

test/tbb/test_resource_limited_node.cpp

Lines changed: 58 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525

2626
#include "tbb/flow_graph.h"
2727

28+
//! \file test_resource_limited_node.cpp
29+
//! \brief Test for [preview] functionality
30+
2831
using input_msg = conformance::message</*default_ctor = */true, /*copy_ctor = */true, /*copy_assign = */false>;
2932
using output_msg = conformance::message</*default_ctor = */false, /*copy_ctor = */false, /*copy_assign = */false>;
3033

@@ -293,47 +296,78 @@ void test_root_genie() {
293296
// TODO: add fairness checks
294297
}
295298

296-
void test_cancellation_with_active_requests() {
299+
void test_cancellation_with_active_requests(bool exception) {
297300
using namespace tbb::flow;
298301

299302
int resource_value = 1;
300303
int input_value = 2;
301304
resource_limiter<int> limiter(resource_value);
302305

303-
using node_type = resource_limited_node<int, std::tuple<int>>;
306+
using node_type = resource_limited_node<int, std::tuple<>>;
304307
using ports_type = typename node_type::output_ports_type;
308+
309+
#if TBB_USE_EXCEPTIONS
310+
struct body_exception {};
311+
#endif
312+
313+
tbb::task_group_context g2_context(tbb::task_group_context::isolated);
314+
graph g1;
315+
graph g2(g2_context);
305316

306-
graph g;
317+
const std::size_t n_submissions = 100;
318+
std::atomic<std::size_t> g2_node_body_counter{0};
307319

308-
node_type cancel_node(g, unlimited, std::tie(limiter),
309-
[&](int input, ports_type& ports, int resource) {
320+
node_type keep_using_node(g2, unlimited, std::tie(limiter),
321+
[&](int input, ports_type&, int resource) {
310322
CHECK_MESSAGE(input == input_value, "Incorrect input");
311323
CHECK_MESSAGE(resource == resource_value, "Incorrect resource");
312324

313-
for (int i = 0; i < 100; ++i) {
314-
std::get<0>(ports).try_put(input);
325+
++g2_node_body_counter;
326+
});
327+
328+
node_type cancel_node(g1, unlimited, std::tie(limiter),
329+
[&](int input, ports_type&, int resource) {
330+
CHECK_MESSAGE(input == input_value, "Incorrect input");
331+
CHECK_MESSAGE(resource == resource_value, "Incorrect resource");
332+
333+
for (std::size_t i = 0; i < n_submissions; ++i) {
334+
keep_using_node.try_put(input);
315335
}
316336

317-
g.cancel();
337+
if (exception) {
338+
#if TBB_USE_EXCEPTIONS
339+
throw body_exception{};
340+
#else
341+
CHECK_MESSAGE(false, "exception test was called when exceptions are not supported");
342+
#endif
343+
} else {
344+
g1.cancel();
345+
}
318346

319-
for (int i = 0; i < 100; ++i) {
320-
std::get<0>(ports).try_put(input);
347+
for (std::size_t i = 0; i < n_submissions; ++i) {
348+
keep_using_node.try_put(input);
321349
}
322350
});
323351

324-
std::atomic<std::size_t> num_bodies{0};
352+
cancel_node.try_put(input_value);
325353

326-
node_type successor_node(g, unlimited, std::tie(limiter),
327-
[&](int, ports_type&, int) {
328-
++num_bodies;
329-
});
354+
#if TBB_USE_EXCEPTIONS
355+
bool caught_exception = false;
356+
try {
357+
g1.wait_for_all();
358+
} catch (body_exception) {
359+
caught_exception = true;
360+
}
330361

331-
make_edge(output_port<0>(cancel_node), successor_node);
362+
CHECK_MESSAGE(exception == caught_exception, "Expected exception was not caught");
363+
#else
364+
g1.wait_for_all();
365+
#endif
332366

333-
cancel_node.try_put(input_value);
334-
g.wait_for_all();
335-
CHECK_MESSAGE(num_bodies < tbb::this_task_arena::max_concurrency(),
336-
"Maximum number of node bodies exceeded");
367+
g2.wait_for_all();
368+
std::size_t expected_g2_body_calls = exception ? n_submissions : 2 * n_submissions;
369+
CHECK_MESSAGE(g2_node_body_counter == expected_g2_body_calls,
370+
"Incorrect number of g2 node body calls");
337371
}
338372

339373
//! \brief \ref interface
@@ -463,6 +497,9 @@ TEST_CASE("resource_limited_node and std::invoke") {
463497

464498
//! \brief \ref error_guessing
465499
TEST_CASE("resource_limited_node cancellation with active requests") {
466-
test_cancellation_with_active_requests();
500+
test_cancellation_with_active_requests(/*exception =*/false);
501+
#if TBB_USE_EXCEPTIONS
502+
test_cancellation_with_active_requests(/*exception = */true);
503+
#endif
467504
}
468505
#endif

0 commit comments

Comments
 (0)