summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/synch/critical.c
diff options
context:
space:
mode:
Diffstat (limited to 'winpr/libwinpr/synch/critical.c')
-rw-r--r--winpr/libwinpr/synch/critical.c272
1 files changed, 272 insertions, 0 deletions
diff --git a/winpr/libwinpr/synch/critical.c b/winpr/libwinpr/synch/critical.c
new file mode 100644
index 0000000..795d93a
--- /dev/null
+++ b/winpr/libwinpr/synch/critical.c
@@ -0,0 +1,272 @@
+/**
+ * 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 = NULL;
+ lpCriticalSection->LockCount = -1;
+ lpCriticalSection->SpinCount = 0;
+ lpCriticalSection->RecursionCount = 0;
+ lpCriticalSection->OwningThread = NULL;
+ 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(LPCRITICAL_SECTION lpCriticalSection, 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
+ return 0;
+#endif
+}
+
+static VOID _WaitForCriticalSection(LPCRITICAL_SECTION lpCriticalSection)
+{
+ WINPR_ASSERT(lpCriticalSection);
+#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);
+#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 = 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)
+{
+ WINPR_ASSERT(lpCriticalSection);
+
+ 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