Blame winpr/libwinpr/synch/critical.c

Packit 1fb8d4
/**
Packit 1fb8d4
 * WinPR: Windows Portable Runtime
Packit 1fb8d4
 * Synchronization Functions
Packit 1fb8d4
 *
Packit 1fb8d4
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
Packit 1fb8d4
 * Copyright 2013 Norbert Federa <norbert.federa@thincast.com>
Packit 1fb8d4
 *
Packit 1fb8d4
 * Licensed under the Apache License, Version 2.0 (the "License");
Packit 1fb8d4
 * you may not use this file except in compliance with the License.
Packit 1fb8d4
 * You may obtain a copy of the License at
Packit 1fb8d4
 *
Packit 1fb8d4
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 1fb8d4
 *
Packit 1fb8d4
 * Unless required by applicable law or agreed to in writing, software
Packit 1fb8d4
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 1fb8d4
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 1fb8d4
 * See the License for the specific language governing permissions and
Packit 1fb8d4
 * limitations under the License.
Packit 1fb8d4
 */
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_CONFIG_H
Packit 1fb8d4
#include "config.h"
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#include <winpr/tchar.h>
Packit 1fb8d4
#include <winpr/synch.h>
Packit 1fb8d4
#include <winpr/sysinfo.h>
Packit 1fb8d4
#include <winpr/interlocked.h>
Packit 1fb8d4
#include <winpr/thread.h>
Packit 1fb8d4
Packit 1fb8d4
#include "synch.h"
Packit 1fb8d4
Packit 1fb8d4
#ifdef HAVE_UNISTD_H
Packit 1fb8d4
#include <unistd.h>
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#if defined(__APPLE__)
Packit 1fb8d4
#include <mach/task.h>
Packit 1fb8d4
#include <mach/mach.h>
Packit 1fb8d4
#include <mach/semaphore.h>
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
#ifndef _WIN32
Packit 1fb8d4
Packit 1fb8d4
#include "../log.h"
Packit 1fb8d4
#define TAG WINPR_TAG("synch.critical")
Packit 1fb8d4
Packit 1fb8d4
VOID InitializeCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
Packit 1fb8d4
{
Packit 1fb8d4
	InitializeCriticalSectionEx(lpCriticalSection, 0, 0);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL InitializeCriticalSectionEx(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount,
Packit 1fb8d4
                                 DWORD Flags)
Packit 1fb8d4
{
Packit 1fb8d4
	/**
Packit 1fb8d4
	 * See http://msdn.microsoft.com/en-us/library/ff541979(v=vs.85).aspx
Packit 1fb8d4
	 * - The LockCount field indicates the number of times that any thread has
Packit 1fb8d4
	 *   called the EnterCriticalSection routine for this critical section,
Packit 1fb8d4
	 *   minus one. This field starts at -1 for an unlocked critical section.
Packit 1fb8d4
	 *   Each call of EnterCriticalSection increments this value; each call of
Packit 1fb8d4
	 *   LeaveCriticalSection decrements it.
Packit 1fb8d4
	 * - The RecursionCount field indicates the number of times that the owning
Packit 1fb8d4
	 *   thread has called EnterCriticalSection for this critical section.
Packit 1fb8d4
	 */
Packit 1fb8d4
	if (Flags != 0)
Packit 1fb8d4
	{
Packit 1fb8d4
		WLog_WARN(TAG, "Flags unimplemented");
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	lpCriticalSection->DebugInfo = NULL;
Packit 1fb8d4
	lpCriticalSection->LockCount = -1;
Packit 1fb8d4
	lpCriticalSection->SpinCount = 0;
Packit 1fb8d4
	lpCriticalSection->RecursionCount = 0;
Packit 1fb8d4
	lpCriticalSection->OwningThread = NULL;
Packit Service 5a9772
	lpCriticalSection->LockSemaphore = (winpr_sem_t*)malloc(sizeof(winpr_sem_t));
Packit 1fb8d4
Packit 1fb8d4
	if (!lpCriticalSection->LockSemaphore)
Packit 1fb8d4
		return FALSE;
Packit 1fb8d4
Packit 1fb8d4
#if defined(__APPLE__)
Packit 1fb8d4
Packit Service 5a9772
	if (semaphore_create(mach_task_self(), lpCriticalSection->LockSemaphore, SYNC_POLICY_FIFO, 0) !=
Packit Service 5a9772
	    KERN_SUCCESS)
Packit 1fb8d4
		goto out_fail;
Packit 1fb8d4
Packit 1fb8d4
#else
Packit 1fb8d4
Packit 1fb8d4
	if (sem_init(lpCriticalSection->LockSemaphore, 0, 0) != 0)
Packit 1fb8d4
		goto out_fail;
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
	SetCriticalSectionSpinCount(lpCriticalSection, dwSpinCount);
Packit 1fb8d4
	return TRUE;
Packit 1fb8d4
out_fail:
Packit 1fb8d4
	free(lpCriticalSection->LockSemaphore);
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL InitializeCriticalSectionAndSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount)
Packit 1fb8d4
{
Packit 1fb8d4
	return InitializeCriticalSectionEx(lpCriticalSection, dwSpinCount, 0);
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
DWORD SetCriticalSectionSpinCount(LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount)
Packit 1fb8d4
{
Packit 1fb8d4
#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
Packit 1fb8d4
	SYSTEM_INFO sysinfo;
Packit 1fb8d4
	DWORD dwPreviousSpinCount = lpCriticalSection->SpinCount;
Packit 1fb8d4
Packit 1fb8d4
	if (dwSpinCount)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Don't spin on uniprocessor systems! */
Packit 1fb8d4
		GetNativeSystemInfo(&sysinfo);
Packit 1fb8d4
Packit 1fb8d4
		if (sysinfo.dwNumberOfProcessors < 2)
Packit 1fb8d4
			dwSpinCount = 0;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	lpCriticalSection->SpinCount = dwSpinCount;
Packit 1fb8d4
	return dwPreviousSpinCount;
Packit 1fb8d4
#else
Packit 1fb8d4
	return 0;
Packit 1fb8d4
#endif
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static VOID _WaitForCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
Packit 1fb8d4
{
Packit 1fb8d4
#if defined(__APPLE__)
Packit Service 5a9772
	semaphore_wait(*((winpr_sem_t*)lpCriticalSection->LockSemaphore));
Packit 1fb8d4
#else
Packit Service 5a9772
	sem_wait((winpr_sem_t*)lpCriticalSection->LockSemaphore);
Packit 1fb8d4
#endif
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
static VOID _UnWaitCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
Packit 1fb8d4
{
Packit 1fb8d4
#if defined __APPLE__
Packit Service 5a9772
	semaphore_signal(*((winpr_sem_t*)lpCriticalSection->LockSemaphore));
Packit 1fb8d4
#else
Packit Service 5a9772
	sem_post((winpr_sem_t*)lpCriticalSection->LockSemaphore);
Packit 1fb8d4
#endif
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
VOID EnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
Packit 1fb8d4
{
Packit 1fb8d4
#if !defined(WINPR_CRITICAL_SECTION_DISABLE_SPINCOUNT)
Packit 1fb8d4
	ULONG SpinCount = lpCriticalSection->SpinCount;
Packit 1fb8d4
Packit 1fb8d4
	/* If we're lucky or if the current thread is already owner we can return early */
Packit 1fb8d4
	if (SpinCount && TryEnterCriticalSection(lpCriticalSection))
Packit 1fb8d4
		return;
Packit 1fb8d4
Packit 1fb8d4
	/* Spin requested times but don't compete with another waiting thread */
Packit 1fb8d4
	while (SpinCount-- && lpCriticalSection->LockCount < 1)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Atomically try to acquire and check the if the section is free. */
Packit 1fb8d4
		if (InterlockedCompareExchange(&lpCriticalSection->LockCount, 0, -1) == -1)
Packit 1fb8d4
		{
Packit 1fb8d4
			lpCriticalSection->RecursionCount = 1;
Packit Service 5a9772
			lpCriticalSection->OwningThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
Packit 1fb8d4
			return;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		/* Failed to get the lock. Let the scheduler know that we're spinning. */
Packit 1fb8d4
		if (sched_yield() != 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			/**
Packit 1fb8d4
			 * On some operating systems sched_yield is a stub.
Packit 1fb8d4
			 * usleep should at least trigger a context switch if any thread is waiting.
Packit 1fb8d4
			 * A ThreadYield() would be nice in winpr ...
Packit 1fb8d4
			 */
Packit 1fb8d4
			usleep(1);
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
#endif
Packit 1fb8d4
Packit 1fb8d4
	/* First try the fastest possible path to get the lock. */
Packit 1fb8d4
	if (InterlockedIncrement(&lpCriticalSection->LockCount))
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Section is already locked. Check if it is owned by the current thread. */
Packit Service 5a9772
		if (lpCriticalSection->OwningThread == (HANDLE)(ULONG_PTR)GetCurrentThreadId())
Packit 1fb8d4
		{
Packit 1fb8d4
			/* Recursion. No need to wait. */
Packit 1fb8d4
			lpCriticalSection->RecursionCount++;
Packit 1fb8d4
			return;
Packit 1fb8d4
		}
Packit 1fb8d4
Packit 1fb8d4
		/* Section is locked by another thread. We have to wait. */
Packit 1fb8d4
		_WaitForCriticalSection(lpCriticalSection);
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* We got the lock. Own it ... */
Packit 1fb8d4
	lpCriticalSection->RecursionCount = 1;
Packit Service 5a9772
	lpCriticalSection->OwningThread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
BOOL TryEnterCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
Packit 1fb8d4
{
Packit Service 5a9772
	HANDLE current_thread = (HANDLE)(ULONG_PTR)GetCurrentThreadId();
Packit 1fb8d4
Packit 1fb8d4
	/* Atomically acquire the the lock if the section is free. */
Packit 1fb8d4
	if (InterlockedCompareExchange(&lpCriticalSection->LockCount, 0, -1) == -1)
Packit 1fb8d4
	{
Packit 1fb8d4
		lpCriticalSection->RecursionCount = 1;
Packit 1fb8d4
		lpCriticalSection->OwningThread = current_thread;
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	/* Section is already locked. Check if it is owned by the current thread. */
Packit 1fb8d4
	if (lpCriticalSection->OwningThread == current_thread)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Recursion, return success */
Packit 1fb8d4
		lpCriticalSection->RecursionCount++;
Packit 1fb8d4
		InterlockedIncrement(&lpCriticalSection->LockCount);
Packit 1fb8d4
		return TRUE;
Packit 1fb8d4
	}
Packit 1fb8d4
Packit 1fb8d4
	return FALSE;
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
VOID LeaveCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
Packit 1fb8d4
{
Packit 1fb8d4
	/* Decrement RecursionCount and check if this is the last LeaveCriticalSection call ...*/
Packit 1fb8d4
	if (--lpCriticalSection->RecursionCount < 1)
Packit 1fb8d4
	{
Packit 1fb8d4
		/* Last recursion, clear owner, unlock and if there are other waiting threads ... */
Packit 1fb8d4
		lpCriticalSection->OwningThread = NULL;
Packit 1fb8d4
Packit 1fb8d4
		if (InterlockedDecrement(&lpCriticalSection->LockCount) >= 0)
Packit 1fb8d4
		{
Packit 1fb8d4
			/* ...signal the semaphore to unblock the next waiting thread */
Packit 1fb8d4
			_UnWaitCriticalSection(lpCriticalSection);
Packit 1fb8d4
		}
Packit 1fb8d4
	}
Packit 1fb8d4
	else
Packit 1fb8d4
	{
Packit 1fb8d4
		InterlockedDecrement(&lpCriticalSection->LockCount);
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
VOID DeleteCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
Packit 1fb8d4
{
Packit 1fb8d4
	lpCriticalSection->LockCount = -1;
Packit 1fb8d4
	lpCriticalSection->SpinCount = 0;
Packit 1fb8d4
	lpCriticalSection->RecursionCount = 0;
Packit 1fb8d4
	lpCriticalSection->OwningThread = NULL;
Packit 1fb8d4
Packit 1fb8d4
	if (lpCriticalSection->LockSemaphore != NULL)
Packit 1fb8d4
	{
Packit 1fb8d4
#if defined __APPLE__
Packit Service 5a9772
		semaphore_destroy(mach_task_self(), *((winpr_sem_t*)lpCriticalSection->LockSemaphore));
Packit 1fb8d4
#else
Packit Service 5a9772
		sem_destroy((winpr_sem_t*)lpCriticalSection->LockSemaphore);
Packit 1fb8d4
#endif
Packit 1fb8d4
		free(lpCriticalSection->LockSemaphore);
Packit 1fb8d4
		lpCriticalSection->LockSemaphore = NULL;
Packit 1fb8d4
	}
Packit 1fb8d4
}
Packit 1fb8d4
Packit 1fb8d4
#endif