Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
41
third_party/FreeRDP/winpr/libwinpr/synch/CMakeLists.txt
vendored
Normal file
41
third_party/FreeRDP/winpr/libwinpr/synch/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
# WinPR: Windows Portable Runtime
|
||||
# libwinpr-synch cmake build script
|
||||
#
|
||||
# Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
winpr_module_add(
|
||||
address.c
|
||||
barrier.c
|
||||
critical.c
|
||||
event.c
|
||||
init.c
|
||||
mutex.c
|
||||
pollset.c
|
||||
pollset.h
|
||||
semaphore.c
|
||||
sleep.c
|
||||
synch.h
|
||||
timer.c
|
||||
wait.c
|
||||
)
|
||||
|
||||
if(FREEBSD)
|
||||
winpr_system_include_directory_add(${EPOLLSHIM_INCLUDE_DIR})
|
||||
winpr_library_add_private(${EPOLLSHIM_LIBS})
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
|
||||
add_subdirectory(test)
|
||||
endif()
|
||||
9
third_party/FreeRDP/winpr/libwinpr/synch/ModuleOptions.cmake
vendored
Normal file
9
third_party/FreeRDP/winpr/libwinpr/synch/ModuleOptions.cmake
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
set(MINWIN_LAYER "1")
|
||||
set(MINWIN_GROUP "core")
|
||||
set(MINWIN_MAJOR_VERSION "2")
|
||||
set(MINWIN_MINOR_VERSION "0")
|
||||
set(MINWIN_SHORT_NAME "synch")
|
||||
set(MINWIN_LONG_NAME "Synchronization Functions")
|
||||
set(MODULE_LIBRARY_NAME
|
||||
"api-ms-win-${MINWIN_GROUP}-${MINWIN_SHORT_NAME}-l${MINWIN_LAYER}-${MINWIN_MAJOR_VERSION}-${MINWIN_MINOR_VERSION}"
|
||||
)
|
||||
46
third_party/FreeRDP/winpr/libwinpr/synch/address.c
vendored
Normal file
46
third_party/FreeRDP/winpr/libwinpr/synch/address.c
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
|
||||
/**
|
||||
* WakeByAddressAll
|
||||
* WakeByAddressSingle
|
||||
* WaitOnAddress
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
VOID WakeByAddressAll(WINPR_ATTR_UNUSED PVOID Address)
|
||||
{
|
||||
}
|
||||
|
||||
VOID WakeByAddressSingle(WINPR_ATTR_UNUSED PVOID Address)
|
||||
{
|
||||
}
|
||||
|
||||
BOOL WaitOnAddress(WINPR_ATTR_UNUSED VOID volatile* Address, WINPR_ATTR_UNUSED PVOID CompareAddress,
|
||||
WINPR_ATTR_UNUSED size_t AddressSize, WINPR_ATTR_UNUSED DWORD dwMilliseconds)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
263
third_party/FreeRDP/winpr/libwinpr/synch/barrier.c
vendored
Normal file
263
third_party/FreeRDP/winpr/libwinpr/synch/barrier.c
vendored
Normal file
@@ -0,0 +1,263 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2016 Norbert Federa <norbert.federa@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include "synch.h"
|
||||
|
||||
#include <winpr/crt.h>
|
||||
|
||||
#ifdef WINPR_SYNCHRONIZATION_BARRIER
|
||||
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/library.h>
|
||||
#include <winpr/interlocked.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
/**
|
||||
* WinPR uses the internal RTL_BARRIER struct members exactly like Windows:
|
||||
*
|
||||
* DWORD Reserved1: number of threads that have not yet entered the barrier
|
||||
* DWORD Reserved2: number of threads required to enter the barrier
|
||||
* ULONG_PTR Reserved3[2]; two synchronization events (manual reset events)
|
||||
* DWORD Reserved4; number of processors
|
||||
* DWORD Reserved5; spincount
|
||||
*/
|
||||
|
||||
#ifdef _WIN32
|
||||
|
||||
static HMODULE g_Kernel32 = nullptr;
|
||||
static BOOL g_NativeBarrier = FALSE;
|
||||
static INIT_ONCE g_InitOnce = INIT_ONCE_STATIC_INIT;
|
||||
|
||||
typedef BOOL(WINAPI* fnInitializeSynchronizationBarrier)(LPSYNCHRONIZATION_BARRIER lpBarrier,
|
||||
LONG lTotalThreads, LONG lSpinCount);
|
||||
typedef BOOL(WINAPI* fnEnterSynchronizationBarrier)(LPSYNCHRONIZATION_BARRIER lpBarrier,
|
||||
DWORD dwFlags);
|
||||
typedef BOOL(WINAPI* fnDeleteSynchronizationBarrier)(LPSYNCHRONIZATION_BARRIER lpBarrier);
|
||||
|
||||
static fnInitializeSynchronizationBarrier pfnInitializeSynchronizationBarrier = nullptr;
|
||||
static fnEnterSynchronizationBarrier pfnEnterSynchronizationBarrier = nullptr;
|
||||
static fnDeleteSynchronizationBarrier pfnDeleteSynchronizationBarrier = nullptr;
|
||||
|
||||
static BOOL CALLBACK InitOnce_Barrier(PINIT_ONCE once, PVOID param, PVOID* context)
|
||||
{
|
||||
g_Kernel32 = LoadLibraryA("kernel32.dll");
|
||||
|
||||
if (!g_Kernel32)
|
||||
return TRUE;
|
||||
|
||||
pfnInitializeSynchronizationBarrier = GetProcAddressAs(
|
||||
g_Kernel32, "InitializeSynchronizationBarrier", fnInitializeSynchronizationBarrier);
|
||||
|
||||
pfnEnterSynchronizationBarrier =
|
||||
GetProcAddressAs(g_Kernel32, "EnterSynchronizationBarrier", fnEnterSynchronizationBarrier);
|
||||
pfnDeleteSynchronizationBarrier = GetProcAddressAs(g_Kernel32, "DeleteSynchronizationBarrier",
|
||||
fnDeleteSynchronizationBarrier);
|
||||
|
||||
if (pfnInitializeSynchronizationBarrier && pfnEnterSynchronizationBarrier &&
|
||||
pfnDeleteSynchronizationBarrier)
|
||||
{
|
||||
g_NativeBarrier = TRUE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
BOOL WINAPI winpr_InitializeSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier,
|
||||
LONG lTotalThreads, LONG lSpinCount)
|
||||
{
|
||||
SYSTEM_INFO sysinfo;
|
||||
HANDLE hEvent0 = nullptr;
|
||||
HANDLE hEvent1 = nullptr;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (!InitOnceExecuteOnce(&g_InitOnce, InitOnce_Barrier, nullptr, nullptr))
|
||||
return FALSE;
|
||||
|
||||
if (g_NativeBarrier)
|
||||
return pfnInitializeSynchronizationBarrier(lpBarrier, lTotalThreads, lSpinCount);
|
||||
#endif
|
||||
|
||||
if (!lpBarrier || lTotalThreads < 1 || lSpinCount < -1)
|
||||
{
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
ZeroMemory(lpBarrier, sizeof(SYNCHRONIZATION_BARRIER));
|
||||
|
||||
if (lSpinCount == -1)
|
||||
lSpinCount = 2000;
|
||||
|
||||
if (!(hEvent0 = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
return FALSE;
|
||||
|
||||
if (!(hEvent1 = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
(void)CloseHandle(hEvent0);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
GetNativeSystemInfo(&sysinfo);
|
||||
|
||||
WINPR_ASSERT(lTotalThreads >= 0);
|
||||
lpBarrier->Reserved1 = (DWORD)lTotalThreads;
|
||||
lpBarrier->Reserved2 = (DWORD)lTotalThreads;
|
||||
lpBarrier->Reserved3[0] = (ULONG_PTR)hEvent0;
|
||||
lpBarrier->Reserved3[1] = (ULONG_PTR)hEvent1;
|
||||
lpBarrier->Reserved4 = sysinfo.dwNumberOfProcessors;
|
||||
WINPR_ASSERT(lSpinCount >= 0);
|
||||
lpBarrier->Reserved5 = (DWORD)lSpinCount;
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WINAPI winpr_EnterSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier, DWORD dwFlags)
|
||||
{
|
||||
LONG remainingThreads = 0;
|
||||
HANDLE hCurrentEvent = nullptr;
|
||||
HANDLE hDormantEvent = nullptr;
|
||||
|
||||
#ifdef _WIN32
|
||||
if (g_NativeBarrier)
|
||||
return pfnEnterSynchronizationBarrier(lpBarrier, dwFlags);
|
||||
#endif
|
||||
|
||||
if (!lpBarrier)
|
||||
return FALSE;
|
||||
|
||||
/**
|
||||
* dwFlags according to
|
||||
* https://msdn.microsoft.com/en-us/library/windows/desktop/hh706889(v=vs.85).aspx
|
||||
*
|
||||
* SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY (0x01)
|
||||
* Specifies that the thread entering the barrier should block
|
||||
* immediately until the last thread enters the barrier.
|
||||
*
|
||||
* SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY (0x02)
|
||||
* Specifies that the thread entering the barrier should spin until the
|
||||
* last thread enters the barrier, even if the spinning thread exceeds
|
||||
* the barrier's maximum spin count.
|
||||
*
|
||||
* SYNCHRONIZATION_BARRIER_FLAGS_NO_DELETE (0x04)
|
||||
* Specifies that the function can skip the work required to ensure
|
||||
* that it is safe to delete the barrier, which can improve
|
||||
* performance. All threads that enter this barrier must specify the
|
||||
* flag; otherwise, the flag is ignored. This flag should be used only
|
||||
* if the barrier will never be deleted.
|
||||
*/
|
||||
|
||||
hCurrentEvent = (HANDLE)lpBarrier->Reserved3[0];
|
||||
hDormantEvent = (HANDLE)lpBarrier->Reserved3[1];
|
||||
|
||||
remainingThreads = InterlockedDecrement((LONG*)&lpBarrier->Reserved1);
|
||||
|
||||
WINPR_ASSERT(remainingThreads >= 0);
|
||||
|
||||
if (remainingThreads > 0)
|
||||
{
|
||||
DWORD dwProcessors = lpBarrier->Reserved4;
|
||||
BOOL spinOnly = (dwFlags & SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY) != 0;
|
||||
BOOL blockOnly = (dwFlags & SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY) != 0;
|
||||
BOOL block = TRUE;
|
||||
|
||||
/**
|
||||
* If SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY is set we will
|
||||
* always spin and trust that the user knows what he/she/it
|
||||
* is doing. Otherwise we'll only spin if the flag
|
||||
* SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY is not set and
|
||||
* the number of remaining threads is less than the number
|
||||
* of processors.
|
||||
*/
|
||||
|
||||
if (spinOnly || (((ULONG)remainingThreads < dwProcessors) && !blockOnly))
|
||||
{
|
||||
DWORD dwSpinCount = lpBarrier->Reserved5;
|
||||
DWORD sp = 0;
|
||||
/**
|
||||
* nb: we must let the compiler know that our comparand
|
||||
* can change between the iterations in the loop below
|
||||
*/
|
||||
volatile ULONG_PTR* cmp = &lpBarrier->Reserved3[0];
|
||||
/* we spin until the last thread _completed_ the event switch */
|
||||
while ((block = (*cmp == (ULONG_PTR)hCurrentEvent)))
|
||||
if (!spinOnly && ++sp > dwSpinCount)
|
||||
break;
|
||||
}
|
||||
|
||||
if (block)
|
||||
(void)WaitForSingleObject(hCurrentEvent, INFINITE);
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* reset the dormant event first */
|
||||
(void)ResetEvent(hDormantEvent);
|
||||
|
||||
/* reset the remaining counter */
|
||||
lpBarrier->Reserved1 = lpBarrier->Reserved2;
|
||||
|
||||
/* switch events - this will also unblock the spinning threads */
|
||||
lpBarrier->Reserved3[1] = (ULONG_PTR)hCurrentEvent;
|
||||
lpBarrier->Reserved3[0] = (ULONG_PTR)hDormantEvent;
|
||||
|
||||
/* signal the blocked threads */
|
||||
(void)SetEvent(hCurrentEvent);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL WINAPI winpr_DeleteSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpBarrier)
|
||||
{
|
||||
#ifdef _WIN32
|
||||
if (g_NativeBarrier)
|
||||
return pfnDeleteSynchronizationBarrier(lpBarrier);
|
||||
#endif
|
||||
|
||||
/**
|
||||
* According to https://msdn.microsoft.com/en-us/library/windows/desktop/hh706887(v=vs.85).aspx
|
||||
* Return value:
|
||||
* The DeleteSynchronizationBarrier function always returns TRUE.
|
||||
*/
|
||||
|
||||
if (!lpBarrier)
|
||||
return TRUE;
|
||||
|
||||
while (lpBarrier->Reserved1 != lpBarrier->Reserved2)
|
||||
SwitchToThread();
|
||||
|
||||
if (lpBarrier->Reserved3[0])
|
||||
(void)CloseHandle((HANDLE)lpBarrier->Reserved3[0]);
|
||||
|
||||
if (lpBarrier->Reserved3[1])
|
||||
(void)CloseHandle((HANDLE)lpBarrier->Reserved3[1]);
|
||||
|
||||
ZeroMemory(lpBarrier, sizeof(SYNCHRONIZATION_BARRIER));
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#endif
|
||||
277
third_party/FreeRDP/winpr/libwinpr/synch/critical.c
vendored
Normal file
277
third_party/FreeRDP/winpr/libwinpr/synch/critical.c
vendored
Normal file
@@ -0,0 +1,277 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2013 Norbert Federa <norbert.federa@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/interlocked.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include "synch.h"
|
||||
|
||||
#ifdef WINPR_HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <mach/task.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/semaphore.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "../log.h"
|
||||
#define TAG WINPR_TAG("synch.critical")
|
||||
|
||||
VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
InitializeCriticalSectionEx(lpCriticalSection, 0, 0);
|
||||
}
|
||||
|
||||
BOOL InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount,
|
||||
DWORD Flags)
|
||||
{
|
||||
WINPR_ASSERT(lpCriticalSection);
|
||||
/**
|
||||
* See http://msdn.microsoft.com/en-us/library/ff541979(v=vs.85).aspx
|
||||
* - The LockCount field indicates the number of times that any thread has
|
||||
* called the EnterCriticalSection routine for this critical section,
|
||||
* minus one. This field starts at -1 for an unlocked critical section.
|
||||
* Each call of EnterCriticalSection increments this value; each call of
|
||||
* LeaveCriticalSection decrements it.
|
||||
* - The RecursionCount field indicates the number of times that the owning
|
||||
* thread has called EnterCriticalSection for this critical section.
|
||||
*/
|
||||
if (Flags != 0)
|
||||
{
|
||||
WLog_WARN(TAG, "Flags unimplemented");
|
||||
}
|
||||
|
||||
lpCriticalSection->DebugInfo = nullptr;
|
||||
lpCriticalSection->LockCount = -1;
|
||||
lpCriticalSection->SpinCount = 0;
|
||||
lpCriticalSection->RecursionCount = 0;
|
||||
lpCriticalSection->OwningThread = nullptr;
|
||||
lpCriticalSection->LockSemaphore = (winpr_sem_t*)malloc(sizeof(winpr_sem_t));
|
||||
|
||||
if (!lpCriticalSection->LockSemaphore)
|
||||
return FALSE;
|
||||
|
||||
#if defined(__APPLE__)
|
||||
|
||||
if (semaphore_create(mach_task_self(), lpCriticalSection->LockSemaphore, SYNC_POLICY_FIFO, 0) !=
|
||||
KERN_SUCCESS)
|
||||
goto out_fail;
|
||||
|
||||
#else
|
||||
|
||||
if (sem_init(lpCriticalSection->LockSemaphore, 0, 0) != 0)
|
||||
goto out_fail;
|
||||
|
||||
#endif
|
||||
SetCriticalSectionSpinCount(lpCriticalSection, dwSpinCount);
|
||||
return TRUE;
|
||||
out_fail:
|
||||
free(lpCriticalSection->LockSemaphore);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount)
|
||||
{
|
||||
return InitializeCriticalSectionEx(lpCriticalSection, dwSpinCount, 0);
|
||||
}
|
||||
|
||||
DWORD SetCriticalSectionSpinCount(WINPR_ATTR_UNUSED LPCRITICAL_SECTION lpCriticalSection,
|
||||
WINPR_ATTR_UNUSED DWORD dwSpinCount)
|
||||
{
|
||||
WINPR_ASSERT(lpCriticalSection);
|
||||
#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
|
||||
SYSTEM_INFO sysinfo;
|
||||
DWORD dwPreviousSpinCount = lpCriticalSection->SpinCount;
|
||||
|
||||
if (dwSpinCount)
|
||||
{
|
||||
/* Don't spin on uniprocessor systems! */
|
||||
GetNativeSystemInfo(&sysinfo);
|
||||
|
||||
if (sysinfo.dwNumberOfProcessors < 2)
|
||||
dwSpinCount = 0;
|
||||
}
|
||||
|
||||
lpCriticalSection->SpinCount = dwSpinCount;
|
||||
return dwPreviousSpinCount;
|
||||
#else
|
||||
// WLog_ERR("TODO", "TODO: implement");
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
static VOID WaitForCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
WINPR_ASSERT(lpCriticalSection);
|
||||
WINPR_ASSERT(lpCriticalSection->LockSemaphore);
|
||||
|
||||
#if defined(__APPLE__)
|
||||
semaphore_wait(*((winpr_sem_t*)lpCriticalSection->LockSemaphore));
|
||||
#else
|
||||
sem_wait((winpr_sem_t*)lpCriticalSection->LockSemaphore);
|
||||
#endif
|
||||
}
|
||||
|
||||
static VOID UnWaitCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
WINPR_ASSERT(lpCriticalSection);
|
||||
WINPR_ASSERT(lpCriticalSection->LockSemaphore);
|
||||
#if defined __APPLE__
|
||||
semaphore_signal(*((winpr_sem_t*)lpCriticalSection->LockSemaphore));
|
||||
#else
|
||||
sem_post((winpr_sem_t*)lpCriticalSection->LockSemaphore);
|
||||
#endif
|
||||
}
|
||||
|
||||
VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
WINPR_ASSERT(lpCriticalSection);
|
||||
#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
|
||||
ULONG SpinCount = lpCriticalSection->SpinCount;
|
||||
|
||||
/* If we're lucky or if the current thread is already owner we can return early */
|
||||
if (SpinCount && TryEnterCriticalSection(lpCriticalSection))
|
||||
return;
|
||||
|
||||
/* Spin requested times but don't compete with another waiting thread */
|
||||
while (SpinCount-- && lpCriticalSection->LockCount < 1)
|
||||
{
|
||||
/* Atomically try to acquire and check the if the section is free. */
|
||||
if (InterlockedCompareExchange(&lpCriticalSection->LockCount, 0, -1) == -1)
|
||||
{
|
||||
lpCriticalSection->RecursionCount = 1;
|
||||
lpCriticalSection->OwningThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
|
||||
return;
|
||||
}
|
||||
|
||||
/* Failed to get the lock. Let the scheduler know that we're spinning. */
|
||||
if (sched_yield() != 0)
|
||||
{
|
||||
/**
|
||||
* On some operating systems sched_yield is a stub.
|
||||
* usleep should at least trigger a context switch if any thread is waiting.
|
||||
* A ThreadYield() would be nice in winpr ...
|
||||
*/
|
||||
usleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
/* First try the fastest possible path to get the lock. */
|
||||
if (InterlockedIncrement(&lpCriticalSection->LockCount))
|
||||
{
|
||||
/* Section is already locked. Check if it is owned by the current thread. */
|
||||
if (lpCriticalSection->OwningThread == (HANDLE)(ULONG_PTR)GetCurrentThreadId())
|
||||
{
|
||||
/* Recursion. No need to wait. */
|
||||
lpCriticalSection->RecursionCount++;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Section is locked by another thread. We have to wait. */
|
||||
WaitForCriticalSection(lpCriticalSection);
|
||||
}
|
||||
|
||||
/* We got the lock. Own it ... */
|
||||
lpCriticalSection->RecursionCount = 1;
|
||||
lpCriticalSection->OwningThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
|
||||
}
|
||||
|
||||
BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
HANDLE current_thread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
|
||||
|
||||
WINPR_ASSERT(lpCriticalSection);
|
||||
|
||||
/* Atomically acquire the the lock if the section is free. */
|
||||
if (InterlockedCompareExchange(&lpCriticalSection->LockCount, 0, -1) == -1)
|
||||
{
|
||||
lpCriticalSection->RecursionCount = 1;
|
||||
lpCriticalSection->OwningThread = current_thread;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* Section is already locked. Check if it is owned by the current thread. */
|
||||
if (lpCriticalSection->OwningThread == current_thread)
|
||||
{
|
||||
/* Recursion, return success */
|
||||
lpCriticalSection->RecursionCount++;
|
||||
InterlockedIncrement(&lpCriticalSection->LockCount);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
WINPR_ASSERT(lpCriticalSection);
|
||||
|
||||
/* Decrement RecursionCount and check if this is the last LeaveCriticalSection call ...*/
|
||||
if (--lpCriticalSection->RecursionCount < 1)
|
||||
{
|
||||
/* Last recursion, clear owner, unlock and if there are other waiting threads ... */
|
||||
lpCriticalSection->OwningThread = nullptr;
|
||||
|
||||
if (InterlockedDecrement(&lpCriticalSection->LockCount) >= 0)
|
||||
{
|
||||
/* ...signal the semaphore to unblock the next waiting thread */
|
||||
UnWaitCriticalSection(lpCriticalSection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
(void)InterlockedDecrement(&lpCriticalSection->LockCount);
|
||||
}
|
||||
}
|
||||
|
||||
VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
WINPR_ASSERT(lpCriticalSection);
|
||||
|
||||
lpCriticalSection->LockCount = -1;
|
||||
lpCriticalSection->SpinCount = 0;
|
||||
lpCriticalSection->RecursionCount = 0;
|
||||
lpCriticalSection->OwningThread = nullptr;
|
||||
|
||||
if (lpCriticalSection->LockSemaphore != nullptr)
|
||||
{
|
||||
#if defined __APPLE__
|
||||
semaphore_destroy(mach_task_self(), *((winpr_sem_t*)lpCriticalSection->LockSemaphore));
|
||||
#else
|
||||
sem_destroy((winpr_sem_t*)lpCriticalSection->LockSemaphore);
|
||||
#endif
|
||||
free(lpCriticalSection->LockSemaphore);
|
||||
lpCriticalSection->LockSemaphore = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
559
third_party/FreeRDP/winpr/libwinpr/synch/event.c
vendored
Normal file
559
third_party/FreeRDP/winpr/libwinpr/synch/event.c
vendored
Normal file
@@ -0,0 +1,559 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2017 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2017 Thincast Technologies GmbH
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include "synch.h"
|
||||
|
||||
#ifdef WINPR_HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifdef WINPR_HAVE_SYS_EVENTFD_H
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include "../handle/handle.h"
|
||||
#include "../pipe/pipe.h"
|
||||
|
||||
#include "../log.h"
|
||||
#include "event.h"
|
||||
#define TAG WINPR_TAG("synch.event")
|
||||
|
||||
#if defined(WITH_DEBUG_EVENTS)
|
||||
static wArrayList* global_event_list = nullptr;
|
||||
|
||||
static void dump_event(WINPR_EVENT* event, size_t index)
|
||||
{
|
||||
char** msg = nullptr;
|
||||
size_t used = 0;
|
||||
|
||||
WLog_DBG(TAG, "Event handle created still not closed! [%" PRIuz ", %p]", index, event);
|
||||
msg = winpr_backtrace_symbols(event->create_stack, &used);
|
||||
|
||||
for (size_t i = 2; i < used; i++)
|
||||
WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
|
||||
|
||||
free(msg);
|
||||
}
|
||||
#endif /* WITH_DEBUG_EVENTS */
|
||||
|
||||
#ifdef WINPR_HAVE_SYS_EVENTFD_H
|
||||
#if !defined(WITH_EVENTFD_READ_WRITE)
|
||||
static int eventfd_read(int fd, eventfd_t* value)
|
||||
{
|
||||
return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
|
||||
}
|
||||
|
||||
static int eventfd_write(int fd, eventfd_t value)
|
||||
{
|
||||
return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef WINPR_HAVE_SYS_EVENTFD_H
|
||||
static BOOL set_non_blocking_fd(int fd)
|
||||
{
|
||||
int flags;
|
||||
flags = fcntl(fd, F_GETFL);
|
||||
if (flags < 0)
|
||||
return FALSE;
|
||||
|
||||
return fcntl(fd, F_SETFL, flags | O_NONBLOCK) >= 0;
|
||||
}
|
||||
#endif /* !WINPR_HAVE_SYS_EVENTFD_H */
|
||||
|
||||
BOOL winpr_event_init(WINPR_EVENT_IMPL* event)
|
||||
{
|
||||
#ifdef WINPR_HAVE_SYS_EVENTFD_H
|
||||
event->fds[1] = -1;
|
||||
event->fds[0] = eventfd(0, EFD_NONBLOCK);
|
||||
|
||||
return event->fds[0] >= 0;
|
||||
#else
|
||||
if (pipe(event->fds) < 0)
|
||||
return FALSE;
|
||||
|
||||
if (!set_non_blocking_fd(event->fds[0]) || !set_non_blocking_fd(event->fds[1]))
|
||||
goto out_error;
|
||||
|
||||
return TRUE;
|
||||
|
||||
out_error:
|
||||
winpr_event_uninit(event);
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
void winpr_event_init_from_fd(WINPR_EVENT_IMPL* event, int fd)
|
||||
{
|
||||
event->fds[0] = fd;
|
||||
#ifndef WINPR_HAVE_SYS_EVENTFD_H
|
||||
event->fds[1] = fd;
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL winpr_event_set(WINPR_EVENT_IMPL* event)
|
||||
{
|
||||
int ret = 0;
|
||||
do
|
||||
{
|
||||
#ifdef WINPR_HAVE_SYS_EVENTFD_H
|
||||
eventfd_t value = 1;
|
||||
ret = eventfd_write(event->fds[0], value);
|
||||
#else
|
||||
ret = write(event->fds[1], "-", 1);
|
||||
#endif
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
|
||||
return ret >= 0;
|
||||
}
|
||||
|
||||
BOOL winpr_event_reset(WINPR_EVENT_IMPL* event)
|
||||
{
|
||||
int ret = 0;
|
||||
do
|
||||
{
|
||||
do
|
||||
{
|
||||
#ifdef WINPR_HAVE_SYS_EVENTFD_H
|
||||
eventfd_t value = 1;
|
||||
ret = eventfd_read(event->fds[0], &value);
|
||||
#else
|
||||
char value;
|
||||
ret = read(event->fds[0], &value, 1);
|
||||
#endif
|
||||
} while (ret < 0 && errno == EINTR);
|
||||
} while (ret >= 0);
|
||||
|
||||
return (errno == EAGAIN);
|
||||
}
|
||||
|
||||
void winpr_event_uninit(WINPR_EVENT_IMPL* event)
|
||||
{
|
||||
if (event->fds[0] >= 0)
|
||||
{
|
||||
close(event->fds[0]);
|
||||
event->fds[0] = -1;
|
||||
}
|
||||
|
||||
if (event->fds[1] >= 0)
|
||||
{
|
||||
close(event->fds[1]);
|
||||
event->fds[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL EventCloseHandle(HANDLE handle);
|
||||
|
||||
static BOOL EventIsHandled(HANDLE handle)
|
||||
{
|
||||
return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_EVENT, FALSE);
|
||||
}
|
||||
|
||||
static int EventGetFd(HANDLE handle)
|
||||
{
|
||||
WINPR_EVENT* event = (WINPR_EVENT*)handle;
|
||||
|
||||
if (!EventIsHandled(handle))
|
||||
return -1;
|
||||
|
||||
return event->impl.fds[0];
|
||||
}
|
||||
|
||||
static BOOL EventCloseHandle_(WINPR_EVENT* event)
|
||||
{
|
||||
if (!event)
|
||||
return FALSE;
|
||||
|
||||
if (event->bAttached)
|
||||
{
|
||||
// don't close attached file descriptor
|
||||
event->impl.fds[0] = -1; // mark as invalid
|
||||
}
|
||||
|
||||
winpr_event_uninit(&event->impl);
|
||||
|
||||
#if defined(WITH_DEBUG_EVENTS)
|
||||
if (global_event_list)
|
||||
{
|
||||
ArrayList_Remove(global_event_list, event);
|
||||
if (ArrayList_Count(global_event_list) < 1)
|
||||
{
|
||||
ArrayList_Free(global_event_list);
|
||||
global_event_list = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
winpr_backtrace_free(event->create_stack);
|
||||
#endif
|
||||
free(event->name);
|
||||
free(event);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL EventCloseHandle(HANDLE handle)
|
||||
{
|
||||
WINPR_EVENT* event = (WINPR_EVENT*)handle;
|
||||
|
||||
if (!EventIsHandled(handle))
|
||||
return FALSE;
|
||||
|
||||
return EventCloseHandle_(event);
|
||||
}
|
||||
|
||||
static HANDLE_OPS ops = { EventIsHandled, EventCloseHandle, EventGetFd, nullptr, /* CleanupHandle */
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
|
||||
HANDLE CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
|
||||
LPCWSTR lpName)
|
||||
{
|
||||
HANDLE handle = nullptr;
|
||||
char* name = nullptr;
|
||||
|
||||
if (lpName)
|
||||
{
|
||||
name = ConvertWCharToUtf8Alloc(lpName, nullptr);
|
||||
if (!name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
handle = CreateEventA(lpEventAttributes, bManualReset, bInitialState, name);
|
||||
free(name);
|
||||
return handle;
|
||||
}
|
||||
|
||||
HANDLE CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
|
||||
LPCSTR lpName)
|
||||
{
|
||||
WINPR_EVENT* event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT));
|
||||
|
||||
if (lpEventAttributes)
|
||||
WLog_WARN(TAG, "[%s] does not support lpEventAttributes", lpName);
|
||||
|
||||
if (!event)
|
||||
return nullptr;
|
||||
|
||||
if (lpName)
|
||||
event->name = strdup(lpName);
|
||||
|
||||
event->impl.fds[0] = -1;
|
||||
event->impl.fds[1] = -1;
|
||||
event->bAttached = FALSE;
|
||||
event->bManualReset = bManualReset;
|
||||
event->common.ops = &ops;
|
||||
WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, FD_READ);
|
||||
|
||||
if (!event->bManualReset)
|
||||
WLog_ERR(TAG, "auto-reset events not yet implemented");
|
||||
|
||||
if (!winpr_event_init(&event->impl))
|
||||
goto fail;
|
||||
|
||||
if (bInitialState)
|
||||
{
|
||||
if (!SetEvent(event))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
#if defined(WITH_DEBUG_EVENTS)
|
||||
event->create_stack = winpr_backtrace(20);
|
||||
if (!global_event_list)
|
||||
global_event_list = ArrayList_New(TRUE);
|
||||
|
||||
if (global_event_list)
|
||||
ArrayList_Append(global_event_list, event);
|
||||
#endif
|
||||
return (HANDLE)event;
|
||||
fail:
|
||||
EventCloseHandle_(event);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HANDLE CreateEventExW(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, DWORD dwFlags,
|
||||
DWORD dwDesiredAccess)
|
||||
{
|
||||
BOOL initial = FALSE;
|
||||
BOOL manual = FALSE;
|
||||
|
||||
if (dwFlags & CREATE_EVENT_INITIAL_SET)
|
||||
initial = TRUE;
|
||||
|
||||
if (dwFlags & CREATE_EVENT_MANUAL_RESET)
|
||||
manual = TRUE;
|
||||
|
||||
if (dwDesiredAccess != 0)
|
||||
{
|
||||
char name[MAX_PATH] = WINPR_C_ARRAY_INIT;
|
||||
ConvertWCharToUtf8(lpName, name, sizeof(name) - 1);
|
||||
WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, name, dwDesiredAccess);
|
||||
}
|
||||
|
||||
return CreateEventW(lpEventAttributes, manual, initial, lpName);
|
||||
}
|
||||
|
||||
HANDLE CreateEventExA(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags,
|
||||
DWORD dwDesiredAccess)
|
||||
{
|
||||
BOOL initial = FALSE;
|
||||
BOOL manual = FALSE;
|
||||
|
||||
if (dwFlags & CREATE_EVENT_INITIAL_SET)
|
||||
initial = TRUE;
|
||||
|
||||
if (dwFlags & CREATE_EVENT_MANUAL_RESET)
|
||||
manual = TRUE;
|
||||
|
||||
if (dwDesiredAccess != 0)
|
||||
WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName,
|
||||
dwDesiredAccess);
|
||||
|
||||
return CreateEventA(lpEventAttributes, manual, initial, lpName);
|
||||
}
|
||||
|
||||
HANDLE OpenEventW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName)
|
||||
{
|
||||
/* TODO: Implement */
|
||||
WINPR_UNUSED(dwDesiredAccess);
|
||||
WINPR_UNUSED(bInheritHandle);
|
||||
WINPR_UNUSED(lpName);
|
||||
WLog_ERR(TAG, "not implemented");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HANDLE OpenEventA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName)
|
||||
{
|
||||
/* TODO: Implement */
|
||||
WINPR_UNUSED(dwDesiredAccess);
|
||||
WINPR_UNUSED(bInheritHandle);
|
||||
WINPR_UNUSED(lpName);
|
||||
WLog_ERR(TAG, "not implemented");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BOOL SetEvent(HANDLE hEvent)
|
||||
{
|
||||
ULONG Type = 0;
|
||||
WINPR_HANDLE* Object = nullptr;
|
||||
WINPR_EVENT* event = nullptr;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
|
||||
{
|
||||
WLog_ERR(TAG, "SetEvent: hEvent is not an event");
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
event = (WINPR_EVENT*)Object;
|
||||
return winpr_event_set(&event->impl);
|
||||
}
|
||||
|
||||
BOOL ResetEvent(HANDLE hEvent)
|
||||
{
|
||||
ULONG Type = 0;
|
||||
WINPR_HANDLE* Object = nullptr;
|
||||
WINPR_EVENT* event = nullptr;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
|
||||
{
|
||||
WLog_ERR(TAG, "ResetEvent: hEvent is not an event");
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
event = (WINPR_EVENT*)Object;
|
||||
return winpr_event_reset(&event->impl);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
HANDLE CreateFileDescriptorEventW(WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
BOOL bManualReset, WINPR_ATTR_UNUSED BOOL bInitialState,
|
||||
int FileDescriptor, ULONG mode)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
WINPR_EVENT* event = nullptr;
|
||||
HANDLE handle = nullptr;
|
||||
event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT));
|
||||
|
||||
if (event)
|
||||
{
|
||||
event->impl.fds[0] = -1;
|
||||
event->impl.fds[1] = -1;
|
||||
event->bAttached = TRUE;
|
||||
event->bManualReset = bManualReset;
|
||||
winpr_event_init_from_fd(&event->impl, FileDescriptor);
|
||||
event->common.ops = &ops;
|
||||
WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, mode);
|
||||
handle = (HANDLE)event;
|
||||
}
|
||||
|
||||
return handle;
|
||||
#else
|
||||
return nullptr;
|
||||
#endif
|
||||
}
|
||||
|
||||
HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
|
||||
BOOL bInitialState, int FileDescriptor, ULONG mode)
|
||||
{
|
||||
return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState,
|
||||
FileDescriptor, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an event based on the handle returned by GetEventWaitObject()
|
||||
*/
|
||||
HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
|
||||
BOOL bInitialState, void* pObject)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState,
|
||||
(int)(ULONG_PTR)pObject, WINPR_FD_READ);
|
||||
#else
|
||||
HANDLE hEvent = nullptr;
|
||||
DuplicateHandle(GetCurrentProcess(), pObject, GetCurrentProcess(), &hEvent, 0, FALSE,
|
||||
DUPLICATE_SAME_ACCESS);
|
||||
return hEvent;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns inner file descriptor for usage with select()
|
||||
* This file descriptor is not usable on Windows
|
||||
*/
|
||||
|
||||
int GetEventFileDescriptor(HANDLE hEvent)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return winpr_Handle_getFd(hEvent);
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* Set inner file descriptor for usage with select()
|
||||
* This file descriptor is not usable on Windows
|
||||
*/
|
||||
|
||||
int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
ULONG Type = 0;
|
||||
WINPR_HANDLE* Object = nullptr;
|
||||
WINPR_EVENT* event = nullptr;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
|
||||
{
|
||||
WLog_ERR(TAG, "SetEventFileDescriptor: hEvent is not an event");
|
||||
SetLastError(ERROR_INVALID_PARAMETER);
|
||||
return -1;
|
||||
}
|
||||
|
||||
event = (WINPR_EVENT*)Object;
|
||||
|
||||
if (!event->bAttached && event->impl.fds[0] >= 0 && event->impl.fds[0] != FileDescriptor)
|
||||
close(event->impl.fds[0]);
|
||||
|
||||
event->bAttached = TRUE;
|
||||
event->common.Mode = mode;
|
||||
event->impl.fds[0] = FileDescriptor;
|
||||
return 0;
|
||||
#else
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns platform-specific wait object as a void pointer
|
||||
*
|
||||
* On Windows, the returned object is the same as the hEvent
|
||||
* argument and is an event HANDLE usable in WaitForMultipleObjects
|
||||
*
|
||||
* On other platforms, the returned object can be cast to an int
|
||||
* to obtain a file descriptor usable in select()
|
||||
*/
|
||||
|
||||
void* GetEventWaitObject(HANDLE hEvent)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
int fd = 0;
|
||||
void* obj = nullptr;
|
||||
fd = GetEventFileDescriptor(hEvent);
|
||||
obj = ((void*)(long)fd);
|
||||
return obj;
|
||||
#else
|
||||
return hEvent;
|
||||
#endif
|
||||
}
|
||||
#if defined(WITH_DEBUG_EVENTS)
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
static BOOL dump_handle_list(void* data, size_t index, va_list ap)
|
||||
{
|
||||
WINPR_EVENT* event = data;
|
||||
dump_event(event, index);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void DumpEventHandles_(const char* fkt, const char* file, size_t line)
|
||||
{
|
||||
struct rlimit r = WINPR_C_ARRAY_INIT;
|
||||
int rc = getrlimit(RLIMIT_NOFILE, &r);
|
||||
if (rc >= 0)
|
||||
{
|
||||
size_t count = 0;
|
||||
for (rlim_t x = 0; x < r.rlim_cur; x++)
|
||||
{
|
||||
int flags = fcntl(x, F_GETFD);
|
||||
if (flags >= 0)
|
||||
count++;
|
||||
}
|
||||
WLog_INFO(TAG, "------- limits [%d/%d] open files %" PRIuz, r.rlim_cur, r.rlim_max, count);
|
||||
}
|
||||
WLog_DBG(TAG, "--------- Start dump [%s %s:%" PRIuz "]", fkt, file, line);
|
||||
if (global_event_list)
|
||||
{
|
||||
ArrayList_Lock(global_event_list);
|
||||
ArrayList_ForEach(global_event_list, dump_handle_list);
|
||||
ArrayList_Unlock(global_event_list);
|
||||
}
|
||||
WLog_DBG(TAG, "--------- End dump [%s %s:%" PRIuz "]", fkt, file, line);
|
||||
}
|
||||
#endif
|
||||
57
third_party/FreeRDP/winpr/libwinpr/synch/event.h
vendored
Normal file
57
third_party/FreeRDP/winpr/libwinpr/synch/event.h
vendored
Normal file
@@ -0,0 +1,57 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* event implementation
|
||||
*
|
||||
* Copyright 2021 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef WINPR_LIBWINPR_SYNCH_EVENT_H_
|
||||
#define WINPR_LIBWINPR_SYNCH_EVENT_H_
|
||||
|
||||
#include "../handle/handle.h"
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#ifdef WINPR_HAVE_SYS_EVENTFD_H
|
||||
#include <sys/eventfd.h>
|
||||
#endif
|
||||
|
||||
struct winpr_event_impl
|
||||
{
|
||||
int fds[2];
|
||||
};
|
||||
|
||||
typedef struct winpr_event_impl WINPR_EVENT_IMPL;
|
||||
|
||||
struct winpr_event
|
||||
{
|
||||
WINPR_HANDLE common;
|
||||
|
||||
WINPR_EVENT_IMPL impl;
|
||||
BOOL bAttached;
|
||||
BOOL bManualReset;
|
||||
char* name;
|
||||
#if defined(WITH_DEBUG_EVENTS)
|
||||
void* create_stack;
|
||||
#endif
|
||||
};
|
||||
typedef struct winpr_event WINPR_EVENT;
|
||||
|
||||
BOOL winpr_event_init(WINPR_EVENT_IMPL* event);
|
||||
void winpr_event_init_from_fd(WINPR_EVENT_IMPL* event, int fd);
|
||||
BOOL winpr_event_set(WINPR_EVENT_IMPL* event);
|
||||
BOOL winpr_event_reset(WINPR_EVENT_IMPL* event);
|
||||
void winpr_event_uninit(WINPR_EVENT_IMPL* event);
|
||||
|
||||
#endif /* WINPR_LIBWINPR_SYNCH_EVENT_H_ */
|
||||
99
third_party/FreeRDP/winpr/libwinpr/synch/init.c
vendored
Normal file
99
third_party/FreeRDP/winpr/libwinpr/synch/init.c
vendored
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2014 Thincast Technologies GmbH
|
||||
* Copyright 2014 Norbert Federa <norbert.federa@thincast.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/interlocked.h>
|
||||
|
||||
#include "../log.h"
|
||||
#define TAG WINPR_TAG("sync")
|
||||
|
||||
#if (!defined(_WIN32)) || (defined(_WIN32) && (_WIN32_WINNT < 0x0600))
|
||||
|
||||
BOOL winpr_InitOnceBeginInitialize(WINPR_ATTR_UNUSED LPINIT_ONCE lpInitOnce,
|
||||
WINPR_ATTR_UNUSED DWORD dwFlags,
|
||||
WINPR_ATTR_UNUSED PBOOL fPending,
|
||||
WINPR_ATTR_UNUSED LPVOID* lpContext)
|
||||
{
|
||||
WLog_ERR(TAG, "not implemented");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
BOOL winpr_InitOnceComplete(WINPR_ATTR_UNUSED LPINIT_ONCE lpInitOnce,
|
||||
WINPR_ATTR_UNUSED DWORD dwFlags, WINPR_ATTR_UNUSED LPVOID lpContext)
|
||||
{
|
||||
WLog_ERR(TAG, "not implemented");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
VOID winpr_InitOnceInitialize(WINPR_ATTR_UNUSED PINIT_ONCE InitOnce)
|
||||
{
|
||||
WLog_ERR(TAG, "not implemented");
|
||||
}
|
||||
|
||||
BOOL winpr_InitOnceExecuteOnce(PINIT_ONCE InitOnce, PINIT_ONCE_FN InitFn, PVOID Parameter,
|
||||
LPVOID* Context)
|
||||
{
|
||||
for (;;)
|
||||
{
|
||||
switch ((ULONG_PTR)InitOnce->Ptr & 3)
|
||||
{
|
||||
case 2:
|
||||
/* already completed successfully */
|
||||
return TRUE;
|
||||
|
||||
case 0:
|
||||
|
||||
/* first time */
|
||||
if (InterlockedCompareExchangePointer(&InitOnce->Ptr, (PVOID)1, (PVOID)0) !=
|
||||
(PVOID)0)
|
||||
{
|
||||
/* some other thread was faster */
|
||||
break;
|
||||
}
|
||||
|
||||
/* it's our job to call the init function */
|
||||
if (InitFn(InitOnce, Parameter, Context))
|
||||
{
|
||||
/* success */
|
||||
InitOnce->Ptr = (PVOID)2;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/* the init function returned an error, reset the status */
|
||||
InitOnce->Ptr = (PVOID)0;
|
||||
return FALSE;
|
||||
|
||||
case 1:
|
||||
/* in progress */
|
||||
break;
|
||||
|
||||
default:
|
||||
WLog_ERR(TAG, "internal error");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Sleep(5);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
233
third_party/FreeRDP/winpr/libwinpr/synch/mutex.c
vendored
Normal file
233
third_party/FreeRDP/winpr/libwinpr/synch/mutex.c
vendored
Normal file
@@ -0,0 +1,233 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/debug.h>
|
||||
#include <winpr/wlog.h>
|
||||
#include <winpr/string.h>
|
||||
|
||||
#include "synch.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <errno.h>
|
||||
|
||||
#include "../handle/handle.h"
|
||||
|
||||
#include "../log.h"
|
||||
#define TAG WINPR_TAG("sync.mutex")
|
||||
|
||||
static BOOL MutexCloseHandle(HANDLE handle);
|
||||
|
||||
static BOOL MutexIsHandled(HANDLE handle)
|
||||
{
|
||||
return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_MUTEX, FALSE);
|
||||
}
|
||||
|
||||
static int MutexGetFd(HANDLE handle)
|
||||
{
|
||||
WINPR_MUTEX* mux = (WINPR_MUTEX*)handle;
|
||||
|
||||
if (!MutexIsHandled(handle))
|
||||
return -1;
|
||||
|
||||
/* TODO: Mutex does not support file handles... */
|
||||
(void)mux;
|
||||
return -1;
|
||||
}
|
||||
|
||||
BOOL MutexCloseHandle(HANDLE handle)
|
||||
{
|
||||
WINPR_MUTEX* mutex = (WINPR_MUTEX*)handle;
|
||||
int rc = 0;
|
||||
|
||||
if (!MutexIsHandled(handle))
|
||||
return FALSE;
|
||||
|
||||
if ((rc = pthread_mutex_destroy(&mutex->mutex)))
|
||||
{
|
||||
char ebuffer[256] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "pthread_mutex_destroy failed with %s [%d]",
|
||||
winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
|
||||
#if defined(WITH_DEBUG_MUTEX)
|
||||
{
|
||||
size_t used = 0;
|
||||
void* stack = winpr_backtrace(20);
|
||||
char** msg = nullptr;
|
||||
|
||||
if (stack)
|
||||
msg = winpr_backtrace_symbols(stack, &used);
|
||||
|
||||
if (msg)
|
||||
{
|
||||
for (size_t i = 0; i < used; i++)
|
||||
WLog_ERR(TAG, "%2" PRIdz ": %s", i, msg[i]);
|
||||
}
|
||||
|
||||
free(msg);
|
||||
winpr_backtrace_free(stack);
|
||||
}
|
||||
#endif
|
||||
/**
|
||||
* Note: unfortunately we may not return FALSE here since CloseHandle(hmutex) on
|
||||
* Windows always seems to succeed independently of the mutex object locking state
|
||||
*/
|
||||
}
|
||||
|
||||
free(mutex->name);
|
||||
free(handle);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static HANDLE_OPS ops = { MutexIsHandled, MutexCloseHandle, MutexGetFd, nullptr, /* CleanupHandle */
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr, nullptr };
|
||||
|
||||
HANDLE CreateMutexW(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCWSTR lpName)
|
||||
{
|
||||
HANDLE handle = nullptr;
|
||||
char* name = nullptr;
|
||||
|
||||
if (lpName)
|
||||
{
|
||||
name = ConvertWCharToUtf8Alloc(lpName, nullptr);
|
||||
if (!name)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
handle = CreateMutexA(lpMutexAttributes, bInitialOwner, name);
|
||||
free(name);
|
||||
return handle;
|
||||
}
|
||||
|
||||
HANDLE CreateMutexA(LPSECURITY_ATTRIBUTES lpMutexAttributes, BOOL bInitialOwner, LPCSTR lpName)
|
||||
{
|
||||
HANDLE handle = nullptr;
|
||||
WINPR_MUTEX* mutex = nullptr;
|
||||
mutex = (WINPR_MUTEX*)calloc(1, sizeof(WINPR_MUTEX));
|
||||
|
||||
if (lpMutexAttributes)
|
||||
WLog_WARN(TAG, "[%s] does not support lpMutexAttributes", lpName);
|
||||
|
||||
if (mutex)
|
||||
{
|
||||
pthread_mutexattr_t attr;
|
||||
pthread_mutexattr_init(&attr);
|
||||
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
|
||||
pthread_mutex_init(&mutex->mutex, &attr);
|
||||
WINPR_HANDLE_SET_TYPE_AND_MODE(mutex, HANDLE_TYPE_MUTEX, WINPR_FD_READ);
|
||||
mutex->common.ops = &ops;
|
||||
handle = (HANDLE)mutex;
|
||||
|
||||
if (bInitialOwner)
|
||||
pthread_mutex_lock(&mutex->mutex);
|
||||
|
||||
if (lpName)
|
||||
mutex->name = strdup(lpName); /* Non runtime relevant information, skip nullptr check */
|
||||
}
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
HANDLE CreateMutexExA(LPSECURITY_ATTRIBUTES lpMutexAttributes, LPCSTR lpName, DWORD dwFlags,
|
||||
DWORD dwDesiredAccess)
|
||||
{
|
||||
BOOL initial = FALSE;
|
||||
/* TODO: support access modes */
|
||||
|
||||
if (dwDesiredAccess != 0)
|
||||
WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName,
|
||||
dwDesiredAccess);
|
||||
|
||||
if (dwFlags & CREATE_MUTEX_INITIAL_OWNER)
|
||||
initial = TRUE;
|
||||
|
||||
return CreateMutexA(lpMutexAttributes, initial, lpName);
|
||||
}
|
||||
|
||||
HANDLE CreateMutexExW(LPSECURITY_ATTRIBUTES lpMutexAttributes, LPCWSTR lpName, DWORD dwFlags,
|
||||
DWORD dwDesiredAccess)
|
||||
{
|
||||
BOOL initial = FALSE;
|
||||
|
||||
/* TODO: support access modes */
|
||||
if (dwDesiredAccess != 0)
|
||||
{
|
||||
char name[MAX_PATH] = WINPR_C_ARRAY_INIT;
|
||||
ConvertWCharToUtf8(lpName, name, sizeof(name) - 1);
|
||||
WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, name, dwDesiredAccess);
|
||||
}
|
||||
|
||||
if (dwFlags & CREATE_MUTEX_INITIAL_OWNER)
|
||||
initial = TRUE;
|
||||
|
||||
return CreateMutexW(lpMutexAttributes, initial, lpName);
|
||||
}
|
||||
|
||||
HANDLE OpenMutexA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName)
|
||||
{
|
||||
/* TODO: Implement */
|
||||
WINPR_UNUSED(dwDesiredAccess);
|
||||
WINPR_UNUSED(bInheritHandle);
|
||||
WINPR_UNUSED(lpName);
|
||||
WLog_ERR(TAG, "TODO: Implement");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HANDLE OpenMutexW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName)
|
||||
{
|
||||
/* TODO: Implement */
|
||||
WINPR_UNUSED(dwDesiredAccess);
|
||||
WINPR_UNUSED(bInheritHandle);
|
||||
WINPR_UNUSED(lpName);
|
||||
WLog_ERR(TAG, "TODO: Implement");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BOOL ReleaseMutex(HANDLE hMutex)
|
||||
{
|
||||
ULONG Type = 0;
|
||||
WINPR_HANDLE* Object = nullptr;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hMutex, &Type, &Object))
|
||||
return FALSE;
|
||||
|
||||
if (Type == HANDLE_TYPE_MUTEX)
|
||||
{
|
||||
WINPR_MUTEX* mutex = (WINPR_MUTEX*)Object;
|
||||
int rc = pthread_mutex_unlock(&mutex->mutex);
|
||||
|
||||
if (rc)
|
||||
{
|
||||
char ebuffer[256] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "pthread_mutex_unlock failed with %s [%d]",
|
||||
winpr_strerror(rc, ebuffer, sizeof(ebuffer)), rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif
|
||||
294
third_party/FreeRDP/winpr/libwinpr/synch/pollset.c
vendored
Normal file
294
third_party/FreeRDP/winpr/libwinpr/synch/pollset.c
vendored
Normal file
@@ -0,0 +1,294 @@
|
||||
#ifndef _WIN32
|
||||
#include <errno.h>
|
||||
|
||||
#include "pollset.h"
|
||||
#include <winpr/handle.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/assert.h>
|
||||
#include "../log.h"
|
||||
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
#include <emscripten.h>
|
||||
#endif
|
||||
|
||||
#define TAG WINPR_TAG("sync.pollset")
|
||||
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
static INT16 handle_mode_to_pollevent(ULONG mode)
|
||||
{
|
||||
INT16 event = 0;
|
||||
|
||||
if (mode & WINPR_FD_READ)
|
||||
event |= POLLIN;
|
||||
|
||||
if (mode & WINPR_FD_WRITE)
|
||||
event |= POLLOUT;
|
||||
|
||||
return event;
|
||||
}
|
||||
#endif
|
||||
|
||||
BOOL pollset_init(WINPR_POLL_SET* set, size_t nhandles)
|
||||
{
|
||||
WINPR_ASSERT(set);
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
if (nhandles > MAXIMUM_WAIT_OBJECTS)
|
||||
{
|
||||
set->isStatic = FALSE;
|
||||
set->pollset = calloc(nhandles, sizeof(*set->pollset));
|
||||
if (!set->pollset)
|
||||
return FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
set->pollset = set->staticSet;
|
||||
set->isStatic = TRUE;
|
||||
}
|
||||
#else
|
||||
set->fdIndex = calloc(nhandles, sizeof(*set->fdIndex));
|
||||
if (!set->fdIndex)
|
||||
return FALSE;
|
||||
|
||||
FD_ZERO(&set->rset_base);
|
||||
FD_ZERO(&set->rset);
|
||||
FD_ZERO(&set->wset_base);
|
||||
FD_ZERO(&set->wset);
|
||||
set->maxFd = 0;
|
||||
set->nread = set->nwrite = 0;
|
||||
#endif
|
||||
|
||||
set->size = nhandles;
|
||||
set->fillIndex = 0;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void pollset_uninit(WINPR_POLL_SET* set)
|
||||
{
|
||||
WINPR_ASSERT(set);
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
if (!set->isStatic)
|
||||
free(set->pollset);
|
||||
#else
|
||||
free(set->fdIndex);
|
||||
#endif
|
||||
}
|
||||
|
||||
void pollset_reset(WINPR_POLL_SET* set)
|
||||
{
|
||||
WINPR_ASSERT(set);
|
||||
#ifndef WINPR_HAVE_POLL_H
|
||||
FD_ZERO(&set->rset_base);
|
||||
FD_ZERO(&set->wset_base);
|
||||
set->maxFd = 0;
|
||||
set->nread = set->nwrite = 0;
|
||||
#endif
|
||||
set->fillIndex = 0;
|
||||
}
|
||||
|
||||
BOOL pollset_add(WINPR_POLL_SET* set, int fd, ULONG mode)
|
||||
{
|
||||
WINPR_ASSERT(set);
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
struct pollfd* item = nullptr;
|
||||
if (set->fillIndex == set->size)
|
||||
return FALSE;
|
||||
|
||||
item = &set->pollset[set->fillIndex];
|
||||
item->fd = fd;
|
||||
item->revents = 0;
|
||||
item->events = handle_mode_to_pollevent(mode);
|
||||
#else
|
||||
FdIndex* fdIndex = &set->fdIndex[set->fillIndex];
|
||||
if (mode & WINPR_FD_READ)
|
||||
{
|
||||
FD_SET(fd, &set->rset_base);
|
||||
set->nread++;
|
||||
}
|
||||
|
||||
if (mode & WINPR_FD_WRITE)
|
||||
{
|
||||
FD_SET(fd, &set->wset_base);
|
||||
set->nwrite++;
|
||||
}
|
||||
|
||||
if (fd > set->maxFd)
|
||||
set->maxFd = fd;
|
||||
|
||||
fdIndex->fd = fd;
|
||||
fdIndex->mode = mode;
|
||||
#endif
|
||||
set->fillIndex++;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int pollset_poll(WINPR_POLL_SET* set, DWORD dwMilliseconds)
|
||||
{
|
||||
WINPR_ASSERT(set);
|
||||
int ret = 0;
|
||||
UINT64 dueTime = 0;
|
||||
UINT64 now = 0;
|
||||
|
||||
now = GetTickCount64();
|
||||
if (dwMilliseconds == INFINITE)
|
||||
dueTime = 0xFFFFFFFFFFFFFFFF;
|
||||
else
|
||||
dueTime = now + dwMilliseconds;
|
||||
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
int timeout = 0;
|
||||
|
||||
do
|
||||
{
|
||||
if (dwMilliseconds == INFINITE)
|
||||
timeout = -1;
|
||||
else
|
||||
timeout = (int)(dueTime - now);
|
||||
|
||||
ret = poll(set->pollset, set->fillIndex, timeout);
|
||||
if (ret >= 0)
|
||||
{
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
/* If we have tried 10 times unsuccessfully we will yield in emscripten so pending event
|
||||
* handlers might be run */
|
||||
if (ret == 0)
|
||||
{
|
||||
if (++set->yieldCounter > 10)
|
||||
{
|
||||
emscripten_sleep(0);
|
||||
set->yieldCounter = 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
set->yieldCounter = 0;
|
||||
#endif
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (errno != EINTR)
|
||||
return -1;
|
||||
|
||||
now = GetTickCount64();
|
||||
} while (now < dueTime);
|
||||
|
||||
#else
|
||||
do
|
||||
{
|
||||
struct timeval staticTimeout;
|
||||
struct timeval* timeout;
|
||||
|
||||
fd_set* rset = nullptr;
|
||||
fd_set* wset = nullptr;
|
||||
|
||||
if (dwMilliseconds == INFINITE)
|
||||
{
|
||||
timeout = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
long waitTime = (long)(dueTime - now);
|
||||
|
||||
timeout = &staticTimeout;
|
||||
timeout->tv_sec = waitTime / 1000;
|
||||
timeout->tv_usec = (waitTime % 1000) * 1000;
|
||||
}
|
||||
|
||||
if (set->nread)
|
||||
{
|
||||
rset = &set->rset;
|
||||
memcpy(rset, &set->rset_base, sizeof(*rset));
|
||||
}
|
||||
|
||||
if (set->nwrite)
|
||||
{
|
||||
wset = &set->wset;
|
||||
memcpy(wset, &set->wset_base, sizeof(*wset));
|
||||
}
|
||||
|
||||
ret = select(set->maxFd + 1, rset, wset, nullptr, timeout);
|
||||
if (ret >= 0)
|
||||
return ret;
|
||||
|
||||
if (errno != EINTR)
|
||||
return -1;
|
||||
|
||||
now = GetTickCount64();
|
||||
|
||||
} while (now < dueTime);
|
||||
|
||||
FD_ZERO(&set->rset);
|
||||
FD_ZERO(&set->wset);
|
||||
#endif
|
||||
|
||||
return 0; /* timeout */
|
||||
}
|
||||
|
||||
BOOL pollset_isSignaled(WINPR_POLL_SET* set, size_t idx)
|
||||
{
|
||||
WINPR_ASSERT(set);
|
||||
|
||||
if (idx > set->fillIndex)
|
||||
{
|
||||
WLog_ERR(TAG, "index=%" PRIuz " out of pollset(fillIndex=%" PRIuz ")", idx, set->fillIndex);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
return !!(set->pollset[idx].revents & set->pollset[idx].events);
|
||||
#else
|
||||
FdIndex* fdIndex = &set->fdIndex[idx];
|
||||
if (fdIndex->fd < 0)
|
||||
return FALSE;
|
||||
|
||||
if ((fdIndex->mode & WINPR_FD_READ) && FD_ISSET(fdIndex->fd, &set->rset))
|
||||
return TRUE;
|
||||
|
||||
if ((fdIndex->mode & WINPR_FD_WRITE) && FD_ISSET(fdIndex->fd, &set->wset))
|
||||
return TRUE;
|
||||
|
||||
return FALSE;
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL pollset_isReadSignaled(WINPR_POLL_SET* set, size_t idx)
|
||||
{
|
||||
WINPR_ASSERT(set);
|
||||
|
||||
if (idx > set->fillIndex)
|
||||
{
|
||||
WLog_ERR(TAG, "index=%" PRIuz " out of pollset(fillIndex=%" PRIuz ")", idx, set->fillIndex);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
return !!(set->pollset[idx].revents & POLLIN);
|
||||
#else
|
||||
FdIndex* fdIndex = &set->fdIndex[idx];
|
||||
if (fdIndex->fd < 0)
|
||||
return FALSE;
|
||||
|
||||
return FD_ISSET(fdIndex->fd, &set->rset);
|
||||
#endif
|
||||
}
|
||||
|
||||
BOOL pollset_isWriteSignaled(WINPR_POLL_SET* set, size_t idx)
|
||||
{
|
||||
WINPR_ASSERT(set);
|
||||
|
||||
if (idx > set->fillIndex)
|
||||
{
|
||||
WLog_ERR(TAG, "index=%" PRIuz " out of pollset(fillIndex=%" PRIuz ")", idx, set->fillIndex);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
return !!(set->pollset[idx].revents & POLLOUT);
|
||||
#else
|
||||
FdIndex* fdIndex = &set->fdIndex[idx];
|
||||
if (fdIndex->fd < 0)
|
||||
return FALSE;
|
||||
|
||||
return FD_ISSET(fdIndex->fd, &set->wset);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
77
third_party/FreeRDP/winpr/libwinpr/synch/pollset.h
vendored
Normal file
77
third_party/FreeRDP/winpr/libwinpr/synch/pollset.h
vendored
Normal file
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* pollset
|
||||
*
|
||||
* Copyright 2021 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#ifndef WINPR_LIBWINPR_SYNCH_POLLSET_H_
|
||||
#define WINPR_LIBWINPR_SYNCH_POLLSET_H_
|
||||
|
||||
#include <winpr/wtypes.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
#include <poll.h>
|
||||
#else
|
||||
#include <sys/select.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
int fd;
|
||||
ULONG mode;
|
||||
} FdIndex;
|
||||
#endif
|
||||
|
||||
struct winpr_poll_set
|
||||
{
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
struct pollfd* pollset;
|
||||
struct pollfd staticSet[MAXIMUM_WAIT_OBJECTS];
|
||||
BOOL isStatic;
|
||||
#else
|
||||
FdIndex* fdIndex;
|
||||
fd_set rset_base;
|
||||
fd_set rset;
|
||||
fd_set wset_base;
|
||||
fd_set wset;
|
||||
int nread, nwrite;
|
||||
int maxFd;
|
||||
#endif
|
||||
size_t fillIndex;
|
||||
size_t size;
|
||||
#if defined(__EMSCRIPTEN__)
|
||||
size_t yieldCounter;
|
||||
#endif
|
||||
};
|
||||
|
||||
typedef struct winpr_poll_set WINPR_POLL_SET;
|
||||
|
||||
BOOL pollset_init(WINPR_POLL_SET* set, size_t nhandles);
|
||||
void pollset_uninit(WINPR_POLL_SET* set);
|
||||
void pollset_reset(WINPR_POLL_SET* set);
|
||||
BOOL pollset_add(WINPR_POLL_SET* set, int fd, ULONG mode);
|
||||
int pollset_poll(WINPR_POLL_SET* set, DWORD dwMilliseconds);
|
||||
|
||||
BOOL pollset_isSignaled(WINPR_POLL_SET* set, size_t idx);
|
||||
BOOL pollset_isReadSignaled(WINPR_POLL_SET* set, size_t idx);
|
||||
BOOL pollset_isWriteSignaled(WINPR_POLL_SET* set, size_t idx);
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* WINPR_LIBWINPR_SYNCH_POLLSET_H_ */
|
||||
261
third_party/FreeRDP/winpr/libwinpr/synch/semaphore.c
vendored
Normal file
261
third_party/FreeRDP/winpr/libwinpr/synch/semaphore.c
vendored
Normal file
@@ -0,0 +1,261 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
#include <winpr/debug.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
#include "synch.h"
|
||||
|
||||
#ifdef WINPR_HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <errno.h>
|
||||
#include "../handle/handle.h"
|
||||
#include "../log.h"
|
||||
#define TAG WINPR_TAG("synch.semaphore")
|
||||
|
||||
static BOOL SemaphoreCloseHandle(HANDLE handle);
|
||||
|
||||
static BOOL SemaphoreIsHandled(HANDLE handle)
|
||||
{
|
||||
return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_SEMAPHORE, FALSE);
|
||||
}
|
||||
|
||||
static int SemaphoreGetFd(HANDLE handle)
|
||||
{
|
||||
WINPR_SEMAPHORE* sem = (WINPR_SEMAPHORE*)handle;
|
||||
|
||||
if (!SemaphoreIsHandled(handle))
|
||||
return -1;
|
||||
|
||||
return sem->pipe_fd[0];
|
||||
}
|
||||
|
||||
static DWORD SemaphoreCleanupHandle(HANDLE handle)
|
||||
{
|
||||
WINPR_SEMAPHORE* sem = (WINPR_SEMAPHORE*)handle;
|
||||
|
||||
if (!SemaphoreIsHandled(handle))
|
||||
return WAIT_FAILED;
|
||||
|
||||
uint8_t val = 0;
|
||||
const SSIZE_T length = read(sem->pipe_fd[0], &val, sizeof(val));
|
||||
|
||||
if (length != 1)
|
||||
{
|
||||
char ebuffer[256] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "semaphore read() failure [%d] %s", errno,
|
||||
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
|
||||
BOOL SemaphoreCloseHandle(HANDLE handle)
|
||||
{
|
||||
WINPR_SEMAPHORE* semaphore = (WINPR_SEMAPHORE*)handle;
|
||||
|
||||
if (!SemaphoreIsHandled(handle))
|
||||
return FALSE;
|
||||
|
||||
#ifdef WINPR_PIPE_SEMAPHORE
|
||||
|
||||
if (semaphore->pipe_fd[0] != -1)
|
||||
{
|
||||
close(semaphore->pipe_fd[0]);
|
||||
semaphore->pipe_fd[0] = -1;
|
||||
|
||||
if (semaphore->pipe_fd[1] != -1)
|
||||
{
|
||||
close(semaphore->pipe_fd[1]);
|
||||
semaphore->pipe_fd[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
#if defined __APPLE__
|
||||
semaphore_destroy(mach_task_self(), *((winpr_sem_t*)semaphore->sem));
|
||||
#else
|
||||
sem_destroy((winpr_sem_t*)semaphore->sem);
|
||||
#endif
|
||||
#endif
|
||||
free(semaphore);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static HANDLE_OPS ops = { SemaphoreIsHandled,
|
||||
SemaphoreCloseHandle,
|
||||
SemaphoreGetFd,
|
||||
SemaphoreCleanupHandle,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr,
|
||||
nullptr };
|
||||
|
||||
HANDLE CreateSemaphoreW(WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpSemaphoreAttributes,
|
||||
LONG lInitialCount, WINPR_ATTR_UNUSED LONG lMaximumCount,
|
||||
WINPR_ATTR_UNUSED LPCWSTR lpName)
|
||||
{
|
||||
HANDLE handle = nullptr;
|
||||
WINPR_SEMAPHORE* semaphore = nullptr;
|
||||
semaphore = (WINPR_SEMAPHORE*)calloc(1, sizeof(WINPR_SEMAPHORE));
|
||||
|
||||
if (!semaphore)
|
||||
return nullptr;
|
||||
|
||||
semaphore->pipe_fd[0] = -1;
|
||||
semaphore->pipe_fd[1] = -1;
|
||||
semaphore->sem = (winpr_sem_t*)nullptr;
|
||||
semaphore->common.ops = &ops;
|
||||
#ifdef WINPR_PIPE_SEMAPHORE
|
||||
|
||||
if (pipe(semaphore->pipe_fd) < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create semaphore");
|
||||
free(semaphore);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
while (lInitialCount > 0)
|
||||
{
|
||||
if (write(semaphore->pipe_fd[1], "-", 1) != 1)
|
||||
{
|
||||
close(semaphore->pipe_fd[0]);
|
||||
close(semaphore->pipe_fd[1]);
|
||||
free(semaphore);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
lInitialCount--;
|
||||
}
|
||||
|
||||
#else
|
||||
semaphore->sem = (winpr_sem_t*)malloc(sizeof(winpr_sem_t));
|
||||
|
||||
if (!semaphore->sem)
|
||||
{
|
||||
WLog_ERR(TAG, "failed to allocate semaphore memory");
|
||||
free(semaphore);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#if defined __APPLE__
|
||||
|
||||
if (semaphore_create(mach_task_self(), semaphore->sem, SYNC_POLICY_FIFO, lMaximumCount) !=
|
||||
KERN_SUCCESS)
|
||||
#else
|
||||
if (sem_init(semaphore->sem, 0, lMaximumCount) == -1)
|
||||
#endif
|
||||
{
|
||||
WLog_ERR(TAG, "failed to create semaphore");
|
||||
free(semaphore->sem);
|
||||
free(semaphore);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
#endif
|
||||
WINPR_HANDLE_SET_TYPE_AND_MODE(semaphore, HANDLE_TYPE_SEMAPHORE, WINPR_FD_READ);
|
||||
handle = (HANDLE)semaphore;
|
||||
return handle;
|
||||
}
|
||||
|
||||
HANDLE CreateSemaphoreA(LPSECURITY_ATTRIBUTES lpSemaphoreAttributes, LONG lInitialCount,
|
||||
LONG lMaximumCount, WINPR_ATTR_UNUSED LPCSTR lpName)
|
||||
{
|
||||
return CreateSemaphoreW(lpSemaphoreAttributes, lInitialCount, lMaximumCount, nullptr);
|
||||
}
|
||||
|
||||
HANDLE OpenSemaphoreW(WINPR_ATTR_UNUSED DWORD dwDesiredAccess,
|
||||
WINPR_ATTR_UNUSED BOOL bInheritHandle, WINPR_ATTR_UNUSED LPCWSTR lpName)
|
||||
{
|
||||
WLog_ERR(TAG, "not implemented");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
HANDLE OpenSemaphoreA(WINPR_ATTR_UNUSED DWORD dwDesiredAccess,
|
||||
WINPR_ATTR_UNUSED BOOL bInheritHandle, WINPR_ATTR_UNUSED LPCSTR lpName)
|
||||
{
|
||||
WLog_ERR(TAG, "not implemented");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
BOOL ReleaseSemaphore(HANDLE hSemaphore, LONG lReleaseCount,
|
||||
WINPR_ATTR_UNUSED LPLONG lpPreviousCount)
|
||||
{
|
||||
ULONG Type = 0;
|
||||
WINPR_HANDLE* Object = nullptr;
|
||||
WINPR_SEMAPHORE* semaphore = nullptr;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hSemaphore, &Type, &Object))
|
||||
return FALSE;
|
||||
|
||||
if (Type == HANDLE_TYPE_SEMAPHORE)
|
||||
{
|
||||
semaphore = (WINPR_SEMAPHORE*)Object;
|
||||
#ifdef WINPR_PIPE_SEMAPHORE
|
||||
|
||||
if (semaphore->pipe_fd[0] != -1)
|
||||
{
|
||||
while (lReleaseCount > 0)
|
||||
{
|
||||
if (write(semaphore->pipe_fd[1], "-", 1) != 1)
|
||||
return FALSE;
|
||||
|
||||
lReleaseCount--;
|
||||
}
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
while (lReleaseCount > 0)
|
||||
{
|
||||
#if defined __APPLE__
|
||||
semaphore_signal(*((winpr_sem_t*)semaphore->sem));
|
||||
#else
|
||||
sem_post((winpr_sem_t*)semaphore->sem);
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
WLog_ERR(TAG, "called on a handle that is not a semaphore");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
#endif
|
||||
149
third_party/FreeRDP/winpr/libwinpr/synch/sleep.c
vendored
Normal file
149
third_party/FreeRDP/winpr/libwinpr/synch/sleep.c
vendored
Normal file
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/platform.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
|
||||
#include "../log.h"
|
||||
#include "../thread/apc.h"
|
||||
#include "../thread/thread.h"
|
||||
#include "../synch/pollset.h"
|
||||
|
||||
#define TAG WINPR_TAG("synch.sleep")
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <time.h>
|
||||
|
||||
WINPR_PRAGMA_DIAG_PUSH
|
||||
WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO
|
||||
WINPR_PRAGMA_DIAG_IGNORED_UNUSED_MACRO
|
||||
|
||||
#ifdef WINPR_HAVE_UNISTD_H
|
||||
#ifndef _XOPEN_SOURCE
|
||||
#define _XOPEN_SOURCE 500 // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
|
||||
#endif
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
WINPR_PRAGMA_DIAG_POP
|
||||
|
||||
VOID Sleep(DWORD dwMilliseconds)
|
||||
{
|
||||
usleep(dwMilliseconds * 1000);
|
||||
}
|
||||
|
||||
DWORD SleepEx(DWORD dwMilliseconds, BOOL bAlertable)
|
||||
{
|
||||
WINPR_THREAD* thread = winpr_GetCurrentThread();
|
||||
WINPR_POLL_SET pollset;
|
||||
int status = 0;
|
||||
DWORD ret = WAIT_FAILED;
|
||||
BOOL autoSignalled = 0;
|
||||
|
||||
if (thread)
|
||||
{
|
||||
/* treat re-entrancy if a completion is calling us */
|
||||
if (thread->apc.treatingCompletions)
|
||||
bAlertable = FALSE;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* called from a non WinPR thread */
|
||||
bAlertable = FALSE;
|
||||
}
|
||||
|
||||
if (!bAlertable || !thread->apc.length)
|
||||
{
|
||||
usleep(dwMilliseconds * 1000);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!pollset_init(&pollset, thread->apc.length))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to initialize pollset");
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
if (!apc_collectFds(thread, &pollset, &autoSignalled))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to APC file descriptors");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!autoSignalled)
|
||||
{
|
||||
/* we poll and wait only if no APC member is ready */
|
||||
status = pollset_poll(&pollset, dwMilliseconds);
|
||||
if (status < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "polling of apc fds failed");
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (apc_executeCompletions(thread, &pollset, 0))
|
||||
{
|
||||
ret = WAIT_IO_COMPLETION;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* according to the spec return value is 0 see
|
||||
* https://docs.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleepex*/
|
||||
ret = 0;
|
||||
}
|
||||
out:
|
||||
pollset_uninit(&pollset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
VOID USleep(DWORD dwMicroseconds)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
usleep(dwMicroseconds);
|
||||
#else
|
||||
static LARGE_INTEGER freq = WINPR_C_ARRAY_INIT;
|
||||
LARGE_INTEGER t1 = WINPR_C_ARRAY_INIT;
|
||||
LARGE_INTEGER t2 = WINPR_C_ARRAY_INIT;
|
||||
|
||||
QueryPerformanceCounter(&t1);
|
||||
|
||||
if (freq.QuadPart == 0)
|
||||
{
|
||||
QueryPerformanceFrequency(&freq);
|
||||
}
|
||||
|
||||
// in order to save cpu cycles we use Sleep() for the large share ...
|
||||
if (dwMicroseconds >= 1000)
|
||||
{
|
||||
Sleep(dwMicroseconds / 1000);
|
||||
}
|
||||
// ... and busy loop until all the requested micro seconds have passed
|
||||
do
|
||||
{
|
||||
QueryPerformanceCounter(&t2);
|
||||
} while (((t2.QuadPart - t1.QuadPart) * 1000000) / freq.QuadPart < dwMicroseconds);
|
||||
#endif
|
||||
}
|
||||
159
third_party/FreeRDP/winpr/libwinpr/synch/synch.h
vendored
Normal file
159
third_party/FreeRDP/winpr/libwinpr/synch/synch.h
vendored
Normal file
@@ -0,0 +1,159 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#ifndef WINPR_SYNCH_PRIVATE_H
|
||||
#define WINPR_SYNCH_PRIVATE_H
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/platform.h>
|
||||
|
||||
#include <winpr/synch.h>
|
||||
|
||||
#include "../handle/handle.h"
|
||||
#include "../thread/apc.h"
|
||||
#include "event.h"
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#define WINPR_PIPE_SEMAPHORE 1
|
||||
|
||||
#if defined __APPLE__
|
||||
#include <pthread.h>
|
||||
#include <sys/time.h>
|
||||
#include <semaphore.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/semaphore.h>
|
||||
#include <mach/task.h>
|
||||
#define winpr_sem_t semaphore_t
|
||||
#else
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#define winpr_sem_t sem_t
|
||||
#endif
|
||||
|
||||
struct winpr_mutex
|
||||
{
|
||||
WINPR_HANDLE common;
|
||||
char* name;
|
||||
pthread_mutex_t mutex;
|
||||
};
|
||||
typedef struct winpr_mutex WINPR_MUTEX;
|
||||
|
||||
struct winpr_semaphore
|
||||
{
|
||||
WINPR_HANDLE common;
|
||||
|
||||
int pipe_fd[2];
|
||||
winpr_sem_t* sem;
|
||||
};
|
||||
typedef struct winpr_semaphore WINPR_SEMAPHORE;
|
||||
|
||||
#ifdef WINPR_HAVE_SYS_TIMERFD_H
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/timerfd.h>
|
||||
#define TIMER_IMPL_TIMERFD
|
||||
|
||||
#elif defined(WITH_POSIX_TIMER)
|
||||
#include <fcntl.h>
|
||||
#define TIMER_IMPL_POSIX
|
||||
|
||||
#elif defined(__APPLE__)
|
||||
#define TIMER_IMPL_DISPATCH
|
||||
#include <dispatch/dispatch.h>
|
||||
#else
|
||||
#warning missing timer implementation
|
||||
#endif
|
||||
|
||||
struct winpr_timer
|
||||
{
|
||||
WINPR_HANDLE common;
|
||||
|
||||
int fd;
|
||||
BOOL bInit;
|
||||
LONG lPeriod;
|
||||
BOOL bManualReset;
|
||||
PTIMERAPCROUTINE pfnCompletionRoutine;
|
||||
LPVOID lpArgToCompletionRoutine;
|
||||
|
||||
#ifdef TIMER_IMPL_TIMERFD
|
||||
struct itimerspec timeout;
|
||||
#endif
|
||||
|
||||
#ifdef TIMER_IMPL_POSIX
|
||||
WINPR_EVENT_IMPL event;
|
||||
timer_t tid;
|
||||
struct itimerspec timeout;
|
||||
#endif
|
||||
|
||||
#ifdef TIMER_IMPL_DISPATCH
|
||||
WINPR_EVENT_IMPL event;
|
||||
dispatch_queue_t queue;
|
||||
dispatch_source_t source;
|
||||
BOOL running;
|
||||
#endif
|
||||
char* name;
|
||||
|
||||
WINPR_APC_ITEM apcItem;
|
||||
};
|
||||
typedef struct winpr_timer WINPR_TIMER;
|
||||
|
||||
typedef struct winpr_timer_queue_timer WINPR_TIMER_QUEUE_TIMER;
|
||||
|
||||
struct winpr_timer_queue
|
||||
{
|
||||
WINPR_HANDLE common;
|
||||
|
||||
pthread_t thread;
|
||||
pthread_attr_t attr;
|
||||
pthread_mutex_t mutex;
|
||||
pthread_cond_t cond;
|
||||
pthread_mutex_t cond_mutex;
|
||||
struct sched_param param;
|
||||
|
||||
BOOL bCancelled;
|
||||
WINPR_TIMER_QUEUE_TIMER* activeHead;
|
||||
WINPR_TIMER_QUEUE_TIMER* inactiveHead;
|
||||
};
|
||||
typedef struct winpr_timer_queue WINPR_TIMER_QUEUE;
|
||||
|
||||
struct winpr_timer_queue_timer
|
||||
{
|
||||
WINPR_HANDLE common;
|
||||
|
||||
ULONG Flags;
|
||||
DWORD DueTime;
|
||||
DWORD Period;
|
||||
PVOID Parameter;
|
||||
WAITORTIMERCALLBACK Callback;
|
||||
|
||||
int FireCount;
|
||||
|
||||
struct timespec StartTime;
|
||||
struct timespec ExpirationTime;
|
||||
|
||||
WINPR_TIMER_QUEUE* timerQueue;
|
||||
WINPR_TIMER_QUEUE_TIMER* next;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif /* WINPR_SYNCH_PRIVATE_H */
|
||||
40
third_party/FreeRDP/winpr/libwinpr/synch/test/CMakeLists.txt
vendored
Normal file
40
third_party/FreeRDP/winpr/libwinpr/synch/test/CMakeLists.txt
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
set(MODULE_NAME "TestSynch")
|
||||
set(MODULE_PREFIX "TEST_SYNCH")
|
||||
|
||||
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
|
||||
|
||||
set(${MODULE_PREFIX}_TESTS
|
||||
TestSynchInit.c
|
||||
TestSynchEvent.c
|
||||
TestSynchMutex.c
|
||||
TestSynchBarrier.c
|
||||
TestSynchCritical.c
|
||||
TestSynchSemaphore.c
|
||||
TestSynchThread.c
|
||||
# TestSynchMultipleThreads.c
|
||||
TestSynchTimerQueue.c
|
||||
TestSynchWaitableTimer.c
|
||||
TestSynchWaitableTimerAPC.c
|
||||
TestSynchAPC.c
|
||||
)
|
||||
|
||||
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
|
||||
|
||||
if(FREEBSD)
|
||||
include_directories(SYSTEM ${EPOLLSHIM_INCLUDE_DIR})
|
||||
endif()
|
||||
|
||||
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
||||
|
||||
target_link_libraries(${MODULE_NAME} winpr)
|
||||
|
||||
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
|
||||
|
||||
foreach(test ${${MODULE_PREFIX}_TESTS})
|
||||
get_filename_component(TestName ${test} NAME_WE)
|
||||
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
|
||||
151
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchAPC.c
vendored
Normal file
151
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchAPC.c
vendored
Normal file
@@ -0,0 +1,151 @@
|
||||
/**
|
||||
* FreeRDP: A Remote Desktop Protocol Implementation
|
||||
* TestSyncAPC
|
||||
*
|
||||
* Copyright 2021 David Fort <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
#include <winpr/wtypes.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BOOL error;
|
||||
BOOL called;
|
||||
} UserApcArg;
|
||||
|
||||
static void CALLBACK userApc(ULONG_PTR arg)
|
||||
{
|
||||
UserApcArg* userArg = (UserApcArg*)arg;
|
||||
userArg->called = TRUE;
|
||||
}
|
||||
|
||||
static DWORD WINAPI uncleanThread(LPVOID lpThreadParameter)
|
||||
{
|
||||
/* this thread post an APC that will never get executed */
|
||||
UserApcArg* userArg = (UserApcArg*)lpThreadParameter;
|
||||
if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)lpThreadParameter))
|
||||
{
|
||||
userArg->error = TRUE;
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI cleanThread(LPVOID lpThreadParameter)
|
||||
{
|
||||
Sleep(500);
|
||||
|
||||
SleepEx(500, TRUE);
|
||||
return 0;
|
||||
}
|
||||
|
||||
typedef struct
|
||||
{
|
||||
HANDLE timer1;
|
||||
DWORD timer1Calls;
|
||||
HANDLE timer2;
|
||||
DWORD timer2Calls;
|
||||
BOOL endTest;
|
||||
} UncleanCloseData;
|
||||
|
||||
static VOID CALLBACK Timer1APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
|
||||
{
|
||||
UncleanCloseData* data = (UncleanCloseData*)lpArg;
|
||||
data->timer1Calls++;
|
||||
(void)CloseHandle(data->timer2);
|
||||
data->endTest = TRUE;
|
||||
}
|
||||
|
||||
static VOID CALLBACK Timer2APCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
|
||||
{
|
||||
UncleanCloseData* data = (UncleanCloseData*)lpArg;
|
||||
data->timer2Calls++;
|
||||
}
|
||||
|
||||
static DWORD /*WINAPI*/ closeHandleTest(LPVOID lpThreadParameter)
|
||||
{
|
||||
LARGE_INTEGER dueTime = WINPR_C_ARRAY_INIT;
|
||||
UncleanCloseData* data = (UncleanCloseData*)lpThreadParameter;
|
||||
data->endTest = FALSE;
|
||||
|
||||
dueTime.QuadPart = -500;
|
||||
if (!SetWaitableTimer(data->timer1, &dueTime, 0, Timer1APCProc, lpThreadParameter, FALSE))
|
||||
return 1;
|
||||
|
||||
dueTime.QuadPart = -900;
|
||||
if (!SetWaitableTimer(data->timer2, &dueTime, 0, Timer2APCProc, lpThreadParameter, FALSE))
|
||||
return 1;
|
||||
|
||||
while (!data->endTest)
|
||||
{
|
||||
SleepEx(100, TRUE);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestSynchAPC(int argc, char* argv[])
|
||||
{
|
||||
HANDLE thread = nullptr;
|
||||
UserApcArg userApcArg;
|
||||
|
||||
userApcArg.error = FALSE;
|
||||
userApcArg.called = FALSE;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
/* first post an APC and check it is executed during a SleepEx */
|
||||
if (!QueueUserAPC((PAPCFUNC)userApc, _GetCurrentThread(), (ULONG_PTR)&userApcArg))
|
||||
return 1;
|
||||
|
||||
if (SleepEx(100, FALSE) != 0)
|
||||
return 2;
|
||||
|
||||
if (SleepEx(100, TRUE) != WAIT_IO_COMPLETION)
|
||||
return 3;
|
||||
|
||||
if (!userApcArg.called)
|
||||
return 4;
|
||||
|
||||
userApcArg.called = FALSE;
|
||||
|
||||
/* test that the APC is cleaned up even when not called */
|
||||
thread = CreateThread(nullptr, 0, uncleanThread, &userApcArg, 0, nullptr);
|
||||
if (!thread)
|
||||
return 10;
|
||||
(void)WaitForSingleObject(thread, INFINITE);
|
||||
(void)CloseHandle(thread);
|
||||
|
||||
if (userApcArg.called || userApcArg.error)
|
||||
return 11;
|
||||
|
||||
/* test a remote APC queuing */
|
||||
thread = CreateThread(nullptr, 0, cleanThread, &userApcArg, 0, nullptr);
|
||||
if (!thread)
|
||||
return 20;
|
||||
|
||||
if (!QueueUserAPC((PAPCFUNC)userApc, thread, (ULONG_PTR)&userApcArg))
|
||||
return 21;
|
||||
|
||||
(void)WaitForSingleObject(thread, INFINITE);
|
||||
(void)CloseHandle(thread);
|
||||
|
||||
if (!userApcArg.called)
|
||||
return 22;
|
||||
|
||||
return 0;
|
||||
}
|
||||
270
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchBarrier.c
vendored
Normal file
270
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchBarrier.c
vendored
Normal file
@@ -0,0 +1,270 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/interlocked.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include "../synch.h"
|
||||
|
||||
static SYNCHRONIZATION_BARRIER gBarrier;
|
||||
static HANDLE gStartEvent = nullptr;
|
||||
static LONG gErrorCount = 0;
|
||||
|
||||
#define MAX_SLEEP_MS 22
|
||||
|
||||
struct test_params
|
||||
{
|
||||
LONG threadCount;
|
||||
LONG trueCount;
|
||||
LONG falseCount;
|
||||
DWORD loops;
|
||||
DWORD flags;
|
||||
};
|
||||
|
||||
static UINT32 prand(UINT32 max)
|
||||
{
|
||||
UINT32 tmp = 0;
|
||||
if (max <= 1)
|
||||
return 1;
|
||||
if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
|
||||
{
|
||||
// NOLINTNEXTLINE(concurrency-mt-unsafe)
|
||||
exit(-1);
|
||||
}
|
||||
return tmp % (max - 1) + 1;
|
||||
}
|
||||
|
||||
static DWORD WINAPI test_synch_barrier_thread(LPVOID lpParam)
|
||||
{
|
||||
BOOL status = FALSE;
|
||||
struct test_params* p = (struct test_params*)lpParam;
|
||||
|
||||
InterlockedIncrement(&p->threadCount);
|
||||
|
||||
// printf("Thread #%03u entered.\n", tnum);
|
||||
|
||||
/* wait for start event from main */
|
||||
if (WaitForSingleObject(gStartEvent, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
goto out;
|
||||
}
|
||||
|
||||
// printf("Thread #%03u unblocked.\n", tnum);
|
||||
|
||||
for (DWORD i = 0; i < p->loops && gErrorCount == 0; i++)
|
||||
{
|
||||
/* simulate different execution times before the barrier */
|
||||
Sleep(1 + prand(MAX_SLEEP_MS));
|
||||
status = EnterSynchronizationBarrier(&gBarrier, p->flags);
|
||||
|
||||
// printf("Thread #%03u status: %s\n", tnum, status ? "TRUE" : "FALSE");
|
||||
if (status)
|
||||
InterlockedIncrement(&p->trueCount);
|
||||
else
|
||||
InterlockedIncrement(&p->falseCount);
|
||||
}
|
||||
|
||||
out:
|
||||
// printf("Thread #%03u leaving.\n", tnum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL TestSynchBarrierWithFlags(DWORD dwFlags, DWORD dwThreads, DWORD dwLoops)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
DWORD dwStatus = 0;
|
||||
|
||||
struct test_params p = {
|
||||
.threadCount = 0, .trueCount = 0, .falseCount = 0, .loops = dwLoops, .flags = dwFlags
|
||||
};
|
||||
DWORD expectedTrueCount = dwLoops;
|
||||
DWORD expectedFalseCount = dwLoops * (dwThreads - 1);
|
||||
|
||||
printf("%s: >> Testing with flags 0x%08" PRIx32 ". Using %" PRIu32
|
||||
" threads performing %" PRIu32 " loops\n",
|
||||
__func__, dwFlags, dwThreads, dwLoops);
|
||||
|
||||
HANDLE* threads = (HANDLE*)calloc(dwThreads, sizeof(HANDLE));
|
||||
if (!threads)
|
||||
{
|
||||
printf("%s: error allocatin thread array memory\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (dwThreads > INT32_MAX)
|
||||
goto fail;
|
||||
|
||||
if (!InitializeSynchronizationBarrier(&gBarrier, (LONG)dwThreads, -1))
|
||||
{
|
||||
printf("%s: InitializeSynchronizationBarrier failed. GetLastError() = 0x%08x", __func__,
|
||||
GetLastError());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(gStartEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateEvent failed with error 0x%08x", __func__, GetLastError());
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DWORD i = 0;
|
||||
for (; i < dwThreads; i++)
|
||||
{
|
||||
threads[i] = CreateThread(nullptr, 0, test_synch_barrier_thread, &p, 0, nullptr);
|
||||
if (!threads[i])
|
||||
{
|
||||
printf("%s: CreateThread failed for thread #%" PRIu32 " with error 0x%08x\n", __func__,
|
||||
i, GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i > 0)
|
||||
{
|
||||
if (!SetEvent(gStartEvent))
|
||||
{
|
||||
printf("%s: SetEvent(gStartEvent) failed with error = 0x%08x)\n", __func__,
|
||||
GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
}
|
||||
|
||||
while (i--)
|
||||
{
|
||||
if (WAIT_OBJECT_0 != (dwStatus = WaitForSingleObject(threads[i], INFINITE)))
|
||||
{
|
||||
printf("%s: WaitForSingleObject(thread[%" PRIu32 "] unexpectedly returned %" PRIu32
|
||||
" (error = 0x%08x)\n",
|
||||
__func__, i, dwStatus, GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
}
|
||||
|
||||
if (!CloseHandle(threads[i]))
|
||||
{
|
||||
printf("%s: CloseHandle(thread[%" PRIu32 "]) failed with error = 0x%08x)\n",
|
||||
__func__, i, GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!CloseHandle(gStartEvent))
|
||||
{
|
||||
printf("%s: CloseHandle(gStartEvent) failed with error = 0x%08x)\n", __func__,
|
||||
GetLastError());
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
}
|
||||
|
||||
if (p.threadCount != (INT64)dwThreads)
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
|
||||
if (p.trueCount != (INT64)expectedTrueCount)
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
|
||||
if (p.falseCount != (INT64)expectedFalseCount)
|
||||
InterlockedIncrement(&gErrorCount);
|
||||
|
||||
printf("%s: error count: %" PRId32 "\n", __func__, gErrorCount);
|
||||
printf("%s: thread count: %" PRId32 " (expected %" PRIu32 ")\n", __func__, p.threadCount,
|
||||
dwThreads);
|
||||
printf("%s: true count: %" PRId32 " (expected %" PRIu32 ")\n", __func__, p.trueCount,
|
||||
expectedTrueCount);
|
||||
printf("%s: false count: %" PRId32 " (expected %" PRIu32 ")\n", __func__, p.falseCount,
|
||||
expectedFalseCount);
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
free((void*)threads);
|
||||
DeleteSynchronizationBarrier(&gBarrier);
|
||||
if (gErrorCount > 0)
|
||||
{
|
||||
printf("%s: Error test failed with %" PRId32 " reported errors\n", __func__, gErrorCount);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TestSynchBarrier(int argc, char* argv[])
|
||||
{
|
||||
SYSTEM_INFO sysinfo;
|
||||
DWORD dwMaxThreads = 0;
|
||||
DWORD dwMinThreads = 0;
|
||||
DWORD dwNumLoops = 10;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
GetNativeSystemInfo(&sysinfo);
|
||||
printf("%s: Number of processors: %" PRIu32 "\n", __func__, sysinfo.dwNumberOfProcessors);
|
||||
dwMinThreads = sysinfo.dwNumberOfProcessors;
|
||||
dwMaxThreads = sysinfo.dwNumberOfProcessors * 4;
|
||||
|
||||
if (dwMaxThreads > 32)
|
||||
dwMaxThreads = 32;
|
||||
|
||||
/* Test invalid parameters */
|
||||
if (InitializeSynchronizationBarrier(&gBarrier, 0, -1))
|
||||
{
|
||||
(void)fprintf(
|
||||
stderr,
|
||||
"%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = 0\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (InitializeSynchronizationBarrier(&gBarrier, -1, -1))
|
||||
{
|
||||
(void)fprintf(
|
||||
stderr,
|
||||
"%s: InitializeSynchronizationBarrier unecpectedly succeeded with lTotalThreads = -1\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (InitializeSynchronizationBarrier(&gBarrier, 1, -2))
|
||||
{
|
||||
(void)fprintf(
|
||||
stderr,
|
||||
"%s: InitializeSynchronizationBarrier unecpectedly succeeded with lSpinCount = -2\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Functional tests */
|
||||
|
||||
if (!TestSynchBarrierWithFlags(0, dwMaxThreads, dwNumLoops))
|
||||
{
|
||||
(void)fprintf(
|
||||
stderr,
|
||||
"%s: TestSynchBarrierWithFlags(0) unecpectedly succeeded with lTotalThreads = -1\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY, dwMinThreads,
|
||||
dwNumLoops))
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"%s: TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_SPIN_ONLY) "
|
||||
"unecpectedly succeeded with lTotalThreads = -1\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY, dwMaxThreads,
|
||||
dwNumLoops))
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"%s: TestSynchBarrierWithFlags(SYNCHRONIZATION_BARRIER_FLAGS_BLOCK_ONLY) "
|
||||
"unecpectedly succeeded with lTotalThreads = -1\n",
|
||||
__func__);
|
||||
return -1;
|
||||
}
|
||||
|
||||
printf("%s: Test successfully completed\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
382
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchCritical.c
vendored
Normal file
382
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchCritical.c
vendored
Normal file
@@ -0,0 +1,382 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/windows.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/interlocked.h>
|
||||
|
||||
#define TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS 100
|
||||
#define TEST_SYNC_CRITICAL_TEST1_RUNS 4
|
||||
|
||||
static CRITICAL_SECTION critical;
|
||||
static LONG gTestValueVulnerable = 0;
|
||||
static LONG gTestValueSerialized = 0;
|
||||
|
||||
static BOOL TestSynchCritical_TriggerAndCheckRaceCondition(HANDLE OwningThread, LONG RecursionCount)
|
||||
{
|
||||
/* if called unprotected this will hopefully trigger a race condition ... */
|
||||
gTestValueVulnerable++;
|
||||
|
||||
if (critical.OwningThread != OwningThread)
|
||||
{
|
||||
printf("CriticalSection failure: OwningThread is invalid\n");
|
||||
return FALSE;
|
||||
}
|
||||
if (critical.RecursionCount != RecursionCount)
|
||||
{
|
||||
printf("CriticalSection failure: RecursionCount is invalid\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* ... which we try to detect using the serialized counter */
|
||||
if (gTestValueVulnerable != InterlockedIncrement(&gTestValueSerialized))
|
||||
{
|
||||
printf("CriticalSection failure: Data corruption detected\n");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static UINT32 prand(UINT32 max)
|
||||
{
|
||||
UINT32 tmp = 0;
|
||||
if (max <= 1)
|
||||
return 1;
|
||||
if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
|
||||
return 0;
|
||||
return tmp % (max - 1) + 1;
|
||||
}
|
||||
|
||||
/* this thread function shall increment the global dwTestValue until the PBOOL passed in arg is
|
||||
* FALSE */
|
||||
static DWORD WINAPI TestSynchCritical_Test1(LPVOID arg)
|
||||
{
|
||||
int rc = 0;
|
||||
HANDLE hThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
|
||||
|
||||
PBOOL pbContinueRunning = (PBOOL)arg;
|
||||
|
||||
while (*pbContinueRunning)
|
||||
{
|
||||
EnterCriticalSection(&critical);
|
||||
|
||||
rc = 1;
|
||||
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
|
||||
return 1;
|
||||
|
||||
/* add some random recursion level */
|
||||
UINT32 j = prand(5);
|
||||
for (UINT32 i = 0; i < j; i++)
|
||||
{
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc++))
|
||||
return 2;
|
||||
EnterCriticalSection(&critical);
|
||||
}
|
||||
for (UINT32 i = 0; i < j; i++)
|
||||
{
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc--))
|
||||
return 2;
|
||||
LeaveCriticalSection(&critical);
|
||||
}
|
||||
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
|
||||
return 3;
|
||||
|
||||
LeaveCriticalSection(&critical);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this thread function tries to call TryEnterCriticalSection while the main thread holds the lock
|
||||
*/
|
||||
static DWORD WINAPI TestSynchCritical_Test2(LPVOID arg)
|
||||
{
|
||||
WINPR_UNUSED(arg);
|
||||
if (TryEnterCriticalSection(&critical) == TRUE)
|
||||
{
|
||||
LeaveCriticalSection(&critical);
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static DWORD WINAPI TestSynchCritical_Main(LPVOID arg)
|
||||
{
|
||||
SYSTEM_INFO sysinfo;
|
||||
DWORD dwPreviousSpinCount = 0;
|
||||
DWORD dwSpinCount = 0;
|
||||
DWORD dwSpinCountExpected = 0;
|
||||
HANDLE hMainThread = nullptr;
|
||||
HANDLE* hThreads = nullptr;
|
||||
HANDLE hThread = nullptr;
|
||||
DWORD dwThreadCount = 0;
|
||||
DWORD dwThreadExitCode = 0;
|
||||
BOOL bTest1Running = 0;
|
||||
|
||||
PBOOL pbThreadTerminated = (PBOOL)arg;
|
||||
|
||||
GetNativeSystemInfo(&sysinfo);
|
||||
|
||||
hMainThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
|
||||
|
||||
/**
|
||||
* Test SpinCount in SetCriticalSectionSpinCount, InitializeCriticalSectionEx and
|
||||
* InitializeCriticalSectionAndSpinCount SpinCount must be forced to be zero on on uniprocessor
|
||||
* systems and on systems where WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT is defined
|
||||
*/
|
||||
|
||||
dwSpinCount = 100;
|
||||
InitializeCriticalSectionEx(&critical, dwSpinCount, 0);
|
||||
while (--dwSpinCount)
|
||||
{
|
||||
dwPreviousSpinCount = SetCriticalSectionSpinCount(&critical, dwSpinCount);
|
||||
dwSpinCountExpected = 0;
|
||||
#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
|
||||
if (sysinfo.dwNumberOfProcessors > 1)
|
||||
dwSpinCountExpected = dwSpinCount + 1;
|
||||
#endif
|
||||
if (dwPreviousSpinCount != dwSpinCountExpected)
|
||||
{
|
||||
printf("CriticalSection failure: SetCriticalSectionSpinCount returned %" PRIu32
|
||||
" (expected: %" PRIu32 ")\n",
|
||||
dwPreviousSpinCount, dwSpinCountExpected);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&critical);
|
||||
|
||||
if (dwSpinCount % 2 == 0)
|
||||
{
|
||||
if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!InitializeCriticalSectionEx(&critical, dwSpinCount, 0))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
DeleteCriticalSection(&critical);
|
||||
|
||||
/**
|
||||
* Test single-threaded recursive
|
||||
* TryEnterCriticalSection/EnterCriticalSection/LeaveCriticalSection
|
||||
*
|
||||
*/
|
||||
|
||||
InitializeCriticalSection(&critical);
|
||||
|
||||
int i = 0;
|
||||
for (; i < 10; i++)
|
||||
{
|
||||
if (critical.RecursionCount != i)
|
||||
{
|
||||
printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
|
||||
critical.RecursionCount, i);
|
||||
goto fail;
|
||||
}
|
||||
if (i % 2 == 0)
|
||||
{
|
||||
EnterCriticalSection(&critical);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (TryEnterCriticalSection(&critical) == FALSE)
|
||||
{
|
||||
printf("CriticalSection failure: TryEnterCriticalSection failed where it should "
|
||||
"not.\n");
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
if (critical.OwningThread != hMainThread)
|
||||
{
|
||||
printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
|
||||
i);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
while (--i >= 0)
|
||||
{
|
||||
LeaveCriticalSection(&critical);
|
||||
if (critical.RecursionCount != i)
|
||||
{
|
||||
printf("CriticalSection failure: RecursionCount field is %" PRId32 " instead of %d.\n",
|
||||
critical.RecursionCount, i);
|
||||
goto fail;
|
||||
}
|
||||
if (critical.OwningThread != (i ? hMainThread : nullptr))
|
||||
{
|
||||
printf("CriticalSection failure: Could not verify section ownership (loop index=%d).\n",
|
||||
i);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
DeleteCriticalSection(&critical);
|
||||
|
||||
/**
|
||||
* Test using multiple threads modifying the same value
|
||||
*/
|
||||
|
||||
dwThreadCount = sysinfo.dwNumberOfProcessors > 1 ? sysinfo.dwNumberOfProcessors : 2;
|
||||
|
||||
hThreads = (HANDLE*)calloc(dwThreadCount, sizeof(HANDLE));
|
||||
if (!hThreads)
|
||||
{
|
||||
printf("Problem allocating memory\n");
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (int j = 0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++)
|
||||
{
|
||||
dwSpinCount = j * 100;
|
||||
if (!InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount))
|
||||
goto fail;
|
||||
|
||||
gTestValueVulnerable = 0;
|
||||
gTestValueSerialized = 0;
|
||||
|
||||
/* the TestSynchCritical_Test1 threads shall run until bTest1Running is FALSE */
|
||||
bTest1Running = TRUE;
|
||||
for (int i = 0; i < (int)dwThreadCount; i++)
|
||||
{
|
||||
if (!(hThreads[i] = CreateThread(nullptr, 0, TestSynchCritical_Test1, &bTest1Running, 0,
|
||||
nullptr)))
|
||||
{
|
||||
printf("CriticalSection failure: Failed to create test_1 thread #%d\n", i);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* let it run for TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS ... */
|
||||
Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS);
|
||||
bTest1Running = FALSE;
|
||||
|
||||
for (int i = 0; i < (int)dwThreadCount; i++)
|
||||
{
|
||||
if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("CriticalSection failure: Failed to wait for thread #%d\n", i);
|
||||
goto fail;
|
||||
}
|
||||
GetExitCodeThread(hThreads[i], &dwThreadExitCode);
|
||||
if (dwThreadExitCode != 0)
|
||||
{
|
||||
printf("CriticalSection failure: Thread #%d returned error code %" PRIu32 "\n", i,
|
||||
dwThreadExitCode);
|
||||
goto fail;
|
||||
}
|
||||
(void)CloseHandle(hThreads[i]);
|
||||
}
|
||||
|
||||
if (gTestValueVulnerable != gTestValueSerialized)
|
||||
{
|
||||
printf("CriticalSection failure: unexpected test value %" PRId32 " (expected %" PRId32
|
||||
")\n",
|
||||
gTestValueVulnerable, gTestValueSerialized);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&critical);
|
||||
}
|
||||
|
||||
free((void*)hThreads);
|
||||
|
||||
/**
|
||||
* TryEnterCriticalSection in thread must fail if we hold the lock in the main thread
|
||||
*/
|
||||
|
||||
InitializeCriticalSection(&critical);
|
||||
|
||||
if (TryEnterCriticalSection(&critical) == FALSE)
|
||||
{
|
||||
printf("CriticalSection failure: TryEnterCriticalSection unexpectedly failed.\n");
|
||||
goto fail;
|
||||
}
|
||||
/* This thread tries to call TryEnterCriticalSection which must fail */
|
||||
if (!(hThread = CreateThread(nullptr, 0, TestSynchCritical_Test2, nullptr, 0, nullptr)))
|
||||
{
|
||||
printf("CriticalSection failure: Failed to create test_2 thread\n");
|
||||
goto fail;
|
||||
}
|
||||
if (WaitForSingleObject(hThread, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("CriticalSection failure: Failed to wait for thread\n");
|
||||
goto fail;
|
||||
}
|
||||
GetExitCodeThread(hThread, &dwThreadExitCode);
|
||||
if (dwThreadExitCode != 0)
|
||||
{
|
||||
printf("CriticalSection failure: Thread returned error code %" PRIu32 "\n",
|
||||
dwThreadExitCode);
|
||||
goto fail;
|
||||
}
|
||||
(void)CloseHandle(hThread);
|
||||
|
||||
*pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
|
||||
return 0;
|
||||
|
||||
fail:
|
||||
*pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
|
||||
return 1;
|
||||
}
|
||||
|
||||
int TestSynchCritical(int argc, char* argv[])
|
||||
{
|
||||
BOOL bThreadTerminated = FALSE;
|
||||
HANDLE hThread = nullptr;
|
||||
DWORD dwThreadExitCode = 0;
|
||||
DWORD dwDeadLockDetectionTimeMs = 0;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
dwDeadLockDetectionTimeMs =
|
||||
2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS;
|
||||
|
||||
printf("Deadlock will be assumed after %" PRIu32 " ms.\n", dwDeadLockDetectionTimeMs);
|
||||
|
||||
if (!(hThread =
|
||||
CreateThread(nullptr, 0, TestSynchCritical_Main, &bThreadTerminated, 0, nullptr)))
|
||||
{
|
||||
printf("CriticalSection failure: Failed to create main thread\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* We have to be able to detect dead locks in this test.
|
||||
* At the time of writing winpr's WaitForSingleObject has not implemented timeout for thread
|
||||
* wait
|
||||
*
|
||||
* Workaround checking the value of bThreadTerminated which is passed in the thread arg
|
||||
*/
|
||||
|
||||
for (DWORD i = 0; i < dwDeadLockDetectionTimeMs; i += 10)
|
||||
{
|
||||
if (bThreadTerminated)
|
||||
break;
|
||||
|
||||
Sleep(10);
|
||||
}
|
||||
|
||||
if (!bThreadTerminated)
|
||||
{
|
||||
printf("CriticalSection failure: Possible dead lock detected\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
GetExitCodeThread(hThread, &dwThreadExitCode);
|
||||
(void)CloseHandle(hThread);
|
||||
|
||||
if (dwThreadExitCode != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
94
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchEvent.c
vendored
Normal file
94
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchEvent.c
vendored
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
int TestSynchEvent(int argc, char* argv[])
|
||||
{
|
||||
HANDLE event = nullptr;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
if (ResetEvent(nullptr))
|
||||
{
|
||||
printf("ResetEvent(nullptr) unexpectedly succeeded\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (SetEvent(nullptr))
|
||||
{
|
||||
printf("SetEvent(nullptr) unexpectedly succeeded\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
event = CreateEvent(nullptr, TRUE, TRUE, nullptr);
|
||||
|
||||
if (!event)
|
||||
{
|
||||
printf("CreateEvent failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(event, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForSingleObject failure 1\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ResetEvent(event))
|
||||
{
|
||||
printf("ResetEvent failure with signaled event object\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(event, 0) != WAIT_TIMEOUT)
|
||||
{
|
||||
printf("WaitForSingleObject failure 2\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!ResetEvent(event))
|
||||
{
|
||||
/* Note: ResetEvent must also succeed if event is currently nonsignaled */
|
||||
printf("ResetEvent failure with nonsignaled event object\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!SetEvent(event))
|
||||
{
|
||||
printf("SetEvent failure with nonsignaled event object\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(event, 0) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForSingleObject failure 3\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (int i = 0; i < 10000; i++)
|
||||
{
|
||||
if (!SetEvent(event))
|
||||
{
|
||||
printf("SetEvent failure with signaled event object (i = %d)\n", i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ResetEvent(event))
|
||||
{
|
||||
printf("ResetEvent failure after multiple SetEvent calls\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Independent of the amount of the previous SetEvent calls, a single
|
||||
ResetEvent must be sufficient to get into nonsignaled state */
|
||||
|
||||
if (WaitForSingleObject(event, 0) != WAIT_TIMEOUT)
|
||||
{
|
||||
printf("WaitForSingleObject failure 4\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)CloseHandle(event);
|
||||
|
||||
return 0;
|
||||
}
|
||||
167
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchInit.c
vendored
Normal file
167
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchInit.c
vendored
Normal file
@@ -0,0 +1,167 @@
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/interlocked.h>
|
||||
|
||||
#define TEST_NUM_THREADS 100
|
||||
#define TEST_NUM_FAILURES 10
|
||||
|
||||
static INIT_ONCE initOnceTest = INIT_ONCE_STATIC_INIT;
|
||||
|
||||
static HANDLE hStartEvent = nullptr;
|
||||
static LONG* pErrors = nullptr;
|
||||
static LONG* pTestThreadFunctionCalls = nullptr;
|
||||
static LONG* pTestOnceFunctionCalls = nullptr;
|
||||
static LONG* pInitOnceExecuteOnceCalls = nullptr;
|
||||
|
||||
static UINT32 prand(UINT32 max)
|
||||
{
|
||||
UINT32 tmp = 0;
|
||||
if (max <= 1)
|
||||
return 1;
|
||||
if (winpr_RAND(&tmp, sizeof(tmp)) < 0)
|
||||
return 0;
|
||||
return tmp % (max - 1) + 1;
|
||||
}
|
||||
|
||||
static BOOL CALLBACK TestOnceFunction(PINIT_ONCE once, PVOID param, PVOID* context)
|
||||
{
|
||||
LONG calls = InterlockedIncrement(pTestOnceFunctionCalls) - 1;
|
||||
|
||||
WINPR_UNUSED(once);
|
||||
WINPR_UNUSED(param);
|
||||
WINPR_UNUSED(context);
|
||||
|
||||
/* simulate execution time */
|
||||
Sleep(30 + prand(40));
|
||||
|
||||
if (calls < TEST_NUM_FAILURES)
|
||||
{
|
||||
/* simulated error */
|
||||
return FALSE;
|
||||
}
|
||||
if (calls == TEST_NUM_FAILURES)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
(void)fprintf(stderr, "%s: error: called again after success\n", __func__);
|
||||
InterlockedIncrement(pErrors);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static DWORD WINAPI TestThreadFunction(LPVOID lpParam)
|
||||
{
|
||||
LONG calls = 0;
|
||||
BOOL ok = 0;
|
||||
|
||||
WINPR_UNUSED(lpParam);
|
||||
|
||||
InterlockedIncrement(pTestThreadFunctionCalls);
|
||||
if (WaitForSingleObject(hStartEvent, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: error: failed to wait for start event\n", __func__);
|
||||
InterlockedIncrement(pErrors);
|
||||
return 0;
|
||||
}
|
||||
|
||||
ok = InitOnceExecuteOnce(&initOnceTest, TestOnceFunction, nullptr, nullptr);
|
||||
calls = InterlockedIncrement(pInitOnceExecuteOnceCalls);
|
||||
if (!ok && calls > TEST_NUM_FAILURES)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: InitOnceExecuteOnce failed unexpectedly\n", __func__);
|
||||
InterlockedIncrement(pErrors);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestSynchInit(int argc, char* argv[])
|
||||
{
|
||||
HANDLE hThreads[TEST_NUM_THREADS];
|
||||
DWORD dwCreatedThreads = 0;
|
||||
BOOL result = FALSE;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
pErrors = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
pTestThreadFunctionCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
pTestOnceFunctionCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
pInitOnceExecuteOnceCalls = winpr_aligned_malloc(sizeof(LONG), sizeof(LONG));
|
||||
|
||||
if (!pErrors || !pTestThreadFunctionCalls || !pTestOnceFunctionCalls ||
|
||||
!pInitOnceExecuteOnceCalls)
|
||||
{
|
||||
(void)fprintf(stderr, "error: _aligned_malloc failed\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
*pErrors = 0;
|
||||
*pTestThreadFunctionCalls = 0;
|
||||
*pTestOnceFunctionCalls = 0;
|
||||
*pInitOnceExecuteOnceCalls = 0;
|
||||
|
||||
if (!(hStartEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
(void)fprintf(stderr, "error creating start event\n");
|
||||
InterlockedIncrement(pErrors);
|
||||
goto out;
|
||||
}
|
||||
|
||||
for (DWORD i = 0; i < TEST_NUM_THREADS; i++)
|
||||
{
|
||||
if (!(hThreads[i] = CreateThread(nullptr, 0, TestThreadFunction, nullptr, 0, nullptr)))
|
||||
{
|
||||
(void)fprintf(stderr, "error creating thread #%" PRIu32 "\n", i);
|
||||
InterlockedIncrement(pErrors);
|
||||
goto out;
|
||||
}
|
||||
dwCreatedThreads++;
|
||||
}
|
||||
|
||||
Sleep(100);
|
||||
(void)SetEvent(hStartEvent);
|
||||
|
||||
for (DWORD i = 0; i < dwCreatedThreads; i++)
|
||||
{
|
||||
if (WaitForSingleObject(hThreads[i], INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "error: error waiting for thread #%" PRIu32 "\n", i);
|
||||
InterlockedIncrement(pErrors);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
if (*pErrors == 0 && *pTestThreadFunctionCalls == TEST_NUM_THREADS &&
|
||||
*pInitOnceExecuteOnceCalls == TEST_NUM_THREADS &&
|
||||
*pTestOnceFunctionCalls == TEST_NUM_FAILURES + 1)
|
||||
{
|
||||
result = TRUE;
|
||||
}
|
||||
|
||||
out:
|
||||
(void)fprintf(stderr, "Test result: %s\n", result ? "OK" : "ERROR");
|
||||
(void)fprintf(stderr, "Error count: %" PRId32 "\n", pErrors ? *pErrors : -1);
|
||||
(void)fprintf(stderr, "Threads created: %" PRIu32 "\n", dwCreatedThreads);
|
||||
(void)fprintf(stderr, "TestThreadFunctionCalls: %" PRId32 "\n",
|
||||
pTestThreadFunctionCalls ? *pTestThreadFunctionCalls : -1);
|
||||
(void)fprintf(stderr, "InitOnceExecuteOnceCalls: %" PRId32 "\n",
|
||||
pInitOnceExecuteOnceCalls ? *pInitOnceExecuteOnceCalls : -1);
|
||||
(void)fprintf(stderr, "TestOnceFunctionCalls: %" PRId32 "\n",
|
||||
pTestOnceFunctionCalls ? *pTestOnceFunctionCalls : -1);
|
||||
|
||||
winpr_aligned_free(pErrors);
|
||||
winpr_aligned_free(pTestThreadFunctionCalls);
|
||||
winpr_aligned_free(pTestOnceFunctionCalls);
|
||||
winpr_aligned_free(pInitOnceExecuteOnceCalls);
|
||||
|
||||
(void)CloseHandle(hStartEvent);
|
||||
|
||||
for (DWORD i = 0; i < dwCreatedThreads; i++)
|
||||
{
|
||||
(void)CloseHandle(hThreads[i]);
|
||||
}
|
||||
|
||||
return (result ? 0 : 1);
|
||||
}
|
||||
255
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c
vendored
Normal file
255
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchMultipleThreads.c
vendored
Normal file
@@ -0,0 +1,255 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#define THREADS 8
|
||||
|
||||
static UINT32 prand(UINT32 max)
|
||||
{
|
||||
UINT32 tmp = 0;
|
||||
if (max <= 1)
|
||||
return 1;
|
||||
winpr_RAND(&tmp, sizeof(tmp));
|
||||
return tmp % (max - 1) + 1;
|
||||
}
|
||||
|
||||
static DWORD WINAPI test_thread(LPVOID arg)
|
||||
{
|
||||
UINT32 timeout = 50 + prand(100);
|
||||
WINPR_UNUSED(arg);
|
||||
Sleep(timeout);
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int start_threads(size_t count, HANDLE* threads)
|
||||
{
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
threads[i] = CreateThread(nullptr, 0, test_thread, nullptr, CREATE_SUSPENDED, nullptr);
|
||||
|
||||
if (!threads[i])
|
||||
{
|
||||
(void)fprintf(stderr, "%s: CreateThread [%" PRIuz "] failure\n", __func__, i);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
ResumeThread(threads[i]);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int close_threads(DWORD count, HANDLE* threads)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
for (DWORD i = 0; i < count; i++)
|
||||
{
|
||||
if (!threads[i])
|
||||
continue;
|
||||
|
||||
if (!CloseHandle(threads[i]))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: CloseHandle [%" PRIu32 "] failure\n", __func__, i);
|
||||
rc = -1;
|
||||
}
|
||||
threads[i] = nullptr;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestWaitForAll(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, 10);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, timeout 10 failed, ret=%d\n",
|
||||
__func__, ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestWaitOne(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, FALSE, INFINITE);
|
||||
if (ret > (WAIT_OBJECT_0 + ARRAYSIZE(threads)))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestWaitOneTimeout(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, FALSE, 1);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects timeout 50 failed, ret=%d\n", __func__,
|
||||
ret);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestWaitOneTimeoutMultijoin(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < ARRAYSIZE(threads); i++)
|
||||
{
|
||||
const DWORD ret = WaitForMultipleObjects(ARRAYSIZE(threads), threads, FALSE, 0);
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects timeout 0 failed, ret=%d\n", __func__,
|
||||
ret);
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(ARRAYSIZE(threads), threads, TRUE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: WaitForMultipleObjects bWaitAll, INFINITE failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static BOOL TestDetach(void)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
HANDLE threads[THREADS] = WINPR_C_ARRAY_INIT;
|
||||
/* WaitForAll, timeout */
|
||||
if (start_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: start_threads failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
if (close_threads(ARRAYSIZE(threads), threads))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: close_threads failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int TestSynchMultipleThreads(int argc, char* argv[])
|
||||
{
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
if (!TestWaitForAll())
|
||||
return -1;
|
||||
|
||||
if (!TestWaitOne())
|
||||
return -2;
|
||||
|
||||
if (!TestWaitOneTimeout())
|
||||
return -3;
|
||||
|
||||
if (!TestWaitOneTimeoutMultijoin())
|
||||
return -4;
|
||||
|
||||
if (!TestDetach())
|
||||
return -5;
|
||||
|
||||
return 0;
|
||||
}
|
||||
258
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchMutex.c
vendored
Normal file
258
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchMutex.c
vendored
Normal file
@@ -0,0 +1,258 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
static BOOL test_mutex_basic(void)
|
||||
{
|
||||
HANDLE mutex = nullptr;
|
||||
DWORD rc = 0;
|
||||
|
||||
if (!(mutex = CreateMutex(nullptr, FALSE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateMutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
rc = WaitForSingleObject(mutex, INFINITE);
|
||||
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("%s: WaitForSingleObject on mutex failed with %" PRIu32 "\n", __func__, rc);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!ReleaseMutex(mutex))
|
||||
{
|
||||
printf("%s: ReleaseMutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ReleaseMutex(mutex))
|
||||
{
|
||||
printf("%s: ReleaseMutex unexpectedly succeeded on released mutex\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!CloseHandle(mutex))
|
||||
{
|
||||
printf("%s: CloseHandle on mutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL test_mutex_recursive(void)
|
||||
{
|
||||
HANDLE mutex = nullptr;
|
||||
DWORD rc = 0;
|
||||
DWORD cnt = 50;
|
||||
|
||||
if (!(mutex = CreateMutex(nullptr, TRUE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateMutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
for (UINT32 i = 0; i < cnt; i++)
|
||||
{
|
||||
rc = WaitForSingleObject(mutex, INFINITE);
|
||||
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("%s: WaitForSingleObject #%" PRIu32 " on mutex failed with %" PRIu32 "\n",
|
||||
__func__, i, rc);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
for (UINT32 i = 0; i < cnt; i++)
|
||||
{
|
||||
if (!ReleaseMutex(mutex))
|
||||
{
|
||||
printf("%s: ReleaseMutex #%" PRIu32 " failed\n", __func__, i);
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!ReleaseMutex(mutex))
|
||||
{
|
||||
/* Note: The mutex was initially owned ! */
|
||||
printf("%s: Final ReleaseMutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (ReleaseMutex(mutex))
|
||||
{
|
||||
printf("%s: ReleaseMutex unexpectedly succeeded on released mutex\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
if (!CloseHandle(mutex))
|
||||
{
|
||||
printf("%s: CloseHandle on mutex failed\n", __func__);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static HANDLE thread1_mutex1 = nullptr;
|
||||
static HANDLE thread1_mutex2 = nullptr;
|
||||
static BOOL thread1_failed = TRUE;
|
||||
|
||||
static DWORD WINAPI test_mutex_thread1(LPVOID lpParam)
|
||||
{
|
||||
HANDLE hStartEvent = (HANDLE)lpParam;
|
||||
DWORD rc = 0;
|
||||
|
||||
if (WaitForSingleObject(hStartEvent, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: failed to wait for start event\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* at this point:
|
||||
* thread1_mutex1 is expected to be locked
|
||||
* thread1_mutex2 is expected to be unlocked
|
||||
* defined task:
|
||||
* try to lock thread1_mutex1 (expected to fail)
|
||||
* lock and unlock thread1_mutex2 (expected to work)
|
||||
*/
|
||||
rc = WaitForSingleObject(thread1_mutex1, 10);
|
||||
|
||||
if (rc != WAIT_TIMEOUT)
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"%s: WaitForSingleObject on thread1_mutex1 unexpectedly returned %" PRIu32
|
||||
" instead of WAIT_TIMEOUT (%u)\n",
|
||||
__func__, rc, WAIT_TIMEOUT);
|
||||
return 0;
|
||||
}
|
||||
|
||||
rc = WaitForSingleObject(thread1_mutex2, 10);
|
||||
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr,
|
||||
"%s: WaitForSingleObject on thread1_mutex2 unexpectedly returned %" PRIu32
|
||||
" instead of WAIT_OBJECT_0\n",
|
||||
__func__, rc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (!ReleaseMutex(thread1_mutex2))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: ReleaseMutex failed on thread1_mutex2\n", __func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
thread1_failed = FALSE;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BOOL test_mutex_threading(void)
|
||||
{
|
||||
HANDLE hThread = nullptr;
|
||||
HANDLE hStartEvent = nullptr;
|
||||
|
||||
if (!(thread1_mutex1 = CreateMutex(nullptr, TRUE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateMutex thread1_mutex1 failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(thread1_mutex2 = CreateMutex(nullptr, FALSE, nullptr)))
|
||||
{
|
||||
printf("%s: CreateMutex thread1_mutex2 failed\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!(hStartEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: error creating start event\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
thread1_failed = TRUE;
|
||||
|
||||
if (!(hThread = CreateThread(nullptr, 0, test_mutex_thread1, (LPVOID)hStartEvent, 0, nullptr)))
|
||||
{
|
||||
(void)fprintf(stderr, "%s: error creating test_mutex_thread_1\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
Sleep(100);
|
||||
|
||||
if (!thread1_failed)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: thread1 premature success\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
(void)SetEvent(hStartEvent);
|
||||
|
||||
if (WaitForSingleObject(hThread, 2000) != WAIT_OBJECT_0)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: thread1 premature success\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (thread1_failed)
|
||||
{
|
||||
(void)fprintf(stderr, "%s: thread1 has not reported success\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
/**
|
||||
* - thread1 must not have succeeded to lock thread1_mutex1
|
||||
* - thread1 must have locked and unlocked thread1_mutex2
|
||||
*/
|
||||
|
||||
if (!ReleaseMutex(thread1_mutex1))
|
||||
{
|
||||
printf("%s: ReleaseMutex unexpectedly failed on thread1_mutex1\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (ReleaseMutex(thread1_mutex2))
|
||||
{
|
||||
printf("%s: ReleaseMutex unexpectedly succeeded on thread1_mutex2\n", __func__);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
(void)CloseHandle(hThread);
|
||||
(void)CloseHandle(hStartEvent);
|
||||
(void)CloseHandle(thread1_mutex1);
|
||||
(void)CloseHandle(thread1_mutex2);
|
||||
return TRUE;
|
||||
fail:
|
||||
(void)ReleaseMutex(thread1_mutex1);
|
||||
(void)ReleaseMutex(thread1_mutex2);
|
||||
(void)CloseHandle(thread1_mutex1);
|
||||
(void)CloseHandle(thread1_mutex2);
|
||||
(void)CloseHandle(hStartEvent);
|
||||
(void)CloseHandle(hThread);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
int TestSynchMutex(int argc, char* argv[])
|
||||
{
|
||||
int rc = 0;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
if (!test_mutex_basic())
|
||||
rc += 1;
|
||||
|
||||
if (!test_mutex_recursive())
|
||||
rc += 2;
|
||||
|
||||
if (!test_mutex_threading())
|
||||
rc += 4;
|
||||
|
||||
printf("TestSynchMutex result %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
21
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchSemaphore.c
vendored
Normal file
21
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchSemaphore.c
vendored
Normal file
@@ -0,0 +1,21 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
int TestSynchSemaphore(int argc, char* argv[])
|
||||
{
|
||||
HANDLE semaphore = nullptr;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
semaphore = CreateSemaphore(nullptr, 0, 1, nullptr);
|
||||
|
||||
if (!semaphore)
|
||||
{
|
||||
printf("CreateSemaphore failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
(void)CloseHandle(semaphore);
|
||||
|
||||
return 0;
|
||||
}
|
||||
131
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchThread.c
vendored
Normal file
131
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchThread.c
vendored
Normal file
@@ -0,0 +1,131 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
static DWORD WINAPI test_thread(LPVOID arg)
|
||||
{
|
||||
WINPR_UNUSED(arg);
|
||||
Sleep(100);
|
||||
ExitThread(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestSynchThread(int argc, char* argv[])
|
||||
{
|
||||
DWORD rc = 0;
|
||||
HANDLE thread = nullptr;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
thread = CreateThread(nullptr, 0, test_thread, nullptr, 0, nullptr);
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
printf("CreateThread failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TryJoin should now fail. */
|
||||
rc = WaitForSingleObject(thread, 0);
|
||||
|
||||
if (WAIT_TIMEOUT != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on running thread failed with %" PRIu32 "\n", rc);
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Join the thread */
|
||||
rc = WaitForSingleObject(thread, INFINITE);
|
||||
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("WaitForSingleObject on thread failed with %" PRIu32 "\n", rc);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* TimedJoin should now succeed. */
|
||||
rc = WaitForSingleObject(thread, 0);
|
||||
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on dead thread failed with %" PRIu32 "\n", rc);
|
||||
return -5;
|
||||
}
|
||||
|
||||
/* check that WaitForSingleObject works multiple times on a terminated thread */
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
rc = WaitForSingleObject(thread, 0);
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on dead thread failed with %" PRIu32 "\n", rc);
|
||||
return -6;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CloseHandle(thread))
|
||||
{
|
||||
printf("CloseHandle failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
thread = CreateThread(nullptr, 0, test_thread, nullptr, 0, nullptr);
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
printf("CreateThread failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* TryJoin should now fail. */
|
||||
rc = WaitForSingleObject(thread, 10);
|
||||
|
||||
if (WAIT_TIMEOUT != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on running thread failed with %" PRIu32 "\n", rc);
|
||||
return -3;
|
||||
}
|
||||
|
||||
/* Join the thread */
|
||||
rc = WaitForSingleObject(thread, INFINITE);
|
||||
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("WaitForSingleObject on thread failed with %" PRIu32 "\n", rc);
|
||||
return -2;
|
||||
}
|
||||
|
||||
/* TimedJoin should now succeed. */
|
||||
rc = WaitForSingleObject(thread, 0);
|
||||
|
||||
if (WAIT_OBJECT_0 != rc)
|
||||
{
|
||||
printf("Timed WaitForSingleObject on dead thread failed with %" PRIu32 "\n", rc);
|
||||
return -5;
|
||||
}
|
||||
|
||||
if (!CloseHandle(thread))
|
||||
{
|
||||
printf("CloseHandle failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Thread detach test */
|
||||
thread = CreateThread(nullptr, 0, test_thread, nullptr, 0, nullptr);
|
||||
|
||||
if (!thread)
|
||||
{
|
||||
printf("CreateThread failure\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!CloseHandle(thread))
|
||||
{
|
||||
printf("CloseHandle failed!");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
120
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchTimerQueue.c
vendored
Normal file
120
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchTimerQueue.c
vendored
Normal file
@@ -0,0 +1,120 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
#define FIRE_COUNT 5
|
||||
#define TIMER_COUNT 5
|
||||
|
||||
struct apc_data
|
||||
{
|
||||
DWORD TimerId;
|
||||
DWORD FireCount;
|
||||
DWORD DueTime;
|
||||
DWORD Period;
|
||||
UINT32 StartTime;
|
||||
DWORD MaxFireCount;
|
||||
HANDLE CompletionEvent;
|
||||
};
|
||||
typedef struct apc_data APC_DATA;
|
||||
|
||||
static VOID CALLBACK TimerRoutine(PVOID lpParam, BOOLEAN TimerOrWaitFired)
|
||||
{
|
||||
WINPR_UNUSED(TimerOrWaitFired);
|
||||
|
||||
if (!lpParam)
|
||||
return;
|
||||
|
||||
APC_DATA* apcData = (APC_DATA*)lpParam;
|
||||
const UINT32 CurrentTime = GetTickCount();
|
||||
const INT64 TimerTime = 1ll * CurrentTime - apcData->StartTime;
|
||||
const INT64 expectedTime =
|
||||
1ll * apcData->DueTime + (1ll * apcData->Period * apcData->FireCount);
|
||||
|
||||
apcData->FireCount++;
|
||||
|
||||
printf("TimerRoutine: TimerId: %" PRIu32 " FireCount: %" PRIu32 " ActualTime: %" PRId64
|
||||
" ExpectedTime: %" PRId64 " Discrepancy: %" PRId64 "\n",
|
||||
apcData->TimerId, apcData->FireCount, TimerTime, expectedTime, TimerTime - expectedTime);
|
||||
|
||||
Sleep(11);
|
||||
|
||||
if (apcData->FireCount == apcData->MaxFireCount)
|
||||
{
|
||||
(void)SetEvent(apcData->CompletionEvent);
|
||||
}
|
||||
}
|
||||
|
||||
int TestSynchTimerQueue(int argc, char* argv[])
|
||||
{
|
||||
HANDLE hTimers[TIMER_COUNT] = WINPR_C_ARRAY_INIT;
|
||||
APC_DATA apcData[TIMER_COUNT] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
|
||||
HANDLE hTimerQueue = CreateTimerQueue();
|
||||
|
||||
if (!hTimerQueue)
|
||||
{
|
||||
printf("CreateTimerQueue failed (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
for (DWORD index = 0; index < TIMER_COUNT; index++)
|
||||
{
|
||||
apcData[index].TimerId = index;
|
||||
apcData[index].StartTime = GetTickCount();
|
||||
apcData[index].DueTime = (index * 10) + 50;
|
||||
apcData[index].Period = 100;
|
||||
apcData[index].FireCount = 0;
|
||||
apcData[index].MaxFireCount = FIRE_COUNT;
|
||||
|
||||
if (!(apcData[index].CompletionEvent = CreateEvent(nullptr, TRUE, FALSE, nullptr)))
|
||||
{
|
||||
printf("Failed to create apcData[%" PRIu32 "] event (%" PRIu32 ")\n", index,
|
||||
GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!CreateTimerQueueTimer(&hTimers[index], hTimerQueue, TimerRoutine, &apcData[index],
|
||||
apcData[index].DueTime, apcData[index].Period, 0))
|
||||
{
|
||||
printf("CreateTimerQueueTimer failed (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (DWORD index = 0; index < TIMER_COUNT; index++)
|
||||
{
|
||||
if (WaitForSingleObject(apcData[index].CompletionEvent, 2000) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("Failed to wait for timer queue timer #%" PRIu32 " (%" PRIu32 ")\n", index,
|
||||
GetLastError());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (DWORD index = 0; index < TIMER_COUNT; index++)
|
||||
{
|
||||
/**
|
||||
* Note: If the CompletionEvent parameter is INVALID_HANDLE_VALUE, the function waits
|
||||
* for any running timer callback functions to complete before returning.
|
||||
*/
|
||||
if (!DeleteTimerQueueTimer(hTimerQueue, hTimers[index], INVALID_HANDLE_VALUE))
|
||||
{
|
||||
printf("DeleteTimerQueueTimer failed (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
(void)CloseHandle(apcData[index].CompletionEvent);
|
||||
}
|
||||
|
||||
if (!DeleteTimerQueue(hTimerQueue))
|
||||
{
|
||||
printf("DeleteTimerQueue failed (%" PRIu32 ")\n", GetLastError());
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
83
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchWaitableTimer.c
vendored
Normal file
83
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchWaitableTimer.c
vendored
Normal file
@@ -0,0 +1,83 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
|
||||
int TestSynchWaitableTimer(int argc, char* argv[])
|
||||
{
|
||||
DWORD status = 0;
|
||||
HANDLE timer = nullptr;
|
||||
LONG period = 0;
|
||||
LARGE_INTEGER due = WINPR_C_ARRAY_INIT;
|
||||
int result = -1;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
timer = CreateWaitableTimer(nullptr, FALSE, nullptr);
|
||||
|
||||
if (!timer)
|
||||
{
|
||||
printf("CreateWaitableTimer failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
due.QuadPart = -1500000LL; /* 0.15 seconds */
|
||||
|
||||
if (!SetWaitableTimer(timer, &due, 0, nullptr, nullptr, 0))
|
||||
{
|
||||
printf("SetWaitableTimer failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = WaitForSingleObject(timer, INFINITE);
|
||||
|
||||
if (status != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForSingleObject(timer, INFINITE) failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Timer Signaled\n");
|
||||
status = WaitForSingleObject(timer, 200);
|
||||
|
||||
if (status != WAIT_TIMEOUT)
|
||||
{
|
||||
printf("WaitForSingleObject(timer, 200) failure: Actual: 0x%08" PRIX32
|
||||
", Expected: 0x%08X\n",
|
||||
status, WAIT_TIMEOUT);
|
||||
goto out;
|
||||
}
|
||||
|
||||
due.QuadPart = 0;
|
||||
period = 120; /* 0.12 seconds */
|
||||
|
||||
if (!SetWaitableTimer(timer, &due, period, nullptr, nullptr, 0))
|
||||
{
|
||||
printf("SetWaitableTimer failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WaitForSingleObject(timer, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForSingleObject(timer, INFINITE) failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Timer Signaled\n");
|
||||
|
||||
if (!SetWaitableTimer(timer, &due, period, nullptr, nullptr, 0))
|
||||
{
|
||||
printf("SetWaitableTimer failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (WaitForMultipleObjects(1, &timer, FALSE, INFINITE) != WAIT_OBJECT_0)
|
||||
{
|
||||
printf("WaitForMultipleObjects(timer, INFINITE) failure\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("Timer Signaled\n");
|
||||
result = 0;
|
||||
out:
|
||||
(void)CloseHandle(timer);
|
||||
return result;
|
||||
}
|
||||
92
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c
vendored
Normal file
92
third_party/FreeRDP/winpr/libwinpr/synch/test/TestSynchWaitableTimerAPC.c
vendored
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
static int g_Count = 0;
|
||||
static HANDLE g_Event = nullptr;
|
||||
|
||||
struct apc_data
|
||||
{
|
||||
UINT32 StartTime;
|
||||
};
|
||||
typedef struct apc_data APC_DATA;
|
||||
|
||||
static VOID CALLBACK TimerAPCProc(LPVOID lpArg, DWORD dwTimerLowValue, DWORD dwTimerHighValue)
|
||||
{
|
||||
APC_DATA* apcData = nullptr;
|
||||
UINT32 CurrentTime = GetTickCount();
|
||||
WINPR_UNUSED(dwTimerLowValue);
|
||||
WINPR_UNUSED(dwTimerHighValue);
|
||||
|
||||
if (!lpArg)
|
||||
return;
|
||||
|
||||
apcData = (APC_DATA*)lpArg;
|
||||
printf("TimerAPCProc: time: %" PRIu32 "\n", CurrentTime - apcData->StartTime);
|
||||
g_Count++;
|
||||
|
||||
if (g_Count >= 5)
|
||||
{
|
||||
(void)SetEvent(g_Event);
|
||||
}
|
||||
}
|
||||
|
||||
int TestSynchWaitableTimerAPC(int argc, char* argv[])
|
||||
{
|
||||
int status = -1;
|
||||
DWORD rc = 0;
|
||||
HANDLE hTimer = nullptr;
|
||||
BOOL bSuccess = 0;
|
||||
LARGE_INTEGER due = WINPR_C_ARRAY_INIT;
|
||||
APC_DATA apcData = WINPR_C_ARRAY_INIT;
|
||||
WINPR_UNUSED(argc);
|
||||
WINPR_UNUSED(argv);
|
||||
g_Event = CreateEvent(nullptr, TRUE, FALSE, nullptr);
|
||||
|
||||
if (!g_Event)
|
||||
{
|
||||
printf("Failed to create event\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
hTimer = CreateWaitableTimer(nullptr, FALSE, nullptr);
|
||||
if (!hTimer)
|
||||
goto cleanup;
|
||||
|
||||
due.QuadPart = -1000 * 100LL; /* 0.1 seconds */
|
||||
apcData.StartTime = GetTickCount();
|
||||
bSuccess = SetWaitableTimer(hTimer, &due, 10, TimerAPCProc, &apcData, FALSE);
|
||||
|
||||
if (!bSuccess)
|
||||
goto cleanup;
|
||||
|
||||
/* nothing shall happen after 0.12 second, because thread is not in alertable state */
|
||||
rc = WaitForSingleObject(g_Event, 120);
|
||||
if (rc != WAIT_TIMEOUT)
|
||||
goto cleanup;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
rc = WaitForSingleObjectEx(g_Event, INFINITE, TRUE);
|
||||
if (rc == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if (rc == WAIT_IO_COMPLETION)
|
||||
continue;
|
||||
|
||||
printf("Failed to wait for completion event (%" PRIu32 ")\n", GetLastError());
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
status = 0;
|
||||
cleanup:
|
||||
|
||||
if (hTimer)
|
||||
(void)CloseHandle(hTimer);
|
||||
|
||||
if (g_Event)
|
||||
(void)CloseHandle(g_Event);
|
||||
|
||||
return status;
|
||||
}
|
||||
1079
third_party/FreeRDP/winpr/libwinpr/synch/timer.c
vendored
Normal file
1079
third_party/FreeRDP/winpr/libwinpr/synch/timer.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
538
third_party/FreeRDP/winpr/libwinpr/synch/wait.c
vendored
Normal file
538
third_party/FreeRDP/winpr/libwinpr/synch/wait.c
vendored
Normal file
@@ -0,0 +1,538 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2014 Hardening <contact@hardening-consulting.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#ifdef WINPR_HAVE_UNISTD_H
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#include <winpr/assert.h>
|
||||
#include <errno.h>
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/platform.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include "synch.h"
|
||||
#include "pollset.h"
|
||||
#include "../thread/thread.h"
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/debug.h>
|
||||
|
||||
#include "../log.h"
|
||||
#define TAG WINPR_TAG("sync.wait")
|
||||
|
||||
/**
|
||||
* WaitForSingleObject
|
||||
* WaitForSingleObjectEx
|
||||
* WaitForMultipleObjectsEx
|
||||
* SignalObjectAndWait
|
||||
*/
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <time.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
|
||||
#include "../handle/handle.h"
|
||||
|
||||
#include "../pipe/pipe.h"
|
||||
|
||||
static struct timespec ts_from_ns(void)
|
||||
{
|
||||
const UINT64 ns = winpr_GetUnixTimeNS();
|
||||
struct timespec timeout = WINPR_C_ARRAY_INIT;
|
||||
timeout.tv_sec = WINPR_TIME_NS_TO_S(ns);
|
||||
timeout.tv_nsec = WINPR_TIME_NS_REM_NS(ns);
|
||||
return timeout;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drop in replacement for pthread_mutex_timedlock
|
||||
* http://code.google.com/p/android/issues/detail?id=7807
|
||||
* http://aleksmaus.blogspot.ca/2011/12/missing-pthreadmutextimedlock-on.html
|
||||
*/
|
||||
#if !defined(WINPR_HAVE_PTHREAD_MUTEX_TIMEDLOCK)
|
||||
#include <pthread.h>
|
||||
|
||||
static long long ts_difftime(const struct timespec* o, const struct timespec* n)
|
||||
{
|
||||
long long oldValue = o->tv_sec * 1000000000LL + o->tv_nsec;
|
||||
long long newValue = n->tv_sec * 1000000000LL + n->tv_nsec;
|
||||
return newValue - oldValue;
|
||||
}
|
||||
|
||||
#ifdef ANDROID
|
||||
#if (__ANDROID_API__ >= 21)
|
||||
#define CONST_NEEDED const
|
||||
#else
|
||||
#define CONST_NEEDED
|
||||
#endif
|
||||
#define STATIC_NEEDED
|
||||
#else /* ANDROID */
|
||||
#define CONST_NEEDED const
|
||||
#define STATIC_NEEDED static
|
||||
#endif
|
||||
|
||||
STATIC_NEEDED int pthread_mutex_timedlock(pthread_mutex_t* mutex,
|
||||
CONST_NEEDED struct timespec* timeout)
|
||||
{
|
||||
struct timespec timenow = WINPR_C_ARRAY_INIT;
|
||||
struct timespec sleepytime = WINPR_C_ARRAY_INIT;
|
||||
unsigned long long diff = 0;
|
||||
int retcode = -1;
|
||||
/* This is just to avoid a completely busy wait */
|
||||
timenow = ts_from_ns();
|
||||
diff = ts_difftime(&timenow, timeout);
|
||||
sleepytime.tv_sec = diff / 1000000000LL;
|
||||
sleepytime.tv_nsec = diff % 1000000000LL;
|
||||
|
||||
while ((retcode = pthread_mutex_trylock(mutex)) == EBUSY)
|
||||
{
|
||||
timenow = ts_from_ns();
|
||||
|
||||
if (ts_difftime(timeout, &timenow) >= 0)
|
||||
{
|
||||
return ETIMEDOUT;
|
||||
}
|
||||
|
||||
nanosleep(&sleepytime, nullptr);
|
||||
}
|
||||
|
||||
return retcode;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ts_add_ms(struct timespec* ts, DWORD dwMilliseconds)
|
||||
{
|
||||
ts->tv_sec += dwMilliseconds / 1000L;
|
||||
ts->tv_nsec += (dwMilliseconds % 1000L) * 1000000L;
|
||||
ts->tv_sec += ts->tv_nsec / 1000000000L;
|
||||
ts->tv_nsec = ts->tv_nsec % 1000000000L;
|
||||
}
|
||||
|
||||
DWORD WaitForSingleObjectEx(HANDLE hHandle, DWORD dwMilliseconds, BOOL bAlertable)
|
||||
{
|
||||
ULONG Type = 0;
|
||||
WINPR_HANDLE* Object = nullptr;
|
||||
WINPR_POLL_SET pollset = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hHandle, &Type, &Object))
|
||||
{
|
||||
WLog_ERR(TAG, "invalid hHandle.");
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
if (Type == HANDLE_TYPE_PROCESS && winpr_Handle_getFd(hHandle) == -1)
|
||||
{
|
||||
/* note: if we have pidfd support (under linux and we have managed to associate a
|
||||
* pidfd with our process), we use the regular method with pollset below.
|
||||
* If not (on other platforms) we do a waitpid */
|
||||
WINPR_PROCESS* process = (WINPR_PROCESS*)Object;
|
||||
|
||||
do
|
||||
{
|
||||
DWORD status = 0;
|
||||
DWORD waitDelay = 0;
|
||||
int ret = waitpid(process->pid, &(process->status), WNOHANG);
|
||||
if (ret == process->pid)
|
||||
{
|
||||
process->dwExitCode = (DWORD)process->status;
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
else if (ret < 0)
|
||||
{
|
||||
char ebuffer[256] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "waitpid failure [%d] %s", errno,
|
||||
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
/* sleep by slices of 50ms */
|
||||
waitDelay = (dwMilliseconds < 50) ? dwMilliseconds : 50;
|
||||
|
||||
status = SleepEx(waitDelay, bAlertable);
|
||||
if (status != 0)
|
||||
return status;
|
||||
|
||||
dwMilliseconds -= waitDelay;
|
||||
|
||||
} while (dwMilliseconds > 50);
|
||||
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
|
||||
if (Type == HANDLE_TYPE_MUTEX)
|
||||
{
|
||||
WINPR_MUTEX* mutex = (WINPR_MUTEX*)Object;
|
||||
|
||||
if (dwMilliseconds != INFINITE)
|
||||
{
|
||||
int status = 0;
|
||||
struct timespec timeout = ts_from_ns();
|
||||
|
||||
ts_add_ms(&timeout, dwMilliseconds);
|
||||
status = pthread_mutex_timedlock(&mutex->mutex, &timeout);
|
||||
|
||||
if (ETIMEDOUT == status)
|
||||
return WAIT_TIMEOUT;
|
||||
}
|
||||
else
|
||||
{
|
||||
pthread_mutex_lock(&mutex->mutex);
|
||||
}
|
||||
|
||||
return WAIT_OBJECT_0;
|
||||
}
|
||||
else
|
||||
{
|
||||
int status = -1;
|
||||
WINPR_THREAD* thread = nullptr;
|
||||
BOOL isSet = FALSE;
|
||||
size_t extraFds = 0;
|
||||
DWORD ret = 0;
|
||||
BOOL autoSignaled = FALSE;
|
||||
|
||||
if (bAlertable)
|
||||
{
|
||||
thread = (WINPR_THREAD*)_GetCurrentThread();
|
||||
if (thread)
|
||||
{
|
||||
/* treat reentrancy, we can't switch to alertable state when we're already
|
||||
treating completions */
|
||||
if (thread->apc.treatingCompletions)
|
||||
bAlertable = FALSE;
|
||||
else
|
||||
extraFds = thread->apc.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* called from a non WinPR thread */
|
||||
bAlertable = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
int fd = winpr_Handle_getFd(Object);
|
||||
if (fd < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "winpr_Handle_getFd did not return a fd!");
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
if (!pollset_init(&pollset, 1 + extraFds))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to initialize pollset");
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
if (!pollset_add(&pollset, fd, Object->Mode))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to add fd in pollset");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to collect APC fds");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!autoSignaled)
|
||||
{
|
||||
status = pollset_poll(&pollset, dwMilliseconds);
|
||||
if (status < 0)
|
||||
{
|
||||
char ebuffer[256] = WINPR_C_ARRAY_INIT;
|
||||
WLog_ERR(TAG, "pollset_poll() failure [%d] %s", errno,
|
||||
winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
ret = WAIT_TIMEOUT;
|
||||
if (bAlertable && apc_executeCompletions(thread, &pollset, 1))
|
||||
ret = WAIT_IO_COMPLETION;
|
||||
|
||||
isSet = pollset_isSignaled(&pollset, 0);
|
||||
pollset_uninit(&pollset);
|
||||
|
||||
if (!isSet)
|
||||
return ret;
|
||||
|
||||
return winpr_Handle_cleanup(Object);
|
||||
}
|
||||
|
||||
out:
|
||||
pollset_uninit(&pollset);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
|
||||
{
|
||||
return WaitForSingleObjectEx(hHandle, dwMilliseconds, FALSE);
|
||||
}
|
||||
|
||||
DWORD WaitForMultipleObjectsEx(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll,
|
||||
DWORD dwMilliseconds, BOOL bAlertable)
|
||||
{
|
||||
DWORD signalled = 0;
|
||||
DWORD polled = 0;
|
||||
DWORD poll_map[MAXIMUM_WAIT_OBJECTS] = WINPR_C_ARRAY_INIT;
|
||||
BOOL signalled_handles[MAXIMUM_WAIT_OBJECTS] = { FALSE };
|
||||
int fd = -1;
|
||||
int status = -1;
|
||||
ULONG Type = 0;
|
||||
WINPR_HANDLE* Object = nullptr;
|
||||
WINPR_THREAD* thread = nullptr;
|
||||
WINPR_POLL_SET pollset = WINPR_C_ARRAY_INIT;
|
||||
DWORD ret = WAIT_FAILED;
|
||||
size_t extraFds = 0;
|
||||
UINT64 now = 0;
|
||||
UINT64 dueTime = 0;
|
||||
|
||||
if (!nCount || (nCount > MAXIMUM_WAIT_OBJECTS))
|
||||
{
|
||||
WLog_ERR(TAG, "invalid handles count(%" PRIu32 ")", nCount);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
if (bAlertable)
|
||||
{
|
||||
thread = winpr_GetCurrentThread();
|
||||
if (thread)
|
||||
{
|
||||
/* treat reentrancy, we can't switch to alertable state when we're already
|
||||
treating completions */
|
||||
if (thread->apc.treatingCompletions)
|
||||
bAlertable = FALSE;
|
||||
else
|
||||
extraFds = thread->apc.length;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* most probably we're not called from WinPR thread, so we can't have any APC */
|
||||
bAlertable = FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
if (!pollset_init(&pollset, nCount + extraFds))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to initialize pollset for nCount=%" PRIu32 " extraCount=%" PRIuz "",
|
||||
nCount, extraFds);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
signalled = 0;
|
||||
|
||||
now = GetTickCount64();
|
||||
if (dwMilliseconds != INFINITE)
|
||||
dueTime = now + dwMilliseconds;
|
||||
else
|
||||
dueTime = 0xFFFFFFFFFFFFFFFF;
|
||||
|
||||
do
|
||||
{
|
||||
BOOL autoSignaled = FALSE;
|
||||
polled = 0;
|
||||
|
||||
/* first collect file descriptors to poll */
|
||||
DWORD idx = 0;
|
||||
for (; idx < nCount; idx++)
|
||||
{
|
||||
if (bWaitAll)
|
||||
{
|
||||
if (signalled_handles[idx])
|
||||
continue;
|
||||
|
||||
poll_map[polled] = idx;
|
||||
}
|
||||
|
||||
if (!winpr_Handle_GetInfo(lpHandles[idx], &Type, &Object))
|
||||
{
|
||||
WLog_ERR(TAG, "invalid event file descriptor at %" PRIu32, idx);
|
||||
winpr_log_backtrace(TAG, WLOG_ERROR, 20);
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
fd = winpr_Handle_getFd(Object);
|
||||
if (fd == -1)
|
||||
{
|
||||
WLog_ERR(TAG, "invalid file descriptor at %" PRIu32, idx);
|
||||
winpr_log_backtrace(TAG, WLOG_ERROR, 20);
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (!pollset_add(&pollset, fd, Object->Mode))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to register fd in pollset at %" PRIu32, idx);
|
||||
winpr_log_backtrace(TAG, WLOG_ERROR, 20);
|
||||
SetLastError(ERROR_INVALID_HANDLE);
|
||||
goto out;
|
||||
}
|
||||
|
||||
polled++;
|
||||
}
|
||||
|
||||
/* treat file descriptors of the APC if needed */
|
||||
if (bAlertable && !apc_collectFds(thread, &pollset, &autoSignaled))
|
||||
{
|
||||
WLog_ERR(TAG, "unable to register APC fds");
|
||||
winpr_log_backtrace(TAG, WLOG_ERROR, 20);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* poll file descriptors */
|
||||
status = 0;
|
||||
if (!autoSignaled)
|
||||
{
|
||||
DWORD waitTime = 0;
|
||||
|
||||
if (dwMilliseconds == INFINITE)
|
||||
waitTime = INFINITE;
|
||||
else
|
||||
waitTime = (DWORD)(dueTime - now);
|
||||
|
||||
status = pollset_poll(&pollset, waitTime);
|
||||
if (status < 0)
|
||||
{
|
||||
char ebuffer[256] = WINPR_C_ARRAY_INIT;
|
||||
#ifdef WINPR_HAVE_POLL_H
|
||||
WLog_ERR(TAG, "poll() handle %" PRIu32 " (%" PRIu32 ") failure [%d] %s", idx,
|
||||
nCount, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
|
||||
#else
|
||||
WLog_ERR(TAG, "select() handle %" PRIu32 " (%" PRIu32 ") failure [%d] %s", idx,
|
||||
nCount, errno, winpr_strerror(errno, ebuffer, sizeof(ebuffer)));
|
||||
#endif
|
||||
winpr_log_backtrace(TAG, WLOG_ERROR, 20);
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/* give priority to the APC queue, to return WAIT_IO_COMPLETION */
|
||||
if (bAlertable && apc_executeCompletions(thread, &pollset, polled))
|
||||
{
|
||||
ret = WAIT_IO_COMPLETION;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* then treat pollset */
|
||||
if (status)
|
||||
{
|
||||
for (DWORD index = 0; index < polled; index++)
|
||||
{
|
||||
DWORD handlesIndex = 0;
|
||||
BOOL signal_set = FALSE;
|
||||
|
||||
if (bWaitAll)
|
||||
handlesIndex = poll_map[index];
|
||||
else
|
||||
handlesIndex = index;
|
||||
|
||||
signal_set = pollset_isSignaled(&pollset, index);
|
||||
if (signal_set)
|
||||
{
|
||||
DWORD rc = winpr_Handle_cleanup(lpHandles[handlesIndex]);
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
{
|
||||
WLog_ERR(TAG, "error in cleanup function for handle at index=%" PRIu32,
|
||||
handlesIndex);
|
||||
ret = rc;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (bWaitAll)
|
||||
{
|
||||
signalled_handles[handlesIndex] = TRUE;
|
||||
|
||||
/* Continue checks from last position. */
|
||||
for (; signalled < nCount; signalled++)
|
||||
{
|
||||
if (!signalled_handles[signalled])
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ret = (WAIT_OBJECT_0 + handlesIndex);
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (signalled >= nCount)
|
||||
{
|
||||
ret = WAIT_OBJECT_0;
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bAlertable && thread->apc.length > extraFds)
|
||||
{
|
||||
pollset_uninit(&pollset);
|
||||
extraFds = thread->apc.length;
|
||||
if (!pollset_init(&pollset, nCount + extraFds))
|
||||
{
|
||||
WLog_ERR(TAG, "unable reallocate pollset");
|
||||
SetLastError(ERROR_INTERNAL_ERROR);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
}
|
||||
else
|
||||
pollset_reset(&pollset);
|
||||
|
||||
now = GetTickCount64();
|
||||
} while (now < dueTime);
|
||||
|
||||
ret = WAIT_TIMEOUT;
|
||||
|
||||
out:
|
||||
pollset_uninit(&pollset);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE* lpHandles, BOOL bWaitAll,
|
||||
DWORD dwMilliseconds)
|
||||
{
|
||||
return WaitForMultipleObjectsEx(nCount, lpHandles, bWaitAll, dwMilliseconds, FALSE);
|
||||
}
|
||||
|
||||
DWORD SignalObjectAndWait(HANDLE hObjectToSignal, HANDLE hObjectToWaitOn, DWORD dwMilliseconds,
|
||||
BOOL bAlertable)
|
||||
{
|
||||
if (!SetEvent(hObjectToSignal))
|
||||
return WAIT_FAILED;
|
||||
|
||||
return WaitForSingleObjectEx(hObjectToWaitOn, dwMilliseconds, bAlertable);
|
||||
}
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user