Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-06 09:25:58

0001 /*
0002     Copyright (c) 2026 UXL Foundation Contributors
0003 
0004     Licensed under the Apache License, Version 2.0 (the "License");
0005     you may not use this file except in compliance with the License.
0006     You may obtain a copy of the License at
0007 
0008         http://www.apache.org/licenses/LICENSE-2.0
0009 
0010     Unless required by applicable law or agreed to in writing, software
0011     distributed under the License is distributed on an "AS IS" BASIS,
0012     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0013     See the License for the specific language governing permissions and
0014     limitations under the License.
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 }; // class request_id
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 }; // class resource_handle_optional
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 // TODO: use actual fair implementation with starvation avoidance
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         // TODO: consider using an aggregator instead of mutex
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             // Resource available immediately
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 }; // class resource_limiter
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; // Local counter in case the resource is denied
0337         auto handle_optional = std::get<Index>(consumers).acquire_from_provider(id);
0338         if (handle_optional.has_value()) {
0339             // Successfully acquired resource - save the handle and proceed to the next resource
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             // One of the resources denied the request
0346             release_resources_helper<Index>::run(consumers, id, req_data);
0347             body_ptr->release_self_ref(id, req_data); // release the self-reference held at the beginning of resource acquisition
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     // TODO: should concurrent container be used instead?
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); // TODO: report real pressure
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         // Increment the counter to avoid another resource reacquisition by notify()
0479         // while current acquisition is in progress
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             // Access to all resources is granted
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         // TODO: report real pressure, investigate if it should be done before or after the release
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 }; // class resource_limited_node
0658 
0659 } // namespace d2
0660 } // namespace detail
0661 } // namespace tbb
0662 
0663 #endif // __TBB__flow_graph_resource_limiting_H