1 /* 2 Copyright (c) 2005-2021 Intel Corporation 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 #ifndef __TBB_monitor_mutex_H 18 #define __TBB_monitor_mutex_H 19 20 #include "oneapi/tbb/detail/_utils.h" 21 #include "oneapi/tbb/detail/_aligned_space.h" 22 #include "semaphore.h" 23 24 #include <mutex> 25 26 namespace tbb { 27 namespace detail { 28 namespace r1 { 29 30 class concurrent_monitor_mutex { 31 public: 32 using scoped_lock = std::lock_guard<concurrent_monitor_mutex>; 33 concurrent_monitor_mutex()34 constexpr concurrent_monitor_mutex() {} 35 36 ~concurrent_monitor_mutex() = default; 37 destroy()38 void destroy() { 39 #if !__TBB_USE_FUTEX 40 if (my_init_flag.load(std::memory_order_relaxed)) { 41 get_semaphore().~semaphore(); 42 } 43 #endif 44 } 45 lock()46 void lock() { 47 auto wakeup_condition = [&] { 48 return my_flag.load(std::memory_order_relaxed) == 0; 49 }; 50 51 while (my_flag.exchange(1)) { 52 if (!timed_spin_wait_until(wakeup_condition)) { 53 ++my_waiters; 54 while (!wakeup_condition()) { 55 wait(); 56 } 57 --my_waiters; 58 } 59 } 60 } 61 unlock()62 void unlock() { 63 my_flag.exchange(0); // full fence, so the next load is relaxed 64 if (my_waiters.load(std::memory_order_relaxed)) { 65 wakeup(); 66 } 67 } 68 69 private: wait()70 void wait() { 71 #if __TBB_USE_FUTEX 72 futex_wait(&my_flag, 1); 73 #else 74 get_semaphore().P(); 75 #endif 76 } 77 wakeup()78 void wakeup() { 79 #if __TBB_USE_FUTEX 80 futex_wakeup_one(&my_flag); 81 #else 82 get_semaphore().V(); 83 #endif 84 } 85 86 // The flag should be int for the futex operations 87 std::atomic<int> my_flag{0}; 88 std::atomic<int> my_waiters{0}; 89 90 #if !__TBB_USE_FUTEX get_semaphore()91 semaphore& get_semaphore() { 92 if (!my_init_flag.load(std::memory_order_acquire)) { 93 std::lock_guard<std::mutex> lock(my_init_mutex); 94 if (!my_init_flag.load(std::memory_order_relaxed)) { 95 new (my_semaphore.begin()) semaphore(); 96 my_init_flag.store(true, std::memory_order_release); 97 } 98 } 99 100 return *my_semaphore.begin(); 101 } 102 103 static std::mutex my_init_mutex; 104 std::atomic<bool> my_init_flag{false}; 105 aligned_space<semaphore> my_semaphore{}; 106 #endif 107 }; 108 109 } // namespace r1 110 } // namespace detail 111 } // namespace tbb 112 113 #endif // __TBB_monitor_mutex_H 114