Added ntdll-unaligned-futex patchset.

This commit is contained in:
Paul Gofman 2020-03-02 10:46:01 +03:00
parent afdf5020ff
commit 59fe96c14c
5 changed files with 1162 additions and 1 deletions

View File

@ -0,0 +1,437 @@
From f48aef32395dee75fff9430fd788262fde5f1e8d Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Mon, 30 Dec 2019 17:17:47 -0600
Subject: [PATCH 1/3] ntdll: Handle unaligned condition variables when using
futexes.
Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=48389
---
dlls/kernel32/tests/sync.c | 76 ++++++++++++++++-----------
dlls/ntdll/sync.c | 105 ++++++++++++++++++++++++++++++-------
2 files changed, 132 insertions(+), 49 deletions(-)
diff --git a/dlls/kernel32/tests/sync.c b/dlls/kernel32/tests/sync.c
index 9e32312e4d..fb93dfdc53 100644
--- a/dlls/kernel32/tests/sync.c
+++ b/dlls/kernel32/tests/sync.c
@@ -2094,10 +2094,18 @@ static void test_condvars_consumer_producer(void)
/* Sample test for some sequence of events happening, sequenced using "condvar_seq" */
static DWORD condvar_seq = 0;
-static CONDITION_VARIABLE condvar_base = CONDITION_VARIABLE_INIT;
+static CONDITION_VARIABLE aligned_cv;
static CRITICAL_SECTION condvar_crit;
static SRWLOCK condvar_srwlock;
+#include "pshpack1.h"
+static struct
+{
+ char c;
+ CONDITION_VARIABLE cv;
+} unaligned_cv;
+#include "poppack.h"
+
/* Sequence of wake/sleep to check boundary conditions:
* 0: init
* 1: producer emits a WakeConditionVariable without consumer waiting.
@@ -2117,28 +2125,31 @@ static SRWLOCK condvar_srwlock;
* 12: producer (shared) wakes up consumer (shared)
* 13: end
*/
-static DWORD WINAPI condvar_base_producer(LPVOID x) {
+static DWORD WINAPI condvar_base_producer(void *arg)
+{
+ CONDITION_VARIABLE *cv = arg;
+
while (condvar_seq < 1) Sleep(1);
- pWakeConditionVariable (&condvar_base);
+ pWakeConditionVariable(cv);
condvar_seq = 2;
while (condvar_seq < 3) Sleep(1);
- pWakeAllConditionVariable (&condvar_base);
+ pWakeAllConditionVariable(cv);
condvar_seq = 4;
while (condvar_seq < 5) Sleep(1);
EnterCriticalSection (&condvar_crit);
- pWakeConditionVariable (&condvar_base);
+ pWakeConditionVariable(cv);
LeaveCriticalSection (&condvar_crit);
while (condvar_seq < 6) Sleep(1);
EnterCriticalSection (&condvar_crit);
- pWakeAllConditionVariable (&condvar_base);
+ pWakeAllConditionVariable(cv);
LeaveCriticalSection (&condvar_crit);
while (condvar_seq < 8) Sleep(1);
EnterCriticalSection (&condvar_crit);
- pWakeConditionVariable (&condvar_base);
+ pWakeConditionVariable(cv);
Sleep(50);
LeaveCriticalSection (&condvar_crit);
@@ -2148,36 +2159,38 @@ static DWORD WINAPI condvar_base_producer(LPVOID x) {
while (condvar_seq < 9) Sleep(1);
pAcquireSRWLockExclusive(&condvar_srwlock);
- pWakeConditionVariable(&condvar_base);
+ pWakeConditionVariable(cv);
pReleaseSRWLockExclusive(&condvar_srwlock);
while (condvar_seq < 10) Sleep(1);
pAcquireSRWLockExclusive(&condvar_srwlock);
- pWakeConditionVariable(&condvar_base);
+ pWakeConditionVariable(cv);
pReleaseSRWLockExclusive(&condvar_srwlock);
while (condvar_seq < 11) Sleep(1);
pAcquireSRWLockShared(&condvar_srwlock);
- pWakeConditionVariable(&condvar_base);
+ pWakeConditionVariable(cv);
pReleaseSRWLockShared(&condvar_srwlock);
while (condvar_seq < 12) Sleep(1);
Sleep(50); /* ensure that consumer waits for cond variable */
pAcquireSRWLockShared(&condvar_srwlock);
- pWakeConditionVariable(&condvar_base);
+ pWakeConditionVariable(cv);
pReleaseSRWLockShared(&condvar_srwlock);
return 0;
}
-static DWORD WINAPI condvar_base_consumer(LPVOID x) {
+static DWORD WINAPI condvar_base_consumer(void *arg)
+{
+ CONDITION_VARIABLE *cv = arg;
BOOL ret;
while (condvar_seq < 2) Sleep(1);
/* wake was emitted, but we were not sleeping */
EnterCriticalSection (&condvar_crit);
- ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 10);
+ ret = pSleepConditionVariableCS(cv, &condvar_crit, 10);
LeaveCriticalSection (&condvar_crit);
ok (!ret, "SleepConditionVariableCS should return FALSE on out of band wake\n");
ok (GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableCS should return ERROR_TIMEOUT on out of band wake, not %d\n", GetLastError());
@@ -2187,33 +2200,33 @@ static DWORD WINAPI condvar_base_consumer(LPVOID x) {
/* wake all was emitted, but we were not sleeping */
EnterCriticalSection (&condvar_crit);
- ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 10);
+ ret = pSleepConditionVariableCS(cv, &condvar_crit, 10);
LeaveCriticalSection (&condvar_crit);
ok (!ret, "SleepConditionVariableCS should return FALSE on out of band wake\n");
ok (GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableCS should return ERROR_TIMEOUT on out of band wake, not %d\n", GetLastError());
EnterCriticalSection (&condvar_crit);
condvar_seq = 5;
- ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 200);
+ ret = pSleepConditionVariableCS(cv, &condvar_crit, 200);
LeaveCriticalSection (&condvar_crit);
ok (ret, "SleepConditionVariableCS should return TRUE on good wake\n");
EnterCriticalSection (&condvar_crit);
condvar_seq = 6;
- ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 200);
+ ret = pSleepConditionVariableCS(cv, &condvar_crit, 200);
LeaveCriticalSection (&condvar_crit);
ok (ret, "SleepConditionVariableCS should return TRUE on good wakeall\n");
condvar_seq = 7;
EnterCriticalSection (&condvar_crit);
- ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 10);
+ ret = pSleepConditionVariableCS(cv, &condvar_crit, 10);
LeaveCriticalSection (&condvar_crit);
ok (!ret, "SleepConditionVariableCS should return FALSE on out of band wake\n");
ok (GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableCS should return ERROR_TIMEOUT on out of band wake, not %d\n", GetLastError());
EnterCriticalSection (&condvar_crit);
condvar_seq = 8;
- ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 20);
+ ret = pSleepConditionVariableCS(cv, &condvar_crit, 20);
LeaveCriticalSection (&condvar_crit);
ok (ret, "SleepConditionVariableCS should still return TRUE on crit unlock delay\n");
@@ -2227,25 +2240,25 @@ static DWORD WINAPI condvar_base_consumer(LPVOID x) {
pAcquireSRWLockExclusive(&condvar_srwlock);
condvar_seq = 9;
- ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 200, 0);
+ ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 200, 0);
pReleaseSRWLockExclusive(&condvar_srwlock);
ok (ret, "pSleepConditionVariableSRW should return TRUE on good wake\n");
pAcquireSRWLockShared(&condvar_srwlock);
condvar_seq = 10;
- ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 200, CONDITION_VARIABLE_LOCKMODE_SHARED);
+ ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 200, CONDITION_VARIABLE_LOCKMODE_SHARED);
pReleaseSRWLockShared(&condvar_srwlock);
ok (ret, "pSleepConditionVariableSRW should return TRUE on good wake\n");
pAcquireSRWLockExclusive(&condvar_srwlock);
condvar_seq = 11;
- ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 200, 0);
+ ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 200, 0);
pReleaseSRWLockExclusive(&condvar_srwlock);
ok (ret, "pSleepConditionVariableSRW should return TRUE on good wake\n");
pAcquireSRWLockShared(&condvar_srwlock);
condvar_seq = 12;
- ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 200, CONDITION_VARIABLE_LOCKMODE_SHARED);
+ ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 200, CONDITION_VARIABLE_LOCKMODE_SHARED);
pReleaseSRWLockShared(&condvar_srwlock);
ok (ret, "pSleepConditionVariableSRW should return TRUE on good wake\n");
@@ -2253,12 +2266,12 @@ static DWORD WINAPI condvar_base_consumer(LPVOID x) {
return 0;
}
-static void test_condvars_base(void) {
+static void test_condvars_base(RTL_CONDITION_VARIABLE *cv)
+{
HANDLE hp, hc;
DWORD dummy;
BOOL ret;
-
if (!pInitializeConditionVariable) {
/* function is not yet in XP, only in newer Windows */
win_skip("no condition variable support.\n");
@@ -2271,7 +2284,7 @@ static void test_condvars_base(void) {
pInitializeSRWLock(&condvar_srwlock);
EnterCriticalSection (&condvar_crit);
- ret = pSleepConditionVariableCS(&condvar_base, &condvar_crit, 10);
+ ret = pSleepConditionVariableCS(cv, &condvar_crit, 10);
LeaveCriticalSection (&condvar_crit);
ok (!ret, "SleepConditionVariableCS should return FALSE on untriggered condvar\n");
@@ -2280,23 +2293,23 @@ static void test_condvars_base(void) {
if (pInitializeSRWLock)
{
pAcquireSRWLockExclusive(&condvar_srwlock);
- ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 10, 0);
+ ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 10, 0);
pReleaseSRWLockExclusive(&condvar_srwlock);
ok(!ret, "SleepConditionVariableSRW should return FALSE on untriggered condvar\n");
ok(GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableSRW should return ERROR_TIMEOUT on untriggered condvar, not %d\n", GetLastError());
pAcquireSRWLockShared(&condvar_srwlock);
- ret = pSleepConditionVariableSRW(&condvar_base, &condvar_srwlock, 10, CONDITION_VARIABLE_LOCKMODE_SHARED);
+ ret = pSleepConditionVariableSRW(cv, &condvar_srwlock, 10, CONDITION_VARIABLE_LOCKMODE_SHARED);
pReleaseSRWLockShared(&condvar_srwlock);
ok(!ret, "SleepConditionVariableSRW should return FALSE on untriggered condvar\n");
ok(GetLastError() == ERROR_TIMEOUT, "SleepConditionVariableSRW should return ERROR_TIMEOUT on untriggered condvar, not %d\n", GetLastError());
}
-
- hp = CreateThread(NULL, 0, condvar_base_producer, NULL, 0, &dummy);
- hc = CreateThread(NULL, 0, condvar_base_consumer, NULL, 0, &dummy);
+ condvar_seq = 0;
+ hp = CreateThread(NULL, 0, condvar_base_producer, cv, 0, &dummy);
+ hc = CreateThread(NULL, 0, condvar_base_consumer, cv, 0, &dummy);
condvar_seq = 1; /* go */
@@ -3183,7 +3196,8 @@ START_TEST(sync)
test_WaitForSingleObject();
test_WaitForMultipleObjects();
test_initonce();
- test_condvars_base();
+ test_condvars_base(&aligned_cv);
+ test_condvars_base(&unaligned_cv.cv);
test_condvars_consumer_producer();
test_srwlock_base();
test_srwlock_example();
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index ac6c85c556..ac324ebbbe 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -117,6 +117,16 @@ static inline int use_futexes(void)
return supported;
}
+static int *get_futex(void **ptr)
+{
+ if (sizeof(void *) == 8)
+ return (int *)((((ULONG_PTR)ptr) + 3) & ~3);
+ else if (!(((ULONG_PTR)ptr) & 3))
+ return (int *)ptr;
+ else
+ return NULL;
+}
+
static void timespec_from_timeout( struct timespec *timespec, const LARGE_INTEGER *timeout )
{
LARGE_INTEGER now;
@@ -2236,32 +2246,83 @@ BOOLEAN WINAPI RtlTryAcquireSRWLockShared( RTL_SRWLOCK *lock )
}
#ifdef __linux__
-static NTSTATUS fast_wait_cv( RTL_CONDITION_VARIABLE *variable, int val, const LARGE_INTEGER *timeout )
+static NTSTATUS fast_wait_cv( int *futex, int val, const LARGE_INTEGER *timeout )
{
struct timespec timespec;
int ret;
- if (!use_futexes())
- return STATUS_NOT_IMPLEMENTED;
-
if (timeout && timeout->QuadPart != TIMEOUT_INFINITE)
{
timespec_from_timeout( &timespec, timeout );
- ret = futex_wait( (int *)&variable->Ptr, val, &timespec );
+ ret = futex_wait( futex, val, &timespec );
}
else
- ret = futex_wait( (int *)&variable->Ptr, val, NULL );
+ ret = futex_wait( futex, val, NULL );
if (ret == -1 && errno == ETIMEDOUT)
return STATUS_TIMEOUT;
return STATUS_WAIT_0;
}
+static NTSTATUS fast_sleep_cs_cv( RTL_CONDITION_VARIABLE *variable,
+ RTL_CRITICAL_SECTION *cs, const LARGE_INTEGER *timeout )
+{
+ NTSTATUS status;
+ int val, *futex;
+
+ if (!use_futexes())
+ return STATUS_NOT_IMPLEMENTED;
+
+ if (!(futex = get_futex( &variable->Ptr )))
+ return STATUS_NOT_IMPLEMENTED;
+
+ val = *futex;
+
+ RtlLeaveCriticalSection( cs );
+ status = fast_wait_cv( futex, val, timeout );
+ RtlEnterCriticalSection( cs );
+ return status;
+}
+
+static NTSTATUS fast_sleep_srw_cv( RTL_CONDITION_VARIABLE *variable,
+ RTL_SRWLOCK *lock, const LARGE_INTEGER *timeout, ULONG flags )
+{
+ NTSTATUS status;
+ int val, *futex;
+
+ if (!use_futexes())
+ return STATUS_NOT_IMPLEMENTED;
+
+ if (!(futex = get_futex( &variable->Ptr )))
+ return STATUS_NOT_IMPLEMENTED;
+
+ val = *futex;
+
+ if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
+ RtlReleaseSRWLockShared( lock );
+ else
+ RtlReleaseSRWLockExclusive( lock );
+
+ status = fast_wait_cv( futex, val, timeout );
+
+ if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
+ RtlAcquireSRWLockShared( lock );
+ else
+ RtlAcquireSRWLockExclusive( lock );
+ return status;
+}
+
static NTSTATUS fast_wake_cv( RTL_CONDITION_VARIABLE *variable, int count )
{
+ int *futex;
+
if (!use_futexes()) return STATUS_NOT_IMPLEMENTED;
- futex_wake( (int *)&variable->Ptr, count );
+ if (!(futex = get_futex( &variable->Ptr )))
+ return STATUS_NOT_IMPLEMENTED;
+
+ interlocked_xchg_add( futex, 1 );
+ futex_wake( futex, count );
return STATUS_SUCCESS;
}
#else
@@ -2309,9 +2370,11 @@ void WINAPI RtlInitializeConditionVariable( RTL_CONDITION_VARIABLE *variable )
*/
void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable )
{
- interlocked_xchg_add( (int *)&variable->Ptr, 1 );
if (fast_wake_cv( variable, 1 ) == STATUS_NOT_IMPLEMENTED)
+ {
+ interlocked_xchg_add( (int *)&variable->Ptr, 1 );
RtlWakeAddressSingle( variable );
+ }
}
/***********************************************************************
@@ -2321,9 +2384,11 @@ void WINAPI RtlWakeConditionVariable( RTL_CONDITION_VARIABLE *variable )
*/
void WINAPI RtlWakeAllConditionVariable( RTL_CONDITION_VARIABLE *variable )
{
- interlocked_xchg_add( (int *)&variable->Ptr, 1 );
if (fast_wake_cv( variable, INT_MAX ) == STATUS_NOT_IMPLEMENTED)
+ {
+ interlocked_xchg_add( (int *)&variable->Ptr, 1 );
RtlWakeAddressAll( variable );
+ }
}
/***********************************************************************
@@ -2345,15 +2410,15 @@ NTSTATUS WINAPI RtlSleepConditionVariableCS( RTL_CONDITION_VARIABLE *variable, R
const LARGE_INTEGER *timeout )
{
NTSTATUS status;
- int val = *(int *)&variable->Ptr;
-
- RtlLeaveCriticalSection( crit );
+ int val;
- if ((status = fast_wait_cv( variable, val, timeout )) == STATUS_NOT_IMPLEMENTED)
- status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
+ if ((status = fast_sleep_cs_cv( variable, crit, timeout )) != STATUS_NOT_IMPLEMENTED)
+ return status;
+ val = *(int *)&variable->Ptr;
+ RtlLeaveCriticalSection( crit );
+ status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
RtlEnterCriticalSection( crit );
-
return status;
}
@@ -2380,15 +2445,19 @@ NTSTATUS WINAPI RtlSleepConditionVariableSRW( RTL_CONDITION_VARIABLE *variable,
const LARGE_INTEGER *timeout, ULONG flags )
{
NTSTATUS status;
- int val = *(int *)&variable->Ptr;
+ int val;
+
+ if ((status = fast_sleep_srw_cv( variable, lock, timeout, flags )) != STATUS_NOT_IMPLEMENTED)
+ return status;
+
+ val = *(int *)&variable->Ptr;
if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
RtlReleaseSRWLockShared( lock );
else
RtlReleaseSRWLockExclusive( lock );
- if ((status = fast_wait_cv( variable, val, timeout )) == STATUS_NOT_IMPLEMENTED)
- status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
+ status = RtlWaitOnAddress( &variable->Ptr, &val, sizeof(int), timeout );
if (flags & RTL_CONDITION_VARIABLE_LOCKMODE_SHARED)
RtlAcquireSRWLockShared( lock );
--
2.24.1

View File

@ -0,0 +1,33 @@
From 9b4b22164e5e76f7e6efcc13d8a7f0971e2af4dd Mon Sep 17 00:00:00 2001
From: Zebediah Figura <z.figura12@gmail.com>
Date: Mon, 30 Dec 2019 17:17:48 -0600
Subject: [PATCH 2/3] ntdll: Handle unaligned SRW locks when using keyed
events.
---
dlls/ntdll/sync.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/dlls/ntdll/sync.c b/dlls/ntdll/sync.c
index ac324ebbbe..5b0f5b903c 100644
--- a/dlls/ntdll/sync.c
+++ b/dlls/ntdll/sync.c
@@ -2035,11 +2035,11 @@ static NTSTATUS fast_release_srw_shared( RTL_SRWLOCK *lock )
#define SRWLOCK_RES_SHARED 0x00000001
#ifdef WORDS_BIGENDIAN
-#define srwlock_key_exclusive(lock) (&lock->Ptr)
-#define srwlock_key_shared(lock) ((void *)((char *)&lock->Ptr + 2))
+#define srwlock_key_exclusive(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 1) & ~1))
+#define srwlock_key_shared(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 3) & ~1))
#else
-#define srwlock_key_exclusive(lock) ((void *)((char *)&lock->Ptr + 2))
-#define srwlock_key_shared(lock) (&lock->Ptr)
+#define srwlock_key_exclusive(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 3) & ~1))
+#define srwlock_key_shared(lock) ((void *)(((ULONG_PTR)&lock->Ptr + 1) & ~1))
#endif
static inline void srwlock_check_invalid( unsigned int val )
--
2.24.1

View File

@ -0,0 +1,2 @@
Fixes: [48389] Detroit: Become Human has poor performance (use of unaligned futexes for condition variables on Linux)

View File

@ -217,6 +217,7 @@ patch_enable_all ()
enable_ntdll_aarch_TEB="$1"
enable_ntdll_ext4_case_folder="$1"
enable_ntdll_set_full_cpu_context="$1"
enable_ntdll_unaligned_futex="$1"
enable_ntdll_x86_64_SegDs="$1"
enable_ntoskrnl_Stubs="$1"
enable_nvapi_Stub_DLL="$1"
@ -764,6 +765,9 @@ patch_enable ()
ntdll-set_full_cpu_context)
enable_ntdll_set_full_cpu_context="$2"
;;
ntdll-unaligned-futex)
enable_ntdll_unaligned_futex="$2"
;;
ntdll-x86_64_SegDs)
enable_ntdll_x86_64_SegDs="$2"
;;
@ -5011,6 +5015,25 @@ if test "$enable_ntdll_set_full_cpu_context" -eq 1; then
) >> "$patchlist"
fi
# Patchset ntdll-unaligned-futex
# |
# | This patchset fixes the following Wine bugs:
# | * [#48389] Detroit: Become Human has poor performance (use of unaligned futexes for condition variables on Linux)
# |
# | Modified files:
# | * dlls/kernel32/tests/sync.c, dlls/ntdll/sync.c
# |
if test "$enable_ntdll_unaligned_futex" -eq 1; then
patch_apply ntdll-unaligned-futex/0001-ntdll-Handle-unaligned-condition-variables-when-usin.patch
patch_apply ntdll-unaligned-futex/0002-ntdll-Handle-unaligned-SRW-locks-when-using-keyed-ev.patch
patch_apply ntdll-unaligned-futex/0003-ntdll-Handle-unaligned-SRW-locks-when-using-futexes.patch
(
printf '%s\n' '+ { "Zebediah Figura", "ntdll: Handle unaligned condition variables when using futexes.", 1 },';
printf '%s\n' '+ { "Zebediah Figura", "ntdll: Handle unaligned SRW locks when using keyed events.", 1 },';
printf '%s\n' '+ { "Zebediah Figura", "ntdll: Handle unaligned SRW locks when using futexes.", 1 },';
) >> "$patchlist"
fi
# Patchset ntdll-x86_64_SegDs
# |
# | This patchset fixes the following Wine bugs:
@ -7306,7 +7329,7 @@ fi
# | dlls/xactengine3_7/xactengine3_7.spec, dlls/xaudio2_7/Makefile.in, dlls/xaudio2_7/tests/Makefile.in,
# | dlls/xaudio2_7/tests/globals.xgs, dlls/xaudio2_7/tests/rsrc.rc, dlls/xaudio2_7/tests/xact.c,
# | dlls/xaudio2_7/tests/xaudio2.c, dlls/xaudio2_7/xact_classes.idl, dlls/xaudio2_7/xact_dll.c, dlls/xaudio2_7/xaudio_dll.c,
# | include/Makefile.in, include/xact3.idl, include/xact3wb.h
# | include/Makefile.in, include/config.h.in, include/xact3.idl, include/xact3wb.h
# |
if test "$enable_xactengine_initial" -eq 1; then
patch_apply xactengine-initial/0001-include-Add-xact3.idl.patch