mirror of
https://github.com/morgan9e/FreeRDP
synced 2026-04-15 00:44:19 +09:00
freerdp: merge with master
This commit is contained in:
@@ -227,6 +227,8 @@ RFX_CONTEXT* rfx_context_new(void)
|
||||
|
||||
if (context->priv->MaxThreadCount)
|
||||
SetThreadpoolThreadMaximum(context->priv->ThreadPool, context->priv->MaxThreadCount);
|
||||
|
||||
context->priv->EncoderStreamPool = StreamPool_New(TRUE, 64*64*3+19);
|
||||
}
|
||||
|
||||
/* initialize the default pixel format */
|
||||
@@ -261,6 +263,7 @@ void rfx_context_free(RFX_CONTEXT* context)
|
||||
{
|
||||
CloseThreadpool(context->priv->ThreadPool);
|
||||
DestroyThreadpoolEnvironment(&context->priv->ThreadPoolEnv);
|
||||
StreamPool_Free(context->priv->EncoderStreamPool);
|
||||
#ifdef WITH_PROFILER
|
||||
fprintf(stderr, "\nWARNING: Profiling results probably unusable with multithreaded RemoteFX codec!\n");
|
||||
#endif
|
||||
@@ -582,17 +585,17 @@ static BOOL rfx_process_message_tile(RFX_CONTEXT* context, RFX_TILE* tile, wStre
|
||||
tile->data, 64 * 4);
|
||||
}
|
||||
|
||||
struct _RFX_TILE_WORK_PARAM
|
||||
struct _RFX_TILE_PROCESS_WORK_PARAM
|
||||
{
|
||||
wStream s;
|
||||
RFX_TILE* tile;
|
||||
RFX_CONTEXT* context;
|
||||
};
|
||||
typedef struct _RFX_TILE_WORK_PARAM RFX_TILE_WORK_PARAM;
|
||||
typedef struct _RFX_TILE_PROCESS_WORK_PARAM RFX_TILE_PROCESS_WORK_PARAM;
|
||||
|
||||
void CALLBACK rfx_process_message_tile_work_callback(PTP_CALLBACK_INSTANCE instance, void* context, PTP_WORK work)
|
||||
{
|
||||
RFX_TILE_WORK_PARAM* param = (RFX_TILE_WORK_PARAM*) context;
|
||||
RFX_TILE_PROCESS_WORK_PARAM* param = (RFX_TILE_PROCESS_WORK_PARAM*) context;
|
||||
rfx_process_message_tile(param->context, param->tile, &(param->s));
|
||||
}
|
||||
|
||||
@@ -607,7 +610,7 @@ static BOOL rfx_process_message_tileset(RFX_CONTEXT* context, RFX_MESSAGE* messa
|
||||
UINT32 blockType;
|
||||
UINT32 tilesDataSize;
|
||||
PTP_WORK* work_objects = NULL;
|
||||
RFX_TILE_WORK_PARAM* params = NULL;
|
||||
RFX_TILE_PROCESS_WORK_PARAM* params = NULL;
|
||||
|
||||
if (Stream_GetRemainingLength(s) < 14)
|
||||
{
|
||||
@@ -691,7 +694,7 @@ static BOOL rfx_process_message_tileset(RFX_CONTEXT* context, RFX_MESSAGE* messa
|
||||
if (context->priv->UseThreads)
|
||||
{
|
||||
work_objects = (PTP_WORK*) malloc(sizeof(PTP_WORK) * message->num_tiles);
|
||||
params = (RFX_TILE_WORK_PARAM*) malloc(sizeof(RFX_TILE_WORK_PARAM) * message->num_tiles);
|
||||
params = (RFX_TILE_PROCESS_WORK_PARAM*) malloc(sizeof(RFX_TILE_PROCESS_WORK_PARAM) * message->num_tiles);
|
||||
}
|
||||
|
||||
/* tiles */
|
||||
@@ -1043,6 +1046,33 @@ static void rfx_compose_message_tile(RFX_CONTEXT* context, wStream* s,
|
||||
Stream_SetPosition(s, end_pos);
|
||||
}
|
||||
|
||||
|
||||
struct _RFX_TILE_COMPOSE_WORK_PARAM
|
||||
{
|
||||
RFX_CONTEXT* context;
|
||||
wStream *s;
|
||||
BYTE* tile_data;
|
||||
int tile_width;
|
||||
int tile_height;
|
||||
int rowstride;
|
||||
UINT32* quantVals;
|
||||
int quantIdxY;
|
||||
int quantIdxCb;
|
||||
int quantIdxCr;
|
||||
int xIdx;
|
||||
int yIdx;
|
||||
};
|
||||
typedef struct _RFX_TILE_COMPOSE_WORK_PARAM RFX_TILE_COMPOSE_WORK_PARAM;
|
||||
|
||||
void CALLBACK rfx_compose_message_tile_work_callback(PTP_CALLBACK_INSTANCE instance, void* context, PTP_WORK work)
|
||||
{
|
||||
RFX_TILE_COMPOSE_WORK_PARAM* param = (RFX_TILE_COMPOSE_WORK_PARAM*) context;
|
||||
|
||||
rfx_compose_message_tile(param->context, param->s,
|
||||
param->tile_data, param->tile_width, param->tile_height, param->rowstride,
|
||||
param->quantVals, param->quantIdxY, param->quantIdxCb, param->quantIdxCr, param->xIdx, param->yIdx);
|
||||
}
|
||||
|
||||
static void rfx_compose_message_tileset(RFX_CONTEXT* context, wStream* s,
|
||||
BYTE* image_data, int width, int height, int rowstride)
|
||||
{
|
||||
@@ -1061,6 +1091,11 @@ static void rfx_compose_message_tileset(RFX_CONTEXT* context, wStream* s,
|
||||
int xIdx;
|
||||
int yIdx;
|
||||
int tilesDataSize;
|
||||
BYTE* tileData;
|
||||
int tileWidth;
|
||||
int tileHeight;
|
||||
PTP_WORK* work_objects = NULL;
|
||||
RFX_TILE_COMPOSE_WORK_PARAM* params = NULL;
|
||||
|
||||
if (context->num_quants == 0)
|
||||
{
|
||||
@@ -1109,17 +1144,64 @@ static void rfx_compose_message_tileset(RFX_CONTEXT* context, wStream* s,
|
||||
DEBUG_RFX("width:%d height:%d rowstride:%d", width, height, rowstride);
|
||||
|
||||
end_pos = Stream_GetPosition(s);
|
||||
|
||||
if (context->priv->UseThreads)
|
||||
{
|
||||
work_objects = (PTP_WORK*) malloc(sizeof(PTP_WORK) * numTiles);
|
||||
params = (RFX_TILE_COMPOSE_WORK_PARAM*) malloc(sizeof(RFX_TILE_COMPOSE_WORK_PARAM) * numTiles);
|
||||
}
|
||||
|
||||
for (yIdx = 0; yIdx < numTilesY; yIdx++)
|
||||
{
|
||||
for (xIdx = 0; xIdx < numTilesX; xIdx++)
|
||||
{
|
||||
rfx_compose_message_tile(context, s,
|
||||
image_data + yIdx * 64 * rowstride + xIdx * 8 * context->bits_per_pixel,
|
||||
(xIdx < numTilesX - 1) ? 64 : width - xIdx * 64,
|
||||
(yIdx < numTilesY - 1) ? 64 : height - yIdx * 64,
|
||||
rowstride, quantVals, quantIdxY, quantIdxCb, quantIdxCr, xIdx, yIdx);
|
||||
tileData = image_data + yIdx * 64 * rowstride + xIdx * 8 * context->bits_per_pixel;
|
||||
tileWidth = (xIdx < numTilesX - 1) ? 64 : width - xIdx * 64;
|
||||
tileHeight = (yIdx < numTilesY - 1) ? 64 : height - yIdx * 64;
|
||||
|
||||
if (context->priv->UseThreads)
|
||||
{
|
||||
i = yIdx * numTilesX + xIdx;
|
||||
|
||||
params[i].context = context;
|
||||
params[i].s = StreamPool_Take(context->priv->EncoderStreamPool, 0);
|
||||
params[i].tile_data = tileData;
|
||||
params[i].tile_width = tileWidth;
|
||||
params[i].tile_height = tileHeight;
|
||||
params[i].rowstride = rowstride;
|
||||
params[i].quantVals = (UINT32*)quantVals;
|
||||
params[i].quantIdxY = quantIdxY;
|
||||
params[i].quantIdxCb = quantIdxCb;
|
||||
params[i].quantIdxCr = quantIdxCr;
|
||||
params[i].xIdx = xIdx;
|
||||
params[i].yIdx = yIdx;
|
||||
|
||||
work_objects[i] = CreateThreadpoolWork((PTP_WORK_CALLBACK) rfx_compose_message_tile_work_callback,
|
||||
(void*) ¶ms[i], &context->priv->ThreadPoolEnv);
|
||||
|
||||
SubmitThreadpoolWork(work_objects[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
rfx_compose_message_tile(context, s, tileData, tileWidth, tileHeight,
|
||||
rowstride, quantVals, quantIdxY, quantIdxCb, quantIdxCr, xIdx, yIdx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (context->priv->UseThreads)
|
||||
{
|
||||
for (i = 0; i < numTiles; i++)
|
||||
{
|
||||
WaitForThreadpoolWorkCallbacks(work_objects[i], FALSE);
|
||||
CloseThreadpoolWork(work_objects[i]);
|
||||
Stream_Write(s, Stream_Buffer(params[i].s), Stream_GetPosition(params[i].s));
|
||||
StreamPool_Return(context->priv->EncoderStreamPool, params[i].s);
|
||||
}
|
||||
free(work_objects);
|
||||
free(params);
|
||||
}
|
||||
|
||||
tilesDataSize = Stream_GetPosition(s) - end_pos;
|
||||
size += tilesDataSize;
|
||||
end_pos = Stream_GetPosition(s);
|
||||
|
||||
@@ -49,6 +49,7 @@ struct _RFX_CONTEXT_PRIV
|
||||
TP_CALLBACK_ENVIRON ThreadPoolEnv;
|
||||
|
||||
wBufferPool* BufferPool;
|
||||
wStreamPool* EncoderStreamPool;
|
||||
|
||||
/* profilers */
|
||||
PROFILER_DEFINE(prof_rfx_decode_rgb);
|
||||
|
||||
@@ -856,6 +856,8 @@ BOOL fastpath_send_update_pdu(rdpFastPath* fastpath, BYTE updateCode, wStream* s
|
||||
comp_flags = FASTPATH_OUTPUT_COMPRESSION_USED;
|
||||
header_bytes = 7 + sec_bytes;
|
||||
bm = (BYTE*) (rdp->mppc_enc->outputBuffer - header_bytes);
|
||||
if (comp_update)
|
||||
Stream_Free(comp_update, FALSE);
|
||||
comp_update = Stream_New(bm, pdu_data_bytes + header_bytes);
|
||||
ls = comp_update;
|
||||
}
|
||||
@@ -902,6 +904,8 @@ BOOL fastpath_send_update_pdu(rdpFastPath* fastpath, BYTE updateCode, wStream* s
|
||||
|
||||
Stream_Write_UINT16(ls, pdu_data_bytes);
|
||||
|
||||
if (update)
|
||||
Stream_Free(update, FALSE);
|
||||
update = Stream_New(bm, pduLength);
|
||||
Stream_Seek(update, pduLength);
|
||||
|
||||
|
||||
@@ -139,14 +139,22 @@ typedef RTL_CONDITION_VARIABLE CONDITION_VARIABLE, *PCONDITION_VARIABLE;
|
||||
|
||||
/* Critical Section */
|
||||
|
||||
#if defined(__linux__)
|
||||
/**
|
||||
* Linux NPTL thread synchronization primitives are implemented using
|
||||
* the futex system calls ... we can't beat futex with a spin loop.
|
||||
*/
|
||||
#define WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT
|
||||
#endif
|
||||
|
||||
typedef struct _RTL_CRITICAL_SECTION
|
||||
{
|
||||
void* DebugInfo;
|
||||
PVOID DebugInfo;
|
||||
LONG LockCount;
|
||||
LONG RecursionCount;
|
||||
PVOID OwningThread;
|
||||
PVOID LockSemaphore;
|
||||
ULONG SpinCount;
|
||||
HANDLE OwningThread;
|
||||
HANDLE LockSemaphore;
|
||||
ULONG_PTR SpinCount;
|
||||
} RTL_CRITICAL_SECTION, *PRTL_CRITICAL_SECTION;
|
||||
|
||||
typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;
|
||||
|
||||
@@ -34,7 +34,7 @@ set_target_properties(${MODULE_NAME} PROPERTIES VERSION ${WINPR_VERSION_FULL} SO
|
||||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL
|
||||
MODULE winpr
|
||||
MODULES winpr-crt winpr-handle winpr-synch)
|
||||
MODULES winpr-crt winpr-handle)
|
||||
|
||||
if(MONOLITHIC_BUILD)
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ endif()
|
||||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD} INTERNAL
|
||||
MODULE winpr
|
||||
MODULES winpr-handle)
|
||||
MODULES winpr-handle winpr-interlocked winpr-thread)
|
||||
|
||||
if(MONOLITHIC_BUILD)
|
||||
set(WINPR_LIBS ${WINPR_LIBS} ${${MODULE_PREFIX}_LIBS} PARENT_SCOPE)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
* Synchronization Functions
|
||||
*
|
||||
* Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
* Copyright 2013 Norbert Federa <norbert.federa@thinstuff.com>
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,6 +23,9 @@
|
||||
#endif
|
||||
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/interlocked.h>
|
||||
#include <winpr/thread.h>
|
||||
|
||||
#include "synch.h"
|
||||
|
||||
@@ -31,84 +35,201 @@
|
||||
|
||||
#ifndef _WIN32
|
||||
|
||||
/**
|
||||
* TODO: EnterCriticalSection allows recursive calls from the same thread.
|
||||
* Implement this using pthreads (see PTHREAD_MUTEX_RECURSIVE_NP)
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/ms682608%28v=vs.85%29.aspx
|
||||
*/
|
||||
|
||||
VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
if (lpCriticalSection)
|
||||
{
|
||||
lpCriticalSection->DebugInfo = NULL;
|
||||
|
||||
lpCriticalSection->LockCount = 0;
|
||||
lpCriticalSection->RecursionCount = 0;
|
||||
lpCriticalSection->SpinCount = 0;
|
||||
|
||||
lpCriticalSection->OwningThread = NULL;
|
||||
lpCriticalSection->LockSemaphore = NULL;
|
||||
|
||||
lpCriticalSection->LockSemaphore = malloc(sizeof(pthread_mutex_t));
|
||||
pthread_mutex_init(lpCriticalSection->LockSemaphore, NULL);
|
||||
}
|
||||
InitializeCriticalSectionEx(lpCriticalSection, 0, 0);
|
||||
}
|
||||
|
||||
BOOL InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD Flags)
|
||||
{
|
||||
/**
|
||||
* 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) {
|
||||
fprintf(stderr, "warning: InitializeCriticalSectionEx Flags unimplemented\n");
|
||||
}
|
||||
return InitializeCriticalSectionAndSpinCount(lpCriticalSection, dwSpinCount);
|
||||
|
||||
lpCriticalSection->DebugInfo = NULL;
|
||||
lpCriticalSection->LockCount = -1;
|
||||
lpCriticalSection->SpinCount = 0;
|
||||
lpCriticalSection->RecursionCount = 0;
|
||||
lpCriticalSection->OwningThread = NULL;
|
||||
|
||||
lpCriticalSection->LockSemaphore = (winpr_sem_t*) malloc(sizeof(winpr_sem_t));
|
||||
#if defined(__APPLE__)
|
||||
semaphore_create(mach_task_self(), lpCriticalSection->LockSemaphore, SYNC_POLICY_FIFO, 0);
|
||||
#else
|
||||
sem_init(lpCriticalSection->LockSemaphore, 0, 0);
|
||||
#endif
|
||||
|
||||
SetCriticalSectionSpinCount(lpCriticalSection, dwSpinCount);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount)
|
||||
{
|
||||
InitializeCriticalSection(lpCriticalSection);
|
||||
SetCriticalSectionSpinCount(lpCriticalSection, dwSpinCount);
|
||||
return TRUE;
|
||||
return InitializeCriticalSectionEx(lpCriticalSection, dwSpinCount, 0);
|
||||
}
|
||||
|
||||
DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount)
|
||||
{
|
||||
#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
|
||||
return 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
VOID _WaitForCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
#if defined(__APPLE__)
|
||||
semaphore_wait(*((winpr_sem_t*) lpCriticalSection->LockSemaphore));
|
||||
#else
|
||||
sem_wait((winpr_sem_t*) lpCriticalSection->LockSemaphore);
|
||||
#endif
|
||||
}
|
||||
|
||||
VOID _UnWaitCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
#if defined __APPLE__
|
||||
semaphore_signal(*((winpr_sem_t*) lpCriticalSection->LockSemaphore));
|
||||
#else
|
||||
sem_post((winpr_sem_t*) lpCriticalSection->LockSemaphore);
|
||||
#endif
|
||||
}
|
||||
|
||||
VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
/**
|
||||
* Linux NPTL thread synchronization primitives are implemented using
|
||||
* the futex system calls ... no need for performing a trylock loop.
|
||||
*/
|
||||
#if !defined(__linux__)
|
||||
ULONG spin = lpCriticalSection->SpinCount;
|
||||
while (spin--)
|
||||
#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)
|
||||
{
|
||||
if (pthread_mutex_trylock((pthread_mutex_t*)lpCriticalSection->LockSemaphore) == 0)
|
||||
/* 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)GetCurrentThreadId();
|
||||
return;
|
||||
pthread_yield();
|
||||
}
|
||||
/* 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
|
||||
pthread_mutex_lock((pthread_mutex_t*)lpCriticalSection->LockSemaphore);
|
||||
|
||||
/* First try the fastest posssible 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)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)GetCurrentThreadId();
|
||||
}
|
||||
|
||||
BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
return (pthread_mutex_trylock((pthread_mutex_t*)lpCriticalSection->LockSemaphore) == 0 ? TRUE : FALSE);
|
||||
HANDLE current_thread = (HANDLE)GetCurrentThreadId();
|
||||
|
||||
/* 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)
|
||||
{
|
||||
pthread_mutex_unlock((pthread_mutex_t*)lpCriticalSection->LockSemaphore);
|
||||
/* 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 = NULL;
|
||||
if (InterlockedDecrement(&lpCriticalSection->LockCount) >= 0)
|
||||
{
|
||||
/* ...signal the semaphore to unblock the next waiting thread */
|
||||
_UnWaitCriticalSection(lpCriticalSection);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
InterlockedDecrement(&lpCriticalSection->LockCount);
|
||||
}
|
||||
}
|
||||
|
||||
VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
|
||||
{
|
||||
pthread_mutex_destroy((pthread_mutex_t*)lpCriticalSection->LockSemaphore);
|
||||
free(lpCriticalSection->LockSemaphore);
|
||||
lpCriticalSection->LockCount = -1;
|
||||
lpCriticalSection->SpinCount = 0;
|
||||
lpCriticalSection->RecursionCount = 0;
|
||||
lpCriticalSection->OwningThread = NULL;
|
||||
|
||||
if (lpCriticalSection->LockSemaphore != NULL)
|
||||
{
|
||||
#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 = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -7,6 +7,7 @@ set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
|
||||
set(${MODULE_PREFIX}_TESTS
|
||||
TestSynchEvent.c
|
||||
TestSynchMutex.c
|
||||
TestSynchCritical.c
|
||||
TestSynchSemaphore.c
|
||||
TestSynchWaitableTimer.c)
|
||||
|
||||
@@ -19,7 +20,7 @@ add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
|
||||
set_complex_link_libraries(VARIABLE ${MODULE_PREFIX}_LIBS
|
||||
MONOLITHIC ${MONOLITHIC_BUILD}
|
||||
MODULE winpr
|
||||
MODULES winpr-synch)
|
||||
MODULES winpr-synch winpr-sysinfo)
|
||||
|
||||
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS})
|
||||
|
||||
@@ -31,3 +32,4 @@ foreach(test ${${MODULE_PREFIX}_TESTS})
|
||||
endforeach()
|
||||
|
||||
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")
|
||||
|
||||
|
||||
329
winpr/libwinpr/synch/test/TestSynchCritical.c
Normal file
329
winpr/libwinpr/synch/test/TestSynchCritical.c
Normal file
@@ -0,0 +1,329 @@
|
||||
|
||||
#include <stdio.h>
|
||||
#include <winpr/crt.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 500
|
||||
#define TEST_SYNC_CRITICAL_TEST1_RUNS 4
|
||||
|
||||
CRITICAL_SECTION critical;
|
||||
LONG gTestValueVulnerable = 0;
|
||||
LONG gTestValueSerialized = 0;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
/* this thread function shall increment the global dwTestValue until the PBOOL passsed in arg is FALSE */
|
||||
static PVOID TestSynchCritical_Test1(PVOID arg)
|
||||
{
|
||||
int i, j, rc;
|
||||
HANDLE hThread = (HANDLE)GetCurrentThreadId();
|
||||
|
||||
PBOOL pbContinueRunning = (PBOOL)arg;
|
||||
|
||||
while(*pbContinueRunning)
|
||||
{
|
||||
EnterCriticalSection(&critical);
|
||||
|
||||
rc = 1;
|
||||
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
|
||||
return (PVOID)1;
|
||||
|
||||
/* add some random recursion level */
|
||||
j = rand()%5;
|
||||
for (i=0; i<j; i++)
|
||||
{
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc++))
|
||||
return (PVOID)2;
|
||||
EnterCriticalSection(&critical);
|
||||
}
|
||||
for (i=0; i<j; i++)
|
||||
{
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc--))
|
||||
return (PVOID)2;
|
||||
LeaveCriticalSection(&critical);
|
||||
}
|
||||
|
||||
if (!TestSynchCritical_TriggerAndCheckRaceCondition(hThread, rc))
|
||||
return (PVOID)3;
|
||||
|
||||
LeaveCriticalSection(&critical);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* this thread function tries to call TryEnterCriticalSection while the main thread holds the lock */
|
||||
static PVOID TestSynchCritical_Test2(PVOID arg)
|
||||
{
|
||||
if (TryEnterCriticalSection(&critical)==TRUE)
|
||||
{
|
||||
LeaveCriticalSection(&critical);
|
||||
return (PVOID)1;
|
||||
}
|
||||
return (PVOID)0;
|
||||
}
|
||||
|
||||
|
||||
static PVOID TestSynchCritical_Main(PVOID arg)
|
||||
{
|
||||
int i, j;
|
||||
SYSTEM_INFO sysinfo;
|
||||
DWORD dwPreviousSpinCount;
|
||||
DWORD dwSpinCount;
|
||||
DWORD dwSpinCountExpected;
|
||||
HANDLE hMainThread;
|
||||
HANDLE* hThreads;
|
||||
HANDLE hThread;
|
||||
DWORD dwThreadCount;
|
||||
DWORD dwThreadExitCode;
|
||||
BOOL bTest1Running;
|
||||
|
||||
PBOOL pbThreadTerminated = (PBOOL)arg;
|
||||
|
||||
GetNativeSystemInfo(&sysinfo);
|
||||
|
||||
hMainThread = (HANDLE)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 %lu (expected: %lu)\n", dwPreviousSpinCount, dwSpinCountExpected);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&critical);
|
||||
|
||||
if (dwSpinCount%2==0)
|
||||
InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount);
|
||||
else
|
||||
InitializeCriticalSectionEx(&critical, dwSpinCount, 0);
|
||||
}
|
||||
DeleteCriticalSection(&critical);
|
||||
|
||||
|
||||
/**
|
||||
* Test single-threaded recursive TryEnterCriticalSection/EnterCriticalSection/LeaveCriticalSection
|
||||
*
|
||||
*/
|
||||
|
||||
InitializeCriticalSection(&critical);
|
||||
|
||||
for (i=0; i<1000; i++)
|
||||
{
|
||||
if (critical.RecursionCount != i)
|
||||
{
|
||||
printf("CriticalSection failure: RecursionCount field is %ld 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 %ld instead of %d.\n", critical.RecursionCount, i);
|
||||
goto fail;
|
||||
}
|
||||
if (critical.OwningThread != (HANDLE)(i ? hMainThread : NULL))
|
||||
{
|
||||
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));
|
||||
|
||||
for (j=0; j < TEST_SYNC_CRITICAL_TEST1_RUNS; j++)
|
||||
{
|
||||
dwSpinCount = j * 1000;
|
||||
InitializeCriticalSectionAndSpinCount(&critical, dwSpinCount);
|
||||
|
||||
gTestValueVulnerable = 0;
|
||||
gTestValueSerialized = 0;
|
||||
|
||||
/* the TestSynchCritical_Test1 threads shall run until bTest1Running is FALSE */
|
||||
bTest1Running = TRUE;
|
||||
for (i=0; i<dwThreadCount; i++) {
|
||||
hThreads[i] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) TestSynchCritical_Test1, &bTest1Running, 0, NULL);
|
||||
}
|
||||
/* let it run for TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS ... */
|
||||
Sleep(TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS);
|
||||
bTest1Running = FALSE;
|
||||
|
||||
for (i=0; i<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 %lu\n", i, dwThreadExitCode);
|
||||
goto fail;
|
||||
}
|
||||
CloseHandle(hThreads[i]);
|
||||
}
|
||||
|
||||
if (gTestValueVulnerable != gTestValueSerialized)
|
||||
{
|
||||
printf("CriticalSection failure: unexpected test value %ld (expected %ld)\n", gTestValueVulnerable, gTestValueSerialized);
|
||||
goto fail;
|
||||
}
|
||||
|
||||
DeleteCriticalSection(&critical);
|
||||
}
|
||||
|
||||
free(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 */
|
||||
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) TestSynchCritical_Test2, NULL, 0, NULL);
|
||||
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 %lu\n", dwThreadExitCode);
|
||||
goto fail;
|
||||
}
|
||||
CloseHandle(hThread);
|
||||
|
||||
*pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
|
||||
return (PVOID)0;
|
||||
|
||||
fail:
|
||||
*pbThreadTerminated = TRUE; /* requ. for winpr issue, see below */
|
||||
return (PVOID)1;
|
||||
}
|
||||
|
||||
|
||||
int TestSynchCritical(int argc, char* argv[])
|
||||
{
|
||||
BOOL bThreadTerminated = FALSE;
|
||||
HANDLE hThread;
|
||||
DWORD dwThreadExitCode;
|
||||
DWORD dwDeadLockDetectionTimeMs;
|
||||
int i;
|
||||
|
||||
dwDeadLockDetectionTimeMs = 2 * TEST_SYNC_CRITICAL_TEST1_RUNTIME_MS * TEST_SYNC_CRITICAL_TEST1_RUNS;
|
||||
|
||||
printf("Deadlock will be assumed after %lu ms.\n", dwDeadLockDetectionTimeMs);
|
||||
|
||||
hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) TestSynchCritical_Main, &bThreadTerminated, 0, NULL);
|
||||
|
||||
/**
|
||||
* 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 (i=0; i<dwDeadLockDetectionTimeMs; i+=100)
|
||||
{
|
||||
if (bThreadTerminated)
|
||||
break;
|
||||
Sleep(100);
|
||||
}
|
||||
|
||||
if (!bThreadTerminated)
|
||||
{
|
||||
printf("CriticalSection failure: Possible dead lock detected\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
GetExitCodeThread(hThread, &dwThreadExitCode);
|
||||
CloseHandle(hThread);
|
||||
|
||||
if(dwThreadExitCode != 0)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -390,7 +390,7 @@ wArrayList* ArrayList_New(BOOL synchronized)
|
||||
|
||||
arrayList->array = (void**) malloc(sizeof(void*) * arrayList->capacity);
|
||||
|
||||
InitializeCriticalSection(&arrayList->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&arrayList->lock, 4000);
|
||||
|
||||
ZeroMemory(&arrayList->object, sizeof(wObject));
|
||||
}
|
||||
|
||||
@@ -134,7 +134,7 @@ wBufferPool* BufferPool_New(BOOL synchronized, int fixedSize, DWORD alignment)
|
||||
pool->synchronized = synchronized;
|
||||
|
||||
if (pool->synchronized)
|
||||
InitializeCriticalSection(&pool->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
|
||||
|
||||
if (!pool->fixedSize)
|
||||
{
|
||||
|
||||
@@ -158,7 +158,7 @@ wCountdownEvent* CountdownEvent_New(DWORD initialCount)
|
||||
{
|
||||
countdown->count = initialCount;
|
||||
countdown->initialCount = initialCount;
|
||||
InitializeCriticalSection(&countdown->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&countdown->lock, 4000);
|
||||
countdown->event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
if (countdown->count == 0)
|
||||
|
||||
@@ -194,7 +194,7 @@ wMessageQueue* MessageQueue_New()
|
||||
queue->array = (wMessage*) malloc(sizeof(wMessage) * queue->capacity);
|
||||
ZeroMemory(queue->array, sizeof(wMessage) * queue->capacity);
|
||||
|
||||
InitializeCriticalSection(&queue->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&queue->lock, 4000);
|
||||
queue->event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,7 @@ wObjectPool* ObjectPool_New(BOOL synchronized)
|
||||
pool->synchronized = synchronized;
|
||||
|
||||
if (pool->synchronized)
|
||||
InitializeCriticalSection(&pool->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
|
||||
|
||||
pool->size = 0;
|
||||
pool->capacity = 32;
|
||||
|
||||
@@ -202,7 +202,7 @@ wPubSub* PubSub_New(BOOL synchronized)
|
||||
pubSub->synchronized = synchronized;
|
||||
|
||||
if (pubSub->synchronized)
|
||||
InitializeCriticalSection(&pubSub->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&pubSub->lock, 4000);
|
||||
|
||||
pubSub->count = 0;
|
||||
pubSub->size = 64;
|
||||
|
||||
@@ -243,7 +243,7 @@ wQueue* Queue_New(BOOL synchronized, int capacity, int growthFactor)
|
||||
queue->array = (void**) malloc(sizeof(void*) * queue->capacity);
|
||||
ZeroMemory(queue->array, sizeof(void*) * queue->capacity);
|
||||
|
||||
InitializeCriticalSection(&queue->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&queue->lock, 4000);
|
||||
queue->event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
|
||||
ZeroMemory(&queue->object, sizeof(wObject));
|
||||
|
||||
@@ -154,7 +154,7 @@ wReferenceTable* ReferenceTable_New(BOOL synchronized, void* context, REFERENCE_
|
||||
ZeroMemory(referenceTable->array, sizeof(wReference) * referenceTable->size);
|
||||
|
||||
referenceTable->synchronized = synchronized;
|
||||
InitializeCriticalSection(&referenceTable->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&referenceTable->lock, 4000);
|
||||
}
|
||||
|
||||
return referenceTable;
|
||||
|
||||
@@ -153,7 +153,7 @@ wStack* Stack_New(BOOL synchronized)
|
||||
stack->synchronized = synchronized;
|
||||
|
||||
if (stack->synchronized)
|
||||
InitializeCriticalSection(&stack->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&stack->lock, 4000);
|
||||
|
||||
stack->size = 0;
|
||||
stack->capacity = 32;
|
||||
|
||||
@@ -323,7 +323,7 @@ wStreamPool* StreamPool_New(BOOL synchronized, size_t defaultSize)
|
||||
pool->synchronized = synchronized;
|
||||
pool->defaultSize = defaultSize;
|
||||
|
||||
InitializeCriticalSection(&pool->lock);
|
||||
InitializeCriticalSectionAndSpinCount(&pool->lock, 4000);
|
||||
|
||||
pool->aSize = 0;
|
||||
pool->aCapacity = 32;
|
||||
|
||||
Reference in New Issue
Block a user