File indexing completed on 2026-08-06 09:25:58
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #ifndef __TBB__flow_graph_resource_limiting_H
0018 #define __TBB__flow_graph_resource_limiting_H
0019
0020 #ifndef __TBB_flow_graph_H
0021 #error Do not #include this internal file directly; use public TBB headers instead.
0022 #endif
0023
0024 #include <unordered_map>
0025 #include <forward_list>
0026 #include <functional>
0027 #include <utility>
0028 #include <atomic>
0029 #include <tuple>
0030
0031 namespace tbb {
0032 namespace detail {
0033 namespace d2 {
0034
0035 template <typename ResourceHandle>
0036 class resource_consumer_base;
0037
0038 class request_id {
0039 std::uint64_t m_unique_integer;
0040 public:
0041 request_id(const std::uint64_t& unique_integer)
0042 : m_unique_integer(unique_integer)
0043 {}
0044
0045 struct hash : protected std::hash<std::uint64_t> {
0046 std::size_t operator()(request_id id) const {
0047 return std::hash<std::uint64_t>::operator()(id.m_unique_integer);
0048 }
0049 };
0050
0051 struct equal : protected std::equal_to<std::uint64_t> {
0052 bool operator()(request_id lhs, request_id rhs) const {
0053 return std::equal_to<std::uint64_t>::operator()(lhs.m_unique_integer, rhs.m_unique_integer);
0054 }
0055 };
0056 };
0057
0058 template <typename ResourceHandle>
0059 class resource_handle_optional {
0060 union {
0061 ResourceHandle m_resource_handle;
0062 };
0063 bool m_has_value;
0064
0065 template <typename... Args>
0066 void construct(Args&&... args) {
0067 ::new(&m_resource_handle) ResourceHandle(std::forward<Args>(args)...);
0068 }
0069 public:
0070 struct in_place_t {};
0071
0072 resource_handle_optional()
0073 : m_has_value(false)
0074 {}
0075
0076 template <typename... Args>
0077 resource_handle_optional(in_place_t, Args&&... args)
0078 : m_has_value(true)
0079 {
0080 construct(std::forward<Args>(args)...);
0081 }
0082
0083 resource_handle_optional(const resource_handle_optional&) = delete;
0084 resource_handle_optional& operator=(const resource_handle_optional&) = delete;
0085
0086 resource_handle_optional(resource_handle_optional&& other)
0087 : m_has_value(other.m_has_value)
0088 {
0089 if (m_has_value) {
0090 construct(std::move(other.m_resource_handle));
0091 }
0092 }
0093
0094 resource_handle_optional& operator=(resource_handle_optional&& other) {
0095 if (this != &other) {
0096 if (m_has_value) m_resource_handle.~ResourceHandle();
0097 m_has_value = other.m_has_value;
0098 if (other.m_has_value) {
0099 construct(std::move(other.m_resource_handle));
0100 }
0101 }
0102 return *this;
0103 }
0104
0105 ~resource_handle_optional() {
0106 if (m_has_value) {
0107 m_resource_handle.~ResourceHandle();
0108 }
0109 }
0110
0111 bool has_value() const { return m_has_value; }
0112
0113 ResourceHandle& value() {
0114 __TBB_ASSERT(has_value(), nullptr);
0115 return m_resource_handle;
0116 }
0117 };
0118
0119 template <typename ResourceHandle>
0120 class resource_provider_base {
0121 public:
0122 using consumer_type = resource_consumer_base<ResourceHandle>;
0123 using optional_type = resource_handle_optional<ResourceHandle>;
0124
0125 virtual void request(consumer_type&, request_id) = 0;
0126 virtual optional_type acquire(consumer_type&, request_id) = 0;
0127 virtual void report_pressure(consumer_type&, std::size_t) = 0;
0128 virtual void release(consumer_type&, request_id, optional_type&&) = 0;
0129 virtual ~resource_provider_base() = default;
0130 };
0131
0132 template <typename ResourceHandle>
0133 class resource_consumer_base {
0134 public:
0135 using provider_type = resource_provider_base<ResourceHandle>;
0136 virtual void notify(provider_type&, request_id) = 0;
0137 virtual ~resource_consumer_base() = default;
0138 };
0139
0140
0141 template <typename ResourceHandle>
0142 class resource_limiter : public resource_provider_base<ResourceHandle> {
0143 public:
0144 using resource_handle_type = ResourceHandle;
0145 using consumer_type = typename resource_provider_base<ResourceHandle>::consumer_type;
0146 using optional_type = typename resource_provider_base<ResourceHandle>::optional_type;
0147
0148 template <typename Handle, typename... Handles>
0149 resource_limiter(Handle&& handle, Handles&&... handles) {
0150 emplace_handles(std::forward<Handle>(handle), std::forward<Handles>(handles)...);
0151 }
0152
0153 void request(consumer_type& consumer, request_id id) override {
0154
0155 tbb::spin_mutex::scoped_lock lock(m_mutex);
0156
0157 if (m_resource_handles.empty()) {
0158 m_consumers.emplace_front(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(&consumer));
0159 } else {
0160
0161 lock.release();
0162 consumer.notify(*this, id);
0163 }
0164 }
0165
0166 optional_type acquire(consumer_type& consumer, request_id id) override {
0167 tbb::spin_mutex::scoped_lock lock(m_mutex);
0168
0169 if (m_resource_handles.empty()) {
0170 m_consumers.emplace_front(std::piecewise_construct, std::forward_as_tuple(id), std::forward_as_tuple(&consumer));
0171 return optional_type{};
0172 } else {
0173 ResourceHandle handle = std::move(m_resource_handles.front());
0174 m_resource_handles.pop_front();
0175 return {typename optional_type::in_place_t{}, std::move(handle)};
0176 }
0177 }
0178
0179 void release(consumer_type&, request_id, optional_type&& handle) override {
0180 __TBB_ASSERT(handle.has_value(), nullptr);
0181 tbb::spin_mutex::scoped_lock lock(m_mutex);
0182
0183 m_resource_handles.emplace_front(std::move(handle.value()));
0184
0185 auto consumers = std::move(m_consumers);
0186 m_consumers.clear();
0187
0188 lock.release();
0189 for (auto consumer_dt : consumers) {
0190 consumer_dt.second->notify(*this, consumer_dt.first);
0191 }
0192 }
0193
0194 void report_pressure(consumer_type&, std::size_t) override {}
0195
0196 using consumer_data = std::pair<request_id, resource_consumer_base<ResourceHandle>*>;
0197
0198 private:
0199 template <typename Handle, typename... Handles>
0200 void emplace_handles(Handle&& handle, Handles&&... handles) {
0201 m_resource_handles.emplace_front(std::forward<Handle>(handle));
0202 emplace_handles(std::forward<Handles>(handles)...);
0203 }
0204
0205 void emplace_handles() {}
0206
0207 tbb::spin_mutex m_mutex;
0208 std::forward_list<ResourceHandle> m_resource_handles;
0209 std::forward_list<consumer_data> m_consumers;
0210 };
0211
0212 template <typename Input, typename OutputPorts>
0213 class resource_limited_body {
0214 graph& m_graph;
0215 public:
0216 virtual void operator()(const Input& input, OutputPorts& ports) = 0;
0217 virtual void notify(request_id id) = 0;
0218 virtual resource_limited_body* clone() = 0;
0219 virtual void* get_body_ptr() = 0;
0220 virtual ~resource_limited_body() = default;
0221
0222 resource_limited_body(graph& g) : m_graph(g) {}
0223
0224 graph& graph_reference() { return m_graph; }
0225 };
0226
0227 template <typename Input, typename OutputPorts, typename ResourceProvider>
0228 class resource_consumer : public resource_consumer_base<typename ResourceProvider::resource_handle_type> {
0229 public:
0230 using resource_handle_type = typename ResourceProvider::resource_handle_type;
0231 using resource_limited_body_type = resource_limited_body<Input, OutputPorts>;
0232 using optional_type = typename ResourceProvider::optional_type;
0233
0234 resource_consumer(ResourceProvider& provider, resource_limited_body_type* body_ptr)
0235 : m_resource_provider(provider)
0236 , m_body_ptr(body_ptr)
0237 {}
0238
0239 void request_from_provider(request_id id) {
0240 m_resource_provider.request(*this, id);
0241 }
0242
0243
0244 optional_type acquire_from_provider(request_id id) {
0245 return m_resource_provider.acquire(*this, id);
0246 }
0247
0248 void release_to_provider(request_id id, optional_type&& handle) {
0249 m_resource_provider.release(*this, id, std::move(handle));
0250 }
0251
0252 void report_pressure_to_provider(std::size_t pressure) {
0253 m_resource_provider.report_pressure(*this, pressure);
0254 }
0255
0256 void notify(resource_provider_base<resource_handle_type>& provider, request_id id) override {
0257 __TBB_ASSERT(&provider == &m_resource_provider, "Provider-consumer mismatch");
0258 m_body_ptr->notify(id);
0259 tbb::detail::suppress_unused_warning(provider);
0260 }
0261
0262 void set_body_ptr(resource_limited_body_type* body_ptr) {
0263 m_body_ptr = body_ptr;
0264 }
0265
0266 private:
0267 ResourceProvider& m_resource_provider;
0268 resource_limited_body_type* m_body_ptr;
0269 };
0270
0271 template <typename Input, typename OutputPorts, typename HandlesTuple>
0272 struct request_data {
0273 Input input_message;
0274 OutputPorts& output_ports;
0275 std::atomic<std::size_t> notify_counter;
0276 HandlesTuple handles;
0277
0278 request_data(const Input& input, OutputPorts& ports)
0279 : input_message(input)
0280 , output_ports(ports)
0281 , notify_counter(std::tuple_size<HandlesTuple>::value + 1)
0282 {}
0283 };
0284
0285 template <std::size_t Index, std::size_t MaxIndex>
0286 struct request_resources_helper {
0287 template <typename ConsumerTuple>
0288 static void run(ConsumerTuple& consumers, request_id id) {
0289 std::get<Index>(consumers).request_from_provider(id);
0290 request_resources_helper<Index + 1, MaxIndex>::run(consumers, id);
0291 }
0292 };
0293
0294 template <std::size_t MaxIndex>
0295 struct request_resources_helper<MaxIndex, MaxIndex> {
0296 template <typename ConsumerTuple>
0297 static void run(ConsumerTuple&, request_id) {}
0298 };
0299
0300 template <std::size_t Index>
0301 struct release_resources_helper {
0302 template <typename ConsumerTuple, typename RequestData>
0303 static void run(ConsumerTuple& consumers, request_id id, RequestData& req_data) {
0304 std::get<Index - 1>(consumers).release_to_provider(id, std::move(std::get<Index - 1>(req_data.handles)));
0305 std::get<Index - 1>(req_data.handles) = {};
0306 release_resources_helper<Index - 1>::run(consumers, id, req_data);
0307 }
0308 };
0309
0310 template <>
0311 struct release_resources_helper<0> {
0312 template <typename ConsumerTuple, typename RequestData>
0313 static void run(ConsumerTuple&, request_id, RequestData&) {}
0314 };
0315
0316 template <std::size_t Index>
0317 struct set_body_ptr_helper {
0318 template <typename ConsumerTuple, typename Body>
0319 static void run(ConsumerTuple& consumers, Body* body_ptr) {
0320 std::get<Index - 1>(consumers).set_body_ptr(body_ptr);
0321 set_body_ptr_helper<Index - 1>::run(consumers, body_ptr);
0322 }
0323 };
0324
0325 template <>
0326 struct set_body_ptr_helper<0> {
0327 template <typename ConsumerTuple, typename Body>
0328 static void run(ConsumerTuple&, Body*) {}
0329 };
0330
0331 template <std::size_t Index, std::size_t MaxIndex>
0332 struct acquire_resources_helper {
0333 template <typename Body, typename ConsumerTuple, typename RequestData>
0334 static bool run(Body* body_ptr, ConsumerTuple& consumers, request_id id, RequestData& req_data) {
0335 __TBB_ASSERT(req_data.notify_counter == 1, "Incorrect notify counter");
0336 ++req_data.notify_counter;
0337 auto handle_optional = std::get<Index>(consumers).acquire_from_provider(id);
0338 if (handle_optional.has_value()) {
0339
0340 --req_data.notify_counter;
0341 std::get<Index>(req_data.handles) = std::move(handle_optional);
0342 return acquire_resources_helper<Index + 1, MaxIndex>::run(body_ptr, consumers, id, req_data);
0343 } else {
0344 __TBB_ASSERT(req_data.notify_counter >= 1, "Incorrect notify counter");
0345
0346 release_resources_helper<Index>::run(consumers, id, req_data);
0347 body_ptr->release_self_ref(id, req_data);
0348 return false;
0349 }
0350 }
0351 };
0352
0353 template <std::size_t MaxIndex>
0354 struct acquire_resources_helper<MaxIndex, MaxIndex> {
0355 template <typename Body, typename ConsumerTuple, typename RequestData>
0356 static bool run(Body*, ConsumerTuple&, request_id, RequestData& req_data) {
0357 --req_data.notify_counter;
0358 return true;
0359 }
0360 };
0361
0362 template <std::size_t Index, std::size_t MaxIndex>
0363 struct report_pressure_helper {
0364 template <typename ConsumerTuple>
0365 static void run(ConsumerTuple& consumers, std::size_t pressure) {
0366 std::get<Index>(consumers).report_pressure_to_provider(pressure);
0367 report_pressure_helper<Index + 1, MaxIndex>::run(consumers, pressure);
0368 }
0369 };
0370
0371 template <std::size_t MaxIndex>
0372 struct report_pressure_helper<MaxIndex, MaxIndex> {
0373 template <typename ConsumerTuple>
0374 static void run(ConsumerTuple&, std::size_t) {}
0375 };
0376
0377 template <typename BodyLeaf, typename RequestDataType>
0378 class try_acquire_resources_and_execute_task : public graph_task {
0379 BodyLeaf* m_body;
0380 request_id m_id;
0381 RequestDataType& m_request_data;
0382
0383 public:
0384 try_acquire_resources_and_execute_task(graph& g, d1::small_object_allocator& allocator, BodyLeaf* body_leaf,
0385 request_id id, RequestDataType& request_data)
0386 : graph_task(g, allocator, no_priority)
0387 , m_body(body_leaf)
0388 , m_id(id)
0389 , m_request_data(request_data)
0390 {
0391 __TBB_ASSERT(body_leaf != nullptr, nullptr);
0392 }
0393
0394 d1::task* execute(d1::execution_data& ed) override {
0395 m_body->try_acquire_resources_and_execute(m_id, m_request_data);
0396 graph_task::template finalize<try_acquire_resources_and_execute_task>(ed);
0397 return nullptr;
0398 }
0399
0400 d1::task* cancel(d1::execution_data& ed) override {
0401 m_body->remove_request(m_id);
0402 graph_task::template finalize<try_acquire_resources_and_execute_task>(ed);
0403 return nullptr;
0404 }
0405 };
0406
0407 template <typename Input, typename OutputPorts, typename Body, typename... ResourceProviders>
0408 class resource_limited_body_leaf
0409 : public resource_limited_body<Input, OutputPorts>
0410 {
0411 using handles_tuple_type = std::tuple<typename ResourceProviders::optional_type...>;
0412 using consumers_tuple_type = std::tuple<resource_consumer<Input, OutputPorts, ResourceProviders>...>;
0413 using request_data_type = request_data<Input, OutputPorts, handles_tuple_type>;
0414
0415 using requests_map_type = std::unordered_map<request_id, request_data_type, request_id::hash, request_id::equal>;
0416
0417 tbb::spin_mutex m_mutex;
0418 requests_map_type m_requests;
0419 consumers_tuple_type m_consumers;
0420 Body m_body;
0421 std::uint64_t m_counter;
0422
0423 template <typename ConsumersTuple>
0424 resource_limited_body_leaf(graph& g, ConsumersTuple&& consumers_tuple, const Body& body)
0425 : resource_limited_body<Input, OutputPorts>(g)
0426 , m_consumers(std::forward<ConsumersTuple>(consumers_tuple))
0427 , m_body(body)
0428 , m_counter(0)
0429 {}
0430
0431 public:
0432 resource_limited_body_leaf(graph& g, std::tuple<ResourceProviders&...> resource_providers, const Body& body)
0433 : resource_limited_body_leaf(g, get_consumers_tuple(resource_providers), body)
0434 {}
0435
0436 consumers_tuple_type get_consumers_tuple(std::tuple<ResourceProviders&...> resource_providers) {
0437 return get_consumers_tuple_impl(resource_providers, tbb::detail::make_index_sequence<sizeof...(ResourceProviders)>());
0438 }
0439
0440 template <std::size_t... Idx>
0441 consumers_tuple_type get_consumers_tuple_impl(std::tuple<ResourceProviders&...> resource_providers,
0442 tbb::detail::index_sequence<Idx...>)
0443 {
0444 return consumers_tuple_type({std::get<Idx>(resource_providers), this}...);
0445 }
0446
0447 void operator()(const Input& input, OutputPorts& ports) override {
0448 auto& res = form_request(input, ports);
0449 report_pressure(0);
0450 request_resources(res.first);
0451 release_self_ref(res.first, res.second);
0452 }
0453
0454 typename requests_map_type::reference form_request(const Input& input, OutputPorts& ports) {
0455 tbb::spin_mutex::scoped_lock lock(m_mutex);
0456 request_id id{++m_counter};
0457 auto res = m_requests.emplace(std::piecewise_construct,
0458 std::forward_as_tuple(id),
0459 std::forward_as_tuple(input, ports));
0460 this->graph_reference().reserve_wait();
0461 __TBB_ASSERT(res.second, "Duplicated requests in the map");
0462 return *res.first;
0463 }
0464
0465 void request_resources(request_id id) {
0466 request_resources_helper<0, sizeof...(ResourceProviders)>::run(m_consumers, id);
0467 }
0468
0469 void release_self_ref(request_id id, request_data_type& req_data) {
0470 std::size_t prev_value = req_data.notify_counter--;
0471 __TBB_ASSERT(prev_value != 0, "Overflow detected");
0472 if (prev_value == 1) {
0473 try_acquire_resources_and_execute(id, req_data);
0474 }
0475 }
0476
0477 bool try_acquire_resources(request_id id, request_data_type& req_data) {
0478
0479
0480 std::size_t prev_value = req_data.notify_counter++;
0481 __TBB_ASSERT(prev_value == 0, "Incorrect notify counter before acquisition");
0482 tbb::detail::suppress_unused_warning(prev_value);
0483 return acquire_resources_helper<0, sizeof...(ResourceProviders)>::run(this, m_consumers, id, req_data);
0484 }
0485
0486 void try_acquire_resources_and_execute(request_id id, request_data_type& req_data) {
0487 if (try_acquire_resources(id, req_data)) {
0488
0489 try_call([&] {
0490 call_body(req_data.input_message, req_data.output_ports, req_data.handles);
0491 }).on_completion([&] {
0492 release_resources(id, req_data);
0493 remove_request(id);
0494 });
0495 }
0496 }
0497
0498 void release_resources(request_id id, request_data_type& req_data) {
0499
0500 report_pressure(0);
0501 release_resources_helper<sizeof...(ResourceProviders)>::run(m_consumers, id, req_data);
0502 }
0503
0504 void remove_request(request_id id) {
0505 tbb::spin_mutex::scoped_lock lock(m_mutex);
0506 std::size_t num_removed = m_requests.erase(id);
0507 this->graph_reference().release_wait();
0508 __TBB_ASSERT(num_removed == 1, "Removing unregistered request");
0509 tbb::detail::suppress_unused_warning(num_removed);
0510 }
0511
0512 void notify(request_id id) override {
0513 tbb::spin_mutex::scoped_lock lock(m_mutex);
0514 auto res = m_requests.find(id);
0515 __TBB_ASSERT(res != m_requests.end(), "Cannot find request for notification");
0516 request_data_type& data = res->second;
0517 lock.release();
0518
0519 std::size_t prev_value = data.notify_counter--;
0520 __TBB_ASSERT(prev_value != 0, "Overflow detected");
0521 if (prev_value == 1) {
0522 d1::small_object_allocator allocator;
0523 using task_type = try_acquire_resources_and_execute_task<resource_limited_body_leaf, request_data_type>;
0524 graph_task* t = allocator.new_object<task_type>(this->graph_reference(), allocator, this, id, data);
0525 spawn_in_graph_arena(this->graph_reference(), *t);
0526 }
0527 }
0528
0529 void report_pressure(std::size_t pressure) {
0530 report_pressure_helper<0, sizeof...(ResourceProviders)>::run(m_consumers, pressure);
0531 }
0532
0533 resource_limited_body_leaf* clone() override {
0534 resource_limited_body_leaf* new_body = new resource_limited_body_leaf(this->graph_reference(), m_consumers, this->m_body);
0535 set_body_ptr_helper<sizeof...(ResourceProviders)>::run(new_body->m_consumers, new_body);
0536 return new_body;
0537 }
0538
0539 void* get_body_ptr() override { return &m_body; }
0540
0541 template <typename ResourceHandlesTuple>
0542 void call_body(const Input& input, OutputPorts& ports, ResourceHandlesTuple& tuple) {
0543 call_body_impl(input, ports, tuple,
0544 tbb::detail::make_index_sequence<std::tuple_size<ResourceHandlesTuple>::value>());
0545 }
0546
0547 template <typename ResourceHandlesTuple, std::size_t... Idx>
0548 void call_body_impl(const Input& input, OutputPorts& ports, ResourceHandlesTuple& tuple,
0549 tbb::detail::index_sequence<Idx...>) {
0550 tbb::detail::invoke(m_body, input, ports, std::get<Idx>(tuple).value()...);
0551 }
0552 };
0553
0554 template <typename Input, typename OutputPorts>
0555 class resource_limited_input
0556 : public function_input_base<Input, queueing, cache_aligned_allocator<Input>,
0557 resource_limited_input<Input, OutputPorts>>
0558 {
0559 public:
0560 static constexpr int N = std::tuple_size<OutputPorts>::value;
0561 using input_type = Input;
0562 using output_ports_type = OutputPorts;
0563 using resource_limited_body_type = resource_limited_body<input_type, output_ports_type>;
0564 using class_type = resource_limited_input<input_type, output_ports_type>;
0565 using base_type = function_input_base<input_type, queueing, cache_aligned_allocator<input_type>, class_type>;
0566 using input_queue_type = function_input_queue<input_type, cache_aligned_allocator<input_type>>;
0567
0568 template <typename Body, typename... ResourceProviders>
0569 resource_limited_input(graph& g, std::size_t max_concurrency,
0570 std::tuple<ResourceProviders&...> resource_providers,
0571 Body& body)
0572 : base_type(g, max_concurrency, no_priority, is_body_noexcept(body, resource_providers))
0573 , m_body(new resource_limited_body_leaf<input_type, output_ports_type, Body, ResourceProviders...>(g, resource_providers, body))
0574 , m_init_body(new resource_limited_body_leaf<input_type, output_ports_type, Body, ResourceProviders...>(g, resource_providers, body))
0575 , m_output_ports(init_output_ports<output_ports_type>::call(g, m_output_ports))
0576 {}
0577
0578 resource_limited_input(const resource_limited_input& other)
0579 : base_type(other)
0580 , m_body(other.m_init_body->clone())
0581 , m_init_body(other.m_init_body->clone())
0582 , m_output_ports(init_output_ports<output_ports_type>::call(this->graph_reference(), m_output_ports))
0583 {}
0584
0585 ~resource_limited_input() {
0586 delete m_body;
0587 delete m_init_body;
0588 }
0589
0590 graph_task* apply_body_impl_bypass(const input_type& i
0591 __TBB_FLOW_GRAPH_METAINFO_ARG(const message_metainfo&))
0592 {
0593 (*m_body)(i, m_output_ports);
0594 graph_task* ttask = nullptr;
0595 if (base_type::my_max_concurrency != 0) {
0596 ttask = base_type::try_get_postponed_task(i);
0597 }
0598 return ttask ? ttask : SUCCESSFULLY_ENQUEUED;
0599 }
0600
0601 output_ports_type& output_ports() { return m_output_ports; }
0602
0603 template <typename Body>
0604 Body copy_function_object() {
0605 return *static_cast<Body*>(m_body->get_body_ptr());
0606 }
0607 protected:
0608 void reset(reset_flags f) {
0609 base_type::reset_function_input_base(f);
0610 if (f & rf_clear_edges) clear_element<N>::clear_this(m_output_ports);
0611 if (f & rf_reset_bodies) {
0612 resource_limited_body_type* tmp = m_init_body->clone();
0613 delete m_body;
0614 m_body = tmp;
0615 }
0616 __TBB_ASSERT(!(f & rf_clear_edges) || clear_element<N>::this_empty(m_output_ports), "resource_limited_node reset failed");
0617 }
0618 private:
0619 template <typename Body, typename... ResourceProviders>
0620 bool is_body_noexcept(Body& body, std::tuple<ResourceProviders&...>) {
0621 return noexcept(tbb::detail::invoke(body, std::declval<input_type>(), m_output_ports,
0622 std::declval<typename ResourceProviders::resource_handle_type&>()...));
0623 }
0624
0625 resource_limited_body_type* m_body;
0626 resource_limited_body_type* m_init_body;
0627 output_ports_type m_output_ports;
0628 };
0629
0630 template <typename Input, typename OutputTuple>
0631 class resource_limited_node
0632 : public graph_node
0633 , public resource_limited_input<Input, typename wrap_tuple_elements<multifunction_output, OutputTuple>::type>
0634 {
0635 public:
0636 using input_type = Input;
0637 using output_type = null_type;
0638 using output_ports_type = typename wrap_tuple_elements<multifunction_output, OutputTuple>::type;
0639 private:
0640 using input_impl_type = resource_limited_input<input_type, output_ports_type>;
0641 using input_impl_type::my_predecessors;
0642 public:
0643 template <typename Body, typename ResourceProvider, typename... ResourceProviders>
0644 resource_limited_node(graph& g, std::size_t concurrency,
0645 std::tuple<ResourceProvider&, ResourceProviders&...> resource_providers,
0646 Body body)
0647 : graph_node(g)
0648 , input_impl_type(g, concurrency, resource_providers, body)
0649 {}
0650
0651 resource_limited_node(const resource_limited_node& other)
0652 : graph_node(other.my_graph)
0653 , input_impl_type(other)
0654 {}
0655 protected:
0656 void reset_node(reset_flags f) override { input_impl_type::reset(f); }
0657 };
0658
0659 }
0660 }
0661 }
0662
0663 #endif