14523a761Stbbdev /*
24523a761Stbbdev Copyright (c) 2021 Intel Corporation
34523a761Stbbdev
44523a761Stbbdev Licensed under the Apache License, Version 2.0 (the "License");
54523a761Stbbdev you may not use this file except in compliance with the License.
64523a761Stbbdev You may obtain a copy of the License at
74523a761Stbbdev
84523a761Stbbdev http://www.apache.org/licenses/LICENSE-2.0
94523a761Stbbdev
104523a761Stbbdev Unless required by applicable law or agreed to in writing, software
114523a761Stbbdev distributed under the License is distributed on an "AS IS" BASIS,
124523a761Stbbdev WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134523a761Stbbdev See the License for the specific language governing permissions and
144523a761Stbbdev limitations under the License.
154523a761Stbbdev */
164523a761Stbbdev
174523a761Stbbdev #include "oneapi/tbb/detail/_utils.h"
184523a761Stbbdev #include "governor.h"
194523a761Stbbdev #include "concurrent_monitor.h"
208827ea7dSLong Nguyen #include "oneapi/tbb/detail/_waitable_atomic.h"
214523a761Stbbdev
224523a761Stbbdev #include <type_traits>
234523a761Stbbdev
244523a761Stbbdev namespace tbb {
254523a761Stbbdev namespace detail {
264523a761Stbbdev namespace r1 {
274523a761Stbbdev
284523a761Stbbdev struct address_context {
294523a761Stbbdev address_context() = default;
304523a761Stbbdev
address_contexttbb::detail::r1::address_context31*5ab8e5f6SVertexwahn address_context(void* address, std::uintptr_t context) :
32*5ab8e5f6SVertexwahn my_address(address), my_context(context)
334523a761Stbbdev {}
344523a761Stbbdev
354523a761Stbbdev void* my_address{nullptr};
36*5ab8e5f6SVertexwahn std::uintptr_t my_context{0};
374523a761Stbbdev };
384523a761Stbbdev
394523a761Stbbdev class address_waiter : public concurrent_monitor_base<address_context> {
404523a761Stbbdev using base_type = concurrent_monitor_base<address_context>;
414523a761Stbbdev public:
424523a761Stbbdev using base_type::base_type;
434523a761Stbbdev /** per-thread descriptor for concurrent_monitor */
444523a761Stbbdev using thread_context = sleep_node<address_context>;
454523a761Stbbdev };
464523a761Stbbdev
474523a761Stbbdev // 1024 is a rough estimate based on two assumptions:
484523a761Stbbdev // 1) there are no more than 1000 threads in the application;
494523a761Stbbdev // 2) the mutexes are optimized for short critical sections less than a couple of microseconds,
504523a761Stbbdev // which is less than 1/1000 of a time slice.
514523a761Stbbdev // In the worst case, we have single mutex that is locked and its thread is preempted.
524523a761Stbbdev // Therefore, the probability of a collision while taking unrelated mutex is about 1/size of a table.
534523a761Stbbdev static constexpr std::size_t num_address_waiters = 2 << 10;
544523a761Stbbdev static_assert(std::is_standard_layout<address_waiter>::value,
554523a761Stbbdev "address_waiter must be with standard layout");
564523a761Stbbdev static address_waiter address_waiter_table[num_address_waiters];
574523a761Stbbdev
clear_address_waiter_table()584523a761Stbbdev void clear_address_waiter_table() {
594523a761Stbbdev for (std::size_t i = 0; i < num_address_waiters; ++i) {
604523a761Stbbdev address_waiter_table[i].destroy();
614523a761Stbbdev }
624523a761Stbbdev }
634523a761Stbbdev
get_address_waiter(void * address)644523a761Stbbdev static address_waiter& get_address_waiter(void* address) {
654523a761Stbbdev std::uintptr_t tag = std::uintptr_t(address);
664523a761Stbbdev return address_waiter_table[((tag >> 5) ^ tag) % num_address_waiters];
674523a761Stbbdev }
684523a761Stbbdev
wait_on_address(void * address,d1::delegate_base & predicate,std::uintptr_t context)694523a761Stbbdev void wait_on_address(void* address, d1::delegate_base& predicate, std::uintptr_t context) {
704523a761Stbbdev address_waiter& waiter = get_address_waiter(address);
714523a761Stbbdev waiter.wait<address_waiter::thread_context>(predicate, address_context{address, context});
724523a761Stbbdev }
734523a761Stbbdev
notify_by_address(void * address,std::uintptr_t target_context)744523a761Stbbdev void notify_by_address(void* address, std::uintptr_t target_context) {
754523a761Stbbdev address_waiter& waiter = get_address_waiter(address);
764523a761Stbbdev
774523a761Stbbdev auto predicate = [address, target_context] (address_context ctx) {
78*5ab8e5f6SVertexwahn return ctx.my_address == address && ctx.my_context == target_context;
794523a761Stbbdev };
804523a761Stbbdev
814523a761Stbbdev waiter.notify_relaxed(predicate);
824523a761Stbbdev }
834523a761Stbbdev
notify_by_address_one(void * address)844523a761Stbbdev void notify_by_address_one(void* address) {
854523a761Stbbdev address_waiter& waiter = get_address_waiter(address);
864523a761Stbbdev
874523a761Stbbdev auto predicate = [address] (address_context ctx) {
884523a761Stbbdev return ctx.my_address == address;
894523a761Stbbdev };
904523a761Stbbdev
914523a761Stbbdev waiter.notify_one_relaxed(predicate);
924523a761Stbbdev }
934523a761Stbbdev
notify_by_address_all(void * address)944523a761Stbbdev void notify_by_address_all(void* address) {
954523a761Stbbdev address_waiter& waiter = get_address_waiter(address);
964523a761Stbbdev
974523a761Stbbdev auto predicate = [address] (address_context ctx) {
984523a761Stbbdev return ctx.my_address == address;
994523a761Stbbdev };
1004523a761Stbbdev
1014523a761Stbbdev waiter.notify_relaxed(predicate);
1024523a761Stbbdev }
1034523a761Stbbdev
1044523a761Stbbdev } // namespace r1
1054523a761Stbbdev } // namespace detail
1064523a761Stbbdev } // namespace tbb
107