summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/interlocked/interlocked.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--winpr/libwinpr/interlocked/interlocked.c488
1 files changed, 488 insertions, 0 deletions
diff --git a/winpr/libwinpr/interlocked/interlocked.c b/winpr/libwinpr/interlocked/interlocked.c
new file mode 100644
index 0000000..6c3b0c5
--- /dev/null
+++ b/winpr/libwinpr/interlocked/interlocked.c
@@ -0,0 +1,488 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * Interlocked Singly-Linked Lists
+ *
+ * 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/synch.h>
+#include <winpr/handle.h>
+
+#include <winpr/interlocked.h>
+
+/* Singly-Linked List */
+
+#ifndef _WIN32
+
+#include <stdio.h>
+#include <stdlib.h>
+
+VOID InitializeSListHead(WINPR_PSLIST_HEADER ListHead)
+{
+#ifdef _WIN64
+ ListHead->s.Alignment = 0;
+ ListHead->s.Region = 0;
+ ListHead->Header8.Init = 1;
+#else
+ ListHead->Alignment = 0;
+#endif
+}
+
+WINPR_PSLIST_ENTRY InterlockedPushEntrySList(WINPR_PSLIST_HEADER ListHead,
+ WINPR_PSLIST_ENTRY ListEntry)
+{
+ WINPR_SLIST_HEADER old;
+ WINPR_SLIST_HEADER newHeader;
+
+#ifdef _WIN64
+ newHeader.HeaderX64.NextEntry = (((ULONG_PTR)ListEntry) >> 4);
+
+ while (1)
+ {
+ old = *ListHead;
+
+ ListEntry->Next = (PSLIST_ENTRY)(((ULONG_PTR)old.HeaderX64.NextEntry) << 4);
+
+ newHeader.HeaderX64.Depth = old.HeaderX64.Depth + 1;
+ newHeader.HeaderX64.Sequence = old.HeaderX64.Sequence + 1;
+
+ if (InterlockedCompareExchange64((LONGLONG*)ListHead, newHeader.s.Alignment,
+ old.s.Alignment))
+ {
+ InterlockedCompareExchange64(&((LONGLONG*)ListHead)[1], newHeader.s.Region,
+ old.s.Region);
+ break;
+ }
+ }
+
+ return (PSLIST_ENTRY)((ULONG_PTR)old.HeaderX64.NextEntry << 4);
+#else
+ newHeader.s.Next.Next = ListEntry;
+
+ do
+ {
+ old = *ListHead;
+ ListEntry->Next = old.s.Next.Next;
+ newHeader.s.Depth = old.s.Depth + 1;
+ newHeader.s.Sequence = old.s.Sequence + 1;
+ if (old.Alignment > INT64_MAX)
+ return NULL;
+ if (newHeader.Alignment > INT64_MAX)
+ return NULL;
+ if (ListHead->Alignment > INT64_MAX)
+ return NULL;
+ } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment,
+ (LONGLONG)newHeader.Alignment,
+ (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment);
+
+ return old.s.Next.Next;
+#endif
+}
+
+WINPR_PSLIST_ENTRY InterlockedPushListSListEx(WINPR_PSLIST_HEADER ListHead, WINPR_PSLIST_ENTRY List,
+ WINPR_PSLIST_ENTRY ListEnd, ULONG Count)
+{
+#ifdef _WIN64
+
+#else
+
+#endif
+ return NULL;
+}
+
+WINPR_PSLIST_ENTRY InterlockedPopEntrySList(WINPR_PSLIST_HEADER ListHead)
+{
+ WINPR_SLIST_HEADER old;
+ WINPR_SLIST_HEADER newHeader;
+ WINPR_PSLIST_ENTRY entry = NULL;
+
+#ifdef _WIN64
+ while (1)
+ {
+ old = *ListHead;
+
+ entry = (PSLIST_ENTRY)(((ULONG_PTR)old.HeaderX64.NextEntry) << 4);
+
+ if (!entry)
+ return NULL;
+
+ newHeader.HeaderX64.NextEntry = ((ULONG_PTR)entry->Next) >> 4;
+ newHeader.HeaderX64.Depth = old.HeaderX64.Depth - 1;
+ newHeader.HeaderX64.Sequence = old.HeaderX64.Sequence - 1;
+
+ if (InterlockedCompareExchange64((LONGLONG*)ListHead, newHeader.s.Alignment,
+ old.s.Alignment))
+ {
+ InterlockedCompareExchange64(&((LONGLONG*)ListHead)[1], newHeader.s.Region,
+ old.s.Region);
+ break;
+ }
+ }
+#else
+ do
+ {
+ old = *ListHead;
+
+ entry = old.s.Next.Next;
+
+ if (!entry)
+ return NULL;
+
+ newHeader.s.Next.Next = entry->Next;
+ newHeader.s.Depth = old.s.Depth - 1;
+ newHeader.s.Sequence = old.s.Sequence + 1;
+
+ if (old.Alignment > INT64_MAX)
+ return NULL;
+ if (newHeader.Alignment > INT64_MAX)
+ return NULL;
+ if (ListHead->Alignment > INT64_MAX)
+ return NULL;
+ } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment,
+ (LONGLONG)newHeader.Alignment,
+ (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment);
+#endif
+ return entry;
+}
+
+WINPR_PSLIST_ENTRY InterlockedFlushSList(WINPR_PSLIST_HEADER ListHead)
+{
+ WINPR_SLIST_HEADER old;
+ WINPR_SLIST_HEADER newHeader;
+
+ if (!QueryDepthSList(ListHead))
+ return NULL;
+
+#ifdef _WIN64
+ newHeader.s.Alignment = 0;
+ newHeader.s.Region = 0;
+ newHeader.HeaderX64.HeaderType = 1;
+
+ while (1)
+ {
+ old = *ListHead;
+ newHeader.HeaderX64.Sequence = old.HeaderX64.Sequence + 1;
+
+ if (InterlockedCompareExchange64((LONGLONG*)ListHead, newHeader.s.Alignment,
+ old.s.Alignment))
+ {
+ InterlockedCompareExchange64(&((LONGLONG*)ListHead)[1], newHeader.s.Region,
+ old.s.Region);
+ break;
+ }
+ }
+
+ return (PSLIST_ENTRY)(((ULONG_PTR)old.HeaderX64.NextEntry) << 4);
+#else
+ newHeader.Alignment = 0;
+
+ do
+ {
+ old = *ListHead;
+ newHeader.s.Sequence = old.s.Sequence + 1;
+
+ if (old.Alignment > INT64_MAX)
+ return NULL;
+ if (newHeader.Alignment > INT64_MAX)
+ return NULL;
+ if (ListHead->Alignment > INT64_MAX)
+ return NULL;
+ } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment,
+ (LONGLONG)newHeader.Alignment,
+ (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment);
+
+ return old.s.Next.Next;
+#endif
+}
+
+USHORT QueryDepthSList(WINPR_PSLIST_HEADER ListHead)
+{
+#ifdef _WIN64
+ return ListHead->HeaderX64.Depth;
+#else
+ return ListHead->s.Depth;
+#endif
+}
+
+LONG InterlockedIncrement(LONG volatile* Addend)
+{
+#if defined(__GNUC__) || defined(__clang__)
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
+ return __sync_add_and_fetch(Addend, 1);
+ WINPR_PRAGMA_DIAG_POP
+#else
+ return 0;
+#endif
+}
+
+LONG InterlockedDecrement(LONG volatile* Addend)
+{
+#if defined(__GNUC__) || defined(__clang__)
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
+ return __sync_sub_and_fetch(Addend, 1);
+ WINPR_PRAGMA_DIAG_POP
+#else
+ return 0;
+#endif
+}
+
+LONG InterlockedExchange(LONG volatile* Target, LONG Value)
+{
+#if defined(__GNUC__) || defined(__clang__)
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
+ return __sync_val_compare_and_swap(Target, *Target, Value);
+ WINPR_PRAGMA_DIAG_POP
+#else
+ return 0;
+#endif
+}
+
+LONG InterlockedExchangeAdd(LONG volatile* Addend, LONG Value)
+{
+#if defined(__GNUC__) || defined(__clang__)
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
+ return __sync_fetch_and_add(Addend, Value);
+ WINPR_PRAGMA_DIAG_POP
+#else
+ return 0;
+#endif
+}
+
+LONG InterlockedCompareExchange(LONG volatile* Destination, LONG Exchange, LONG Comperand)
+{
+#if defined(__GNUC__) || defined(__clang__)
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
+ return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
+ WINPR_PRAGMA_DIAG_POP
+#else
+ return 0;
+#endif
+}
+
+PVOID InterlockedCompareExchangePointer(PVOID volatile* Destination, PVOID Exchange,
+ PVOID Comperand)
+{
+#if defined(__GNUC__) || defined(__clang__)
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
+ return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
+ WINPR_PRAGMA_DIAG_POP
+#else
+ return 0;
+#endif
+}
+
+#endif /* _WIN32 */
+
+#if defined(_WIN32) && !defined(WINPR_INTERLOCKED_COMPARE_EXCHANGE64)
+
+/* InterlockedCompareExchange64 already defined */
+
+#elif defined(_WIN32) && defined(WINPR_INTERLOCKED_COMPARE_EXCHANGE64)
+
+static volatile HANDLE mutex = NULL;
+
+BOOL static_mutex_lock(volatile HANDLE* static_mutex)
+{
+ if (*static_mutex == NULL)
+ {
+ HANDLE handle;
+
+ if (!(handle = CreateMutex(NULL, FALSE, NULL)))
+ return FALSE;
+
+ if (InterlockedCompareExchangePointer((PVOID*)static_mutex, (PVOID)handle, NULL) != NULL)
+ CloseHandle(handle);
+ }
+
+ return (WaitForSingleObject(*static_mutex, INFINITE) == WAIT_OBJECT_0);
+}
+
+LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange,
+ LONGLONG Comperand)
+{
+ LONGLONG previousValue = 0;
+ BOOL locked = static_mutex_lock(&mutex);
+
+ previousValue = *Destination;
+
+ if (*Destination == Comperand)
+ *Destination = Exchange;
+
+ if (locked)
+ ReleaseMutex(mutex);
+ else
+ fprintf(stderr, "WARNING: InterlockedCompareExchange64 operation might have failed\n");
+
+ return previousValue;
+}
+
+#elif (defined(ANDROID) && ANDROID) || \
+ (defined(__GNUC__) && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8))
+
+#include <pthread.h>
+
+static pthread_mutex_t mutex;
+
+LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange,
+ LONGLONG Comperand)
+{
+ LONGLONG previousValue = 0;
+
+ pthread_mutex_lock(&mutex);
+
+ previousValue = *Destination;
+
+ if (*Destination == Comperand)
+ *Destination = Exchange;
+
+ pthread_mutex_unlock(&mutex);
+
+ return previousValue;
+}
+
+#else
+
+LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange,
+ LONGLONG Comperand)
+{
+#if defined(__GNUC__) || defined(__clang__)
+ WINPR_PRAGMA_DIAG_PUSH
+ WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
+ return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
+ WINPR_PRAGMA_DIAG_POP
+#else
+ return 0;
+#endif
+}
+
+#endif
+
+/* Doubly-Linked List */
+
+/**
+ * Kernel-Mode Basics: Windows Linked Lists:
+ * http://www.osronline.com/article.cfm?article=499
+ *
+ * Singly and Doubly Linked Lists:
+ * http://msdn.microsoft.com/en-us/library/windows/hardware/ff563802/
+ */
+
+VOID InitializeListHead(WINPR_PLIST_ENTRY ListHead)
+{
+ ListHead->Flink = ListHead->Blink = ListHead;
+}
+
+BOOL IsListEmpty(const WINPR_LIST_ENTRY* ListHead)
+{
+ return (BOOL)(ListHead->Flink == ListHead);
+}
+
+BOOL RemoveEntryList(WINPR_PLIST_ENTRY Entry)
+{
+ WINPR_PLIST_ENTRY OldFlink = NULL;
+ WINPR_PLIST_ENTRY OldBlink = NULL;
+
+ OldFlink = Entry->Flink;
+ OldBlink = Entry->Blink;
+ OldFlink->Blink = OldBlink;
+ OldBlink->Flink = OldFlink;
+
+ return (BOOL)(OldFlink == OldBlink);
+}
+
+VOID InsertHeadList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry)
+{
+ WINPR_PLIST_ENTRY OldFlink = NULL;
+
+ OldFlink = ListHead->Flink;
+ Entry->Flink = OldFlink;
+ Entry->Blink = ListHead;
+ OldFlink->Blink = Entry;
+ ListHead->Flink = Entry;
+}
+
+WINPR_PLIST_ENTRY RemoveHeadList(WINPR_PLIST_ENTRY ListHead)
+{
+ WINPR_PLIST_ENTRY Flink = NULL;
+ WINPR_PLIST_ENTRY Entry = NULL;
+
+ Entry = ListHead->Flink;
+ Flink = Entry->Flink;
+ ListHead->Flink = Flink;
+ Flink->Blink = ListHead;
+
+ return Entry;
+}
+
+VOID InsertTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry)
+{
+ WINPR_PLIST_ENTRY OldBlink = NULL;
+
+ OldBlink = ListHead->Blink;
+ Entry->Flink = ListHead;
+ Entry->Blink = OldBlink;
+ OldBlink->Flink = Entry;
+ ListHead->Blink = Entry;
+}
+
+WINPR_PLIST_ENTRY RemoveTailList(WINPR_PLIST_ENTRY ListHead)
+{
+ WINPR_PLIST_ENTRY Blink = NULL;
+ WINPR_PLIST_ENTRY Entry = NULL;
+
+ Entry = ListHead->Blink;
+ Blink = Entry->Blink;
+ ListHead->Blink = Blink;
+ Blink->Flink = ListHead;
+
+ return Entry;
+}
+
+VOID AppendTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY ListToAppend)
+{
+ WINPR_PLIST_ENTRY ListEnd = ListHead->Blink;
+
+ ListHead->Blink->Flink = ListToAppend;
+ ListHead->Blink = ListToAppend->Blink;
+ ListToAppend->Blink->Flink = ListHead;
+ ListToAppend->Blink = ListEnd;
+}
+
+VOID PushEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead, WINPR_PSINGLE_LIST_ENTRY Entry)
+{
+ Entry->Next = ListHead->Next;
+ ListHead->Next = Entry;
+}
+
+WINPR_PSINGLE_LIST_ENTRY PopEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead)
+{
+ WINPR_PSINGLE_LIST_ENTRY FirstEntry = NULL;
+
+ FirstEntry = ListHead->Next;
+
+ if (FirstEntry != NULL)
+ ListHead->Next = FirstEntry->Next;
+
+ return FirstEntry;
+}