diff --git a/OpenConsole.sln b/OpenConsole.sln index 60c2f7682..843d866cc 100644 --- a/OpenConsole.sln +++ b/OpenConsole.sln @@ -397,6 +397,8 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests_Control", "src\ca EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTerminal.UIA.Tests", "src\cascadia\WindowsTerminal_UIATests\WindowsTerminal.UIA.Tests.csproj", "{F19DACD5-0C6E-40DC-B6E4-767A3200542C}" EndProject +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "api-ms-win-core-synch-l1-2-0", "src\api-ms-win-core-synch-l1-2-0\api-ms-win-core-synch-l1-2-0.vcxproj", "{9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution AuditMode|Any CPU = AuditMode|Any CPU @@ -3292,6 +3294,50 @@ Global {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x64.Build.0 = Release|x64 {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.ActiveCfg = Release|Win32 {F19DACD5-0C6E-40DC-B6E4-767A3200542C}.Release|x86.Build.0 = Release|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|Any CPU.ActiveCfg = AuditMode|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|ARM.ActiveCfg = AuditMode|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|ARM64.ActiveCfg = AuditMode|ARM64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|ARM64.Build.0 = AuditMode|ARM64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|DotNet_x64Test.ActiveCfg = AuditMode|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|DotNet_x86Test.ActiveCfg = AuditMode|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|x64.ActiveCfg = AuditMode|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|x64.Build.0 = AuditMode|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|x86.ActiveCfg = AuditMode|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.AuditMode|x86.Build.0 = AuditMode|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|Any CPU.ActiveCfg = Debug|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|ARM.ActiveCfg = Debug|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|ARM64.ActiveCfg = Debug|ARM64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|ARM64.Build.0 = Debug|ARM64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|DotNet_x64Test.ActiveCfg = Debug|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|DotNet_x64Test.Build.0 = Debug|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|DotNet_x86Test.ActiveCfg = Debug|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|DotNet_x86Test.Build.0 = Debug|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|x64.ActiveCfg = Debug|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|x64.Build.0 = Debug|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|x86.ActiveCfg = Debug|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Debug|x86.Build.0 = Debug|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|Any CPU.ActiveCfg = Fuzzing|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|ARM.ActiveCfg = Fuzzing|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|ARM64.ActiveCfg = Fuzzing|ARM64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|ARM64.Build.0 = Fuzzing|ARM64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|DotNet_x64Test.ActiveCfg = Fuzzing|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|DotNet_x86Test.ActiveCfg = Fuzzing|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x64.ActiveCfg = Fuzzing|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x64.Build.0 = Fuzzing|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x86.ActiveCfg = Fuzzing|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Fuzzing|x86.Build.0 = Fuzzing|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|Any CPU.ActiveCfg = Release|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|ARM.ActiveCfg = Release|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|ARM64.ActiveCfg = Release|ARM64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|ARM64.Build.0 = Release|ARM64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|DotNet_x64Test.ActiveCfg = Release|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|DotNet_x64Test.Build.0 = Release|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|DotNet_x86Test.ActiveCfg = Release|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|DotNet_x86Test.Build.0 = Release|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x64.ActiveCfg = Release|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x64.Build.0 = Release|x64 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x86.ActiveCfg = Release|Win32 + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5}.Release|x86.Build.0 = Release|Win32 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -3390,6 +3436,7 @@ Global {05D9052F-D78F-478F-968A-2DE38A6DB996} = {E8F24881-5E37-4362-B191-A3BA0ED7F4EB} {C323DAEE-B307-4C7B-ACE5-7293CBEFCB5B} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} {F19DACD5-0C6E-40DC-B6E4-767A3200542C} = {BDB237B6-1D1D-400F-84CC-40A58FA59C8E} + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5} = {89CDCC5C-9F53-4054-97A4-639D99F169CD} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3140B1B7-C8EE-43D1-A772-D82A7061A271} diff --git a/src/api-ms-win-core-synch-l1-2-0/api-ms-win-core-synch-l1-2-0.vcxproj b/src/api-ms-win-core-synch-l1-2-0/api-ms-win-core-synch-l1-2-0.vcxproj new file mode 100644 index 000000000..54a0c0e79 --- /dev/null +++ b/src/api-ms-win-core-synch-l1-2-0/api-ms-win-core-synch-l1-2-0.vcxproj @@ -0,0 +1,27 @@ + + + + 16.0 + Win32Proj + {9cf74355-f018-4c19-81ad-9dc6b7f2c6f5} + apimswincoresynchl120 + DynamicLibrary + + + + + + + + + + + + NotUsing + + + kernel32.lib + definitions.def + + + \ No newline at end of file diff --git a/src/api-ms-win-core-synch-l1-2-0/api-ms-win-core-synch-l1-2-0.vcxproj.filters b/src/api-ms-win-core-synch-l1-2-0/api-ms-win-core-synch-l1-2-0.vcxproj.filters new file mode 100644 index 000000000..163db3010 --- /dev/null +++ b/src/api-ms-win-core-synch-l1-2-0/api-ms-win-core-synch-l1-2-0.vcxproj.filters @@ -0,0 +1,30 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + + + + + + Source Files + + + \ No newline at end of file diff --git a/src/api-ms-win-core-synch-l1-2-0/definitions.def b/src/api-ms-win-core-synch-l1-2-0/definitions.def new file mode 100644 index 000000000..5eae42b4d --- /dev/null +++ b/src/api-ms-win-core-synch-l1-2-0/definitions.def @@ -0,0 +1,19 @@ +LIBRARY +EXPORTS + DeleteSynchronizationBarrier = kernel32.DeleteSynchronizationBarrier + EnterSynchronizationBarrier = kernel32.EnterSynchronizationBarrier + InitOnceBeginInitialize = kernel32.InitOnceBeginInitialize + InitOnceComplete = kernel32.InitOnceComplete + InitOnceExecuteOnce = kernel32.InitOnceExecuteOnce + InitOnceInitialize = kernel32.InitOnceInitialize + InitializeConditionVariable = kernel32.InitializeConditionVariable + InitializeSynchronizationBarrier = kernel32.InitializeSynchronizationBarrier + SignalObjectAndWait = kernel32.SignalObjectAndWait + Sleep = kernel32.Sleep + SleepConditionVariableCS = kernel32.SleepConditionVariableCS + SleepConditionVariableSRW = kernel32.SleepConditionVariableSRW + WaitOnAddress + WakeAllConditionVariable = kernel32.WakeAllConditionVariable + WakeByAddressAll + WakeByAddressSingle + WakeConditionVariable = kernel32.WakeConditionVariable diff --git a/src/api-ms-win-core-synch-l1-2-0/main.cpp b/src/api-ms-win-core-synch-l1-2-0/main.cpp new file mode 100644 index 000000000..7bbc50ac7 --- /dev/null +++ b/src/api-ms-win-core-synch-l1-2-0/main.cpp @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT license. + +// +// The code in this file was adapted from the STL on the 2021-07-05. Commit: +// https://github.com/microsoft/STL/blob/e745bad3b1d05b5b19ec652d68abb37865ffa454/stl/src/atomic_wait.cpp +// +// It backports the following Windows 8 functions to Windows 7: +// * WaitOnAddress +// * WakeByAddressSingle +// * WakeByAddressAll +// + +#include +#include + +#include +#define _WIN32_WINNT 0x0601 +#include + +#define WIN32_LEAN_AND_MEAN +#define NOMINMAX +#include +#include + +namespace +{ + class [[nodiscard]] SRWLockGuard + { + public: + explicit SRWLockGuard(SRWLOCK & lock) noexcept : + _lock(&lock) + { + AcquireSRWLockExclusive(_lock); + } + + ~SRWLockGuard() + { + ReleaseSRWLockExclusive(_lock); + } + + SRWLockGuard(const SRWLockGuard&) = delete; + SRWLockGuard& operator=(const SRWLockGuard&) = delete; + + SRWLockGuard(SRWLockGuard &&) = delete; + SRWLockGuard& operator=(SRWLockGuard&&) = delete; + + private: + SRWLOCK* _lock; + }; + + struct WaitContext + { + const volatile void* address; + WaitContext* next; + WaitContext* prev; + CONDITION_VARIABLE cv; + }; + + struct [[nodiscard]] GuardedWaitContext : WaitContext + { + GuardedWaitContext(const volatile void* storage, WaitContext* head) noexcept : + WaitContext{ storage, head, head->prev, CONDITION_VARIABLE_INIT } + { + prev->next = this; + next->prev = this; + } + + ~GuardedWaitContext() + { + const auto n = next; + const auto p = prev; + next->prev = p; + prev->next = n; + } + + GuardedWaitContext(const GuardedWaitContext&) = delete; + GuardedWaitContext& operator=(const GuardedWaitContext&) = delete; + + GuardedWaitContext(GuardedWaitContext &&) = delete; + GuardedWaitContext& operator=(GuardedWaitContext&&) = delete; + }; + +#pragma warning(push) +#pragma warning(disable : 4324) // structure was padded due to alignment specifier + struct alignas(std::hardware_destructive_interference_size) WaitTableEntry + { + SRWLOCK lock = SRWLOCK_INIT; + WaitContext head = { nullptr, &head, &head, CONDITION_VARIABLE_INIT }; + }; +#pragma warning(pop) + + [[nodiscard]] WaitTableEntry& GetWaitTableEntry(const volatile void* const storage) noexcept + { + // A prime number for the hash table size was chosen to prevent collisions. + constexpr size_t size = 251; + constexpr std::hash hasher; + + static WaitTableEntry table[size]; +#pragma warning(suppress : 26446) // Prefer to use gsl::at() instead of unchecked subscript operator +#pragma warning(suppress : 26482) // Only index into arrays using constant expressions +#pragma warning(suppress : 26490) // Don't use reinterpret_cast + return table[hasher(reinterpret_cast(storage)) % size]; + } + +#pragma warning(suppress : 26429) // Symbol 'comparand' is never tested for nullness, it can be marked as not_null + bool AreEqual(const volatile void* storage, const void* comparand, size_t size) noexcept + { + switch (size) + { + case 1: + return __iso_volatile_load8(static_cast(storage)) == *static_cast(comparand); + case 2: + return __iso_volatile_load16(static_cast(storage)) == *static_cast(comparand); + case 4: + return __iso_volatile_load32(static_cast(storage)) == *static_cast(comparand); + case 8: + return __iso_volatile_load64(static_cast(storage)) == *static_cast(comparand); + default: + abort(); + } + } +} // unnamed namespace + +extern "C" BOOL WINAPI WaitOnAddress(_In_reads_bytes_(AddressSize) volatile VOID* Address, _In_reads_bytes_(AddressSize) PVOID CompareAddress, _In_ SIZE_T AddressSize, _In_opt_ DWORD dwMilliseconds) +{ + auto& entry = GetWaitTableEntry(Address); + + SRWLockGuard guard{ entry.lock }; + GuardedWaitContext context{ Address, &entry.head }; + + for (;;) + { + // NOTE: under lock to prevent lost wakes + if (!AreEqual(Address, CompareAddress, AddressSize)) + { + return TRUE; + } + + if (!SleepConditionVariableSRW(&context.cv, &entry.lock, dwMilliseconds, 0)) + { +#ifndef NDEBUG + if (GetLastError() != ERROR_TIMEOUT) + { + abort(); + } +#endif + return FALSE; + } + + if (dwMilliseconds != INFINITE) + { + // spurious wake to recheck the clock + return TRUE; + } + } +} + +extern "C" VOID WINAPI WakeByAddressSingle(_In_ PVOID Address) +{ + auto& entry = GetWaitTableEntry(Address); + SRWLockGuard guard(entry.lock); + + for (auto context = entry.head.next; context != &entry.head; context = context->next) + { + if (context->address == Address) + { + // Can't move wake outside SRWLOCKed section: SRWLOCK also protects the context itself + WakeAllConditionVariable(&context->cv); + // This break; is the difference between WakeByAddressSingle and WakeByAddressAll + break; + } + } +} + +extern "C" VOID WINAPI WakeByAddressAll(_In_ PVOID Address) +{ + auto& entry = GetWaitTableEntry(Address); + SRWLockGuard guard(entry.lock); + + for (auto context = entry.head.next; context != &entry.head; context = context->next) + { + if (context->address == Address) + { + // Can't move wake outside SRWLOCKed section: SRWLOCK also protects the context itself + WakeAllConditionVariable(&context->cv); + } + } +} diff --git a/src/cascadia/PublicTerminalCore/PublicTerminalCore.vcxproj b/src/cascadia/PublicTerminalCore/PublicTerminalCore.vcxproj index c1507c070..1665034e1 100644 --- a/src/cascadia/PublicTerminalCore/PublicTerminalCore.vcxproj +++ b/src/cascadia/PublicTerminalCore/PublicTerminalCore.vcxproj @@ -8,7 +8,6 @@ PublicTerminalCore DynamicLibrary - @@ -21,27 +20,24 @@ - + {1cf55140-ef6a-4736-a403-957e4f7430bb} - + {ca5cad1a-abcd-429c-b551-8562ec954746} - + {18D09A24-8240-42D6-8CB6-236EEE820263} - - {0cf235bd-2da0-407e-90ee-c467e8bbc714} - - + {af0a096a-8b3a-4949-81ef-7df8f0fee91f} - - {3ae13314-1939-4dfa-9c14-38ca0834050c} - - + {48d21369-3d7b-4431-9967-24e81292cf62} + + {9CF74355-F018-4C19-81AD-9DC6B7F2C6F5} +