/** * WinPR: Windows Portable Runtime * Interlocked Singly-Linked Lists * * Copyright 2012 Marc-Andre Moreau * * 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 #include #include #include #include /* Singly-Linked List */ #ifndef _WIN32 #include #include 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 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; }