FreeRDP/winpr/libwinpr/error/test/TestErrorSetLastError.c
Norbert Federa 6f9a8dbc1e winpr: greatly improved NtCurrentTeb performance
Use pthread_setspecific/pthread_getspecific to store/retrieve the thread
environment block (TEB). Use pthread_once to trigger the creation of that
data from within NtCurrentTeb.
This allows us to get rid of the process environment block stuff which
was only used to provide serialized access to a thread table in order to
retrieve the TEB.

NtCurrentTeb is currently only as a per-thread storage location for the
last error value used by SetLastError and GetLastError.

Also made the TestErrorSetLastError CTest a bit more demanding.
It makes sure the 4 threads run for at least 2 seconds.
Each thread constantly calls SetLastError with a random value and checks
if GetLastError returns the same value again. The total amount of
these iterations is calculated in order to measure the performance.

This change increases the NtCurrentTeb performance by roughly 50% on
linux and by several thousand percent (yes) on Mac OS X.

Thanks for watching.
2013-10-11 19:34:23 +02:00

116 lines
2.9 KiB
C

/**
* CTest for winpr's SetLastError/GetLastError
*
* Copyright 2013 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2013 Thinstuff Technologies GmbH
* Copyright 2013 Norbert Federa <nfedera@thinstuff.at>
*
* 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/crt.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/interlocked.h>
#include <winpr/error.h>
static int status = 0;
LONG *pLoopCount = NULL;
BOOL bStopTest = FALSE;
static void* test_error_thread(void* arg)
{
int id;
DWORD dwErrorSet;
DWORD dwErrorGet;
id = (int) (size_t) arg;
do {
dwErrorSet = (DWORD)rand();
SetLastError(dwErrorSet);
if ((dwErrorGet = GetLastError()) != dwErrorSet)
{
printf("GetLastError() failure (thread %d): Expected: 0x%04X, Actual: 0x%04X\n",
id, dwErrorSet, dwErrorGet);
if (!status)
status = -1;
break;
}
InterlockedIncrement(pLoopCount);
} while (!status && !bStopTest);
return NULL;
}
int TestErrorSetLastError(int argc, char* argv[])
{
DWORD error;
HANDLE threads[4];
SetLastError(ERROR_ACCESS_DENIED);
error = GetLastError();
if (error != ERROR_ACCESS_DENIED)
{
printf("GetLastError() failure: Expected: 0x%04X, Actual: 0x%04X\n",
ERROR_ACCESS_DENIED, error);
return -1;
}
pLoopCount = _aligned_malloc(sizeof(LONG), sizeof(LONG));
*pLoopCount = 0;
threads[0] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) test_error_thread, (void*) (size_t) 0, 0, NULL);
threads[1] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) test_error_thread, (void*) (size_t) 1, 0, NULL);
threads[2] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) test_error_thread, (void*) (size_t) 2, 0, NULL);
threads[3] = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) test_error_thread, (void*) (size_t) 3, 0, NULL);
// let the threads run for at least 2 seconds
Sleep(2000);
bStopTest = TRUE;
WaitForSingleObject(threads[0], INFINITE);
WaitForSingleObject(threads[1], INFINITE);
WaitForSingleObject(threads[2], INFINITE);
WaitForSingleObject(threads[3], INFINITE);
CloseHandle(threads[0]);
CloseHandle(threads[1]);
CloseHandle(threads[2]);
CloseHandle(threads[3]);
error = GetLastError();
if (error != ERROR_ACCESS_DENIED)
{
printf("GetLastError() failure: Expected: 0x%04X, Actual: 0x%04X\n",
ERROR_ACCESS_DENIED, error);
return -1;
}
if (*pLoopCount < 4)
{
printf("Error: unexpected loop count\n");
return -1;
}
printf("Completed %lu iterations.\n", *pLoopCount);
return status;
}