xref: /oneTBB/src/tbb/address_waiter.cpp (revision 5ab8e5f6)
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