Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View 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()

View 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}"
)

View 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

View 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

View 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

View 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

View 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_ */

View 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

View 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

View 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

View 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_ */

View 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

View 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
}

View 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 */

View 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")

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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);
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

File diff suppressed because it is too large Load Diff

View 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