/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License Version 2.0 with LLVM Exceptions * (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at * * https://llvm.org/LICENSE.txt * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include #include #include #include #include #include namespace unifex { // Size of chunk used for cancellation allowing for some vectorisation constexpr size_t bulk_cancellation_chunk_size = 16; namespace _bulk_schedule { template struct _schedule_receiver { class type; }; template using schedule_receiver = typename _schedule_receiver::type; template class _schedule_receiver::type { public: template explicit type(Integral count, Receiver2&& r) : count_(std::move(count)) , receiver_((Receiver2&&)r) {} void set_value() noexcept(is_nothrow_receiver_of_v && is_nothrow_next_receiver_v) { using policy_t = decltype(get_execution_policy(receiver_)); auto stop_token = get_stop_token(receiver_); const bool stop_possible = !is_stop_never_possible_v && stop_token.stop_possible(); if(stop_possible) { for (Integral chunk_start(0); chunk_start < count_; chunk_start += bulk_cancellation_chunk_size) { if(stop_token.stop_requested()) { unifex::set_done(std::move(receiver_)); return; } Integral chunk_end = std::min(chunk_start + static_cast(bulk_cancellation_chunk_size), count_); if constexpr (is_one_of_v) { UNIFEX_DIAGNOSTIC_PUSH // Vectorisable version #if defined(__clang__) // When optimizing for size (e.g. with -Oz), Clang will not // vectorize this loop, and will emit a warning. There's // nothing to be done about the warning, though, so just // suppress it. #pragma clang diagnostic ignored "-Wpass-failed" #pragma clang loop vectorize(enable) interleave(enable) #elif defined(__GNUC__) #pragma GCC ivdep #elif defined(_MSC_VER) #pragma loop(ivdep) #endif for (Integral i(chunk_start); i < chunk_end; ++i) { unifex::set_next(receiver_, Integral(i)); } UNIFEX_DIAGNOSTIC_POP } else { // Sequenced version for (Integral i(chunk_start); i < chunk_end; ++i) { unifex::set_next(receiver_, Integral(i)); } } } } else { if constexpr (is_one_of_v) { UNIFEX_DIAGNOSTIC_PUSH // Vectorisable version #if defined(__clang__) // When optimizing for size (e.g. with -Oz), Clang will not // vectorize this loop, and will emit a warning. There's // nothing to be done about the warning, though, so just // suppress it. #pragma clang diagnostic ignored "-Wpass-failed" #pragma clang loop vectorize(enable) interleave(enable) #elif defined(__GNUC__) #pragma GCC ivdep #elif defined(_MSC_VER) #pragma loop(ivdep) #endif for (Integral i(0); i < count_; ++i) { unifex::set_next(receiver_, Integral(i)); } UNIFEX_DIAGNOSTIC_POP } else { // Sequenced version for (Integral i(0); i < count_; ++i) { unifex::set_next(receiver_, Integral(i)); } } } unifex::set_value(std::move(receiver_)); } template(typename Error) (requires receiver) void set_error(Error&& e) noexcept { unifex::set_error(std::move(receiver_), (Error&&)e); } void set_done() noexcept { unifex::set_done(std::move(receiver_)); } private: Integral count_; Receiver receiver_; }; template struct _default_sender { class type; }; template using default_sender = typename _default_sender::type; template class _default_sender::type { using schedule_sender_t = decltype(unifex::schedule(UNIFEX_DECLVAL(const Scheduler&))); public: template class Variant, template class Tuple> using value_types = Variant>; template class Variant, template class Tuple> using next_types = Variant>; template class Variant> using error_types = sender_error_types_t; static constexpr bool sends_done = true; template explicit type(Scheduler2&& s, Integral count) : scheduler_(static_cast(s)) , count_(std::move(count)) {} template(typename Self, typename BulkReceiver) (requires same_as, type> AND receiver_of AND is_next_receiver_v) friend auto tag_invoke(tag_t, Self&& s, BulkReceiver&& r) { return unifex::connect( unifex::schedule(static_cast(s).scheduler_), schedule_receiver>{ static_cast(s).count_, static_cast(r)}); } private: Scheduler scheduler_; Integral count_; }; struct _fn { template(typename Scheduler, typename Integral) (requires tag_invocable<_fn, Scheduler, Integral>) auto operator()(Scheduler&& s, Integral n) const noexcept(is_nothrow_tag_invocable_v<_fn, Scheduler, Integral>) -> tag_invoke_result_t<_fn, Scheduler, Integral> { return tag_invoke(_fn{}, (Scheduler&&)s, std::move(n)); } template(typename Scheduler, typename Integral) (requires scheduler AND (!tag_invocable<_fn, Scheduler, Integral>)) auto operator()(Scheduler&& s, Integral n) const noexcept( std::is_nothrow_constructible_v, Scheduler> && std::is_nothrow_move_constructible_v) -> default_sender, Integral> { return default_sender, Integral>{(Scheduler&&)s, std::move(n)}; } template constexpr auto operator()(Integral n) const noexcept(is_nothrow_callable_v< tag_t, _fn, Integral>) -> bind_back_result_t<_fn, Integral> { return bind_back(*this, n); } }; } // namespace _bulk_schedule inline constexpr _bulk_schedule::_fn bulk_schedule{}; } // namespace unifex #include