#pragma region CPL License /* Nuclex Native Framework Copyright (C) 2002-2023 Nuclex Development Labs This library is free software; you can redistribute it and/or modify it under the terms of the IBM Common Public License as published by the IBM Corporation; either version 1.0 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the IBM Common Public License for more details. You should have received a copy of the IBM Common Public License along with this library */ #pragma endregion // CPL License #ifndef NUCLEX_SUPPORT_PLATFORM_WINDOWSSYNCAPI_H #define NUCLEX_SUPPORT_PLATFORM_WINDOWSSYNCAPI_H #include "Nuclex/Support/Config.h" #if defined(NUCLEX_SUPPORT_WINDOWS) #include "WindowsApi.h" #include // for std::chrono::milliseconds namespace Nuclex { namespace Support { namespace Platform { // ------------------------------------------------------------------------------------------- // /// Wraps the API used for advanced thread synchronization on Windows class WindowsSyncApi { #pragma region enum WaitResult /// Reasons for why has returned public: enum WaitResult { /// The wait was cancelled because the timeout was reached> TimedOut = -1, /// Either the monitored value changed or we woke spuriously ValueChanged = 1 }; #pragma endregion // enum WaitResult /// Waits for the specified wait variable to change in memory /// /// Type of the wait variable, must be either int8, int16, int32 or int64 /// /// Wait variable that will be watched /// Value the wait variable will be compared against /// Maximum time to wait in milliseconds /// /// Is set to true if the wait was cancelled due to reaching its timeout /// /// /// True if the variable has probably changed, false if the variable remained /// unchanged until the wait timeout was reached /// /// /// /// There can be spurious wake-ups where the variable did not change its value /// but some other unpredictable event (including false sharing) causes /// this method to return. /// /// /// If you have several variables to wait on, false sharing will result in a lot /// of spurious wake-ups. To minimize spurious wake-ups in this specific case, /// interleave the wait variables with other data (if possible) or pad the wait /// variables so they each have at least a size of /// std::hardware_constructive_interference_size. C++17 developers /// can make use of alignas() for individual variables (but do take /// care of neighboring variables if you can) /// /// public: template inline static WaitResult WaitOnAddress( const volatile TWaitVariable &waitVariable, TWaitVariable comparedValue, std::chrono::milliseconds patience ); /// Waits for the specified wait variable to change in memory /// /// Type of the wait variable, must be either int8, int16, int32 or int64 /// /// Wait variable that will be watched /// Value the wait variable will be compared against /// /// True if the variable has probably changed, false if the wait was interrupted /// /// /// /// There can be spurious wake-ups where the variable did not change its value /// but some other unpredictable event (including false sharing) causes /// this method to return. /// /// /// If you have several variables to wait on, false sharing will result in a lot /// of spurious wake-ups. To minimize spurious wake-ups in this specific case, /// interleave the wait variables with other data (if possible) or pad the wait /// variables so they each have at least a size of /// std::hardware_constructive_interference_size. C++17 developers /// can make use of alignas() for individual variables (but do take /// care of neighboring variables if you can) /// /// public: template inline static WaitResult WaitOnAddress( const volatile TWaitVariable &waitVariable, TWaitVariable comparedValue ); /// Wakes a single threads waiting for a value in memory to change /// /// Type of the wait variable, must be either int8, int16, int32 or int64 /// /// /// Variable for which one watching observer will be waken up /// public: template inline static void WakeByAddressSingle( const volatile TWaitVariable &waitVariable ); /// Wakes all threads waiting for a value in memory to change /// /// Type of the wait variable, must be either int8, int16, int32 or int64 /// /// /// Variable for which all watching observers will be waken up /// public: template inline static void WakeByAddressAll( const volatile TWaitVariable &waitVariable ); /// Waits for a value to change in memory /// Wait variable that will be watched /// Value the wait variable will be compared against /// Size of the wait variable in bytes /// Maximum time to wait in milliseconds /// /// True if the variable has probably changed, false if the variable remained /// unchanged until the wait timeout was reached /// private: static WaitResult waitOnAddressWithTimeout( const volatile void *waitVariableAddress, void *comparisonValue, std::size_t waitVariableByteCount, std::chrono::milliseconds patience ); /// Waits for a value to change in memory /// Memory address of the wait variable /// /// Memory address holding the value the wait variable will be compared against /// /// Size of the wait variable in bytes /// /// True if the variable has probably changed, false if the wait was interrupted /// private: static WaitResult waitOnAddressNoTimeout( const volatile void *waitVariableAddress, void *comparisonValue, std::size_t waitVariableByteCount ); /// Wakes a single thread waiting for a value in memory to change /// /// Memory address of the value for which one observer will be waken up /// private: static void wakeByAddressSingle(const volatile void *waitVariableAddress); /// Wakes all threads waiting for a value in memory to change /// /// Memory address of the value for which any observers will be waken up /// private: static void wakeByAddressAll(const volatile void *waitVariableAddress); }; // ------------------------------------------------------------------------------------------- // template<> inline WindowsSyncApi::WaitResult WindowsSyncApi::WaitOnAddress( const volatile std::uint8_t &waitVariable, std::uint8_t comparedValue, std::chrono::milliseconds patience ) { return waitOnAddressWithTimeout( &waitVariable, &comparedValue, sizeof(std::uint8_t), patience ); } // ------------------------------------------------------------------------------------------- // template<> inline WindowsSyncApi::WaitResult WindowsSyncApi::WaitOnAddress( const volatile std::uint16_t &waitVariable, std::uint16_t comparedValue, std::chrono::milliseconds patience ) { return waitOnAddressWithTimeout( &waitVariable, &comparedValue, sizeof(std::uint16_t), patience ); } // ------------------------------------------------------------------------------------------- // template<> inline WindowsSyncApi::WaitResult WindowsSyncApi::WaitOnAddress( const volatile std::uint32_t &waitVariable, std::uint32_t comparedValue, std::chrono::milliseconds patience ) { return waitOnAddressWithTimeout( &waitVariable, &comparedValue, sizeof(std::uint32_t), patience ); } // ------------------------------------------------------------------------------------------- // template<> inline WindowsSyncApi::WaitResult WindowsSyncApi::WaitOnAddress( const volatile std::uint64_t &waitVariable, std::uint64_t comparedValue, std::chrono::milliseconds patience ) { return waitOnAddressWithTimeout( &waitVariable, &comparedValue, sizeof(std::uint64_t), patience ); } // ------------------------------------------------------------------------------------------- // template<> inline WindowsSyncApi::WaitResult WindowsSyncApi::WaitOnAddress( const volatile std::uint8_t &waitVariable, std::uint8_t comparedValue ) { return waitOnAddressNoTimeout( &waitVariable, &comparedValue, sizeof(std::uint8_t) ); } // ------------------------------------------------------------------------------------------- // template<> inline WindowsSyncApi::WaitResult WindowsSyncApi::WaitOnAddress( const volatile std::uint16_t &waitVariable, std::uint16_t comparedValue ) { return waitOnAddressNoTimeout( &waitVariable, &comparedValue, sizeof(std::uint16_t) ); } // ------------------------------------------------------------------------------------------- // template<> inline WindowsSyncApi::WaitResult WindowsSyncApi::WaitOnAddress( const volatile std::uint32_t &waitVariable, std::uint32_t comparedValue ) { return waitOnAddressNoTimeout( &waitVariable, &comparedValue, sizeof(std::uint32_t) ); } // ------------------------------------------------------------------------------------------- // template<> inline WindowsSyncApi::WaitResult WindowsSyncApi::WaitOnAddress( const volatile std::uint64_t &waitVariable, std::uint64_t comparedValue ) { return waitOnAddressNoTimeout( &waitVariable, &comparedValue, sizeof(std::uint64_t) ); } // ------------------------------------------------------------------------------------------- // template<> inline void WindowsSyncApi::WakeByAddressSingle(const volatile std::uint8_t &waitVariable) { wakeByAddressSingle(&waitVariable); } // ------------------------------------------------------------------------------------------- // template<> inline void WindowsSyncApi::WakeByAddressSingle(const volatile std::uint16_t &waitVariable) { wakeByAddressSingle(&waitVariable); } // ------------------------------------------------------------------------------------------- // template<> inline void WindowsSyncApi::WakeByAddressSingle(const volatile std::uint32_t &waitVariable) { wakeByAddressSingle(&waitVariable); } // ------------------------------------------------------------------------------------------- // template<> inline void WindowsSyncApi::WakeByAddressSingle(const volatile std::uint64_t &waitVariable) { wakeByAddressSingle(&waitVariable); } // ------------------------------------------------------------------------------------------- // template<> inline void WindowsSyncApi::WakeByAddressAll(const volatile std::uint8_t &waitVariable) { wakeByAddressAll(&waitVariable); } // ------------------------------------------------------------------------------------------- // template<> inline void WindowsSyncApi::WakeByAddressAll(const volatile std::uint16_t &waitVariable) { wakeByAddressAll(&waitVariable); } // ------------------------------------------------------------------------------------------- // template<> inline void WindowsSyncApi::WakeByAddressAll(const volatile std::uint32_t &waitVariable) { wakeByAddressAll(&waitVariable); } // ------------------------------------------------------------------------------------------- // template<> inline void WindowsSyncApi::WakeByAddressAll(const volatile std::uint64_t &waitVariable) { wakeByAddressAll(&waitVariable); } // ------------------------------------------------------------------------------------------- // }}} // namespace Nuclex::Support::Platform #endif // defined(NUCLEX_SUPPORT_WINDOWS) #endif // NUCLEX_SUPPORT_PLATFORM_WINDOWSSYNCAPI_H