FreeRDP
Loading...
Searching...
No Matches
interlocked.c
1
20#include <winpr/config.h>
21
22#include <winpr/assert.h>
23#include <winpr/wlog.h>
24#include <winpr/platform.h>
25#include <winpr/synch.h>
26#include <winpr/handle.h>
27
28#include <winpr/interlocked.h>
29
30/* Singly-Linked List */
31
32#ifndef _WIN32
33
34#include <stdio.h>
35#include <stdlib.h>
36
37#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
38VOID InitializeSListHead(WINPR_PSLIST_HEADER ListHead)
39{
40 WINPR_ASSERT(ListHead);
41#ifdef _WIN64
42 ListHead->s.Alignment = 0;
43 ListHead->s.Region = 0;
44 ListHead->Header8.Init = 1;
45#else
46 ListHead->Alignment = 0;
47#endif
48}
49
50WINPR_PSLIST_ENTRY InterlockedPushEntrySList(WINPR_PSLIST_HEADER ListHead,
51 WINPR_PSLIST_ENTRY ListEntry)
52{
53 WINPR_SLIST_HEADER old = WINPR_C_ARRAY_INIT;
54 WINPR_SLIST_HEADER newHeader = WINPR_C_ARRAY_INIT;
55
56 WINPR_ASSERT(ListHead);
57 WINPR_ASSERT(ListEntry);
58#ifdef _WIN64
59 newHeader.HeaderX64.NextEntry = (((ULONG_PTR)ListEntry) >> 4);
60
61 while (1)
62 {
63 old = *ListHead;
64
65 ListEntry->Next = (PSLIST_ENTRY)(((ULONG_PTR)old.HeaderX64.NextEntry) << 4);
66
67 newHeader.HeaderX64.Depth = old.HeaderX64.Depth + 1;
68 newHeader.HeaderX64.Sequence = old.HeaderX64.Sequence + 1;
69
70 if (InterlockedCompareExchange64((LONGLONG*)ListHead, newHeader).Alignment, old).Alignment))
71 {
72 InterlockedCompareExchange64(&((LONGLONG*)ListHead)[1], newHeader).Region,
73 old).Region);
74 break;
75 }
76 }
77
78 return (PSLIST_ENTRY)((ULONG_PTR)old.HeaderX64.NextEntry << 4);
79#else
80 newHeader.s.Next.Next = ListEntry;
81
82 do
83 {
84 old = *ListHead;
85 ListEntry->Next = old.s.Next.Next;
86 newHeader.s.Depth = old.s.Depth + 1;
87 newHeader.s.Sequence = old.s.Sequence + 1;
88 if (old.Alignment > INT64_MAX)
89 return nullptr;
90 if (newHeader.Alignment > INT64_MAX)
91 return nullptr;
92 if (ListHead->Alignment > INT64_MAX)
93 return nullptr;
94 } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment,
95 (LONGLONG)newHeader.Alignment,
96 (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment);
97
98 return old.s.Next.Next;
99#endif
100}
101
102WINPR_PSLIST_ENTRY InterlockedPushListSListEx(WINPR_ATTR_UNUSED WINPR_PSLIST_HEADER ListHead,
103 WINPR_ATTR_UNUSED WINPR_PSLIST_ENTRY List,
104 WINPR_ATTR_UNUSED WINPR_PSLIST_ENTRY ListEnd,
105 WINPR_ATTR_UNUSED ULONG Count)
106{
107 WINPR_ASSERT(ListHead);
108 WINPR_ASSERT(List);
109 WINPR_ASSERT(ListEnd);
110
111 WLog_ERR("TODO", "TODO: implement");
112#ifdef _WIN64
113
114#else
115
116#endif
117 return nullptr;
118}
119
120WINPR_PSLIST_ENTRY InterlockedPopEntrySList(WINPR_PSLIST_HEADER ListHead)
121{
122 WINPR_SLIST_HEADER old = WINPR_C_ARRAY_INIT;
123 WINPR_SLIST_HEADER newHeader = WINPR_C_ARRAY_INIT;
124 WINPR_PSLIST_ENTRY entry = nullptr;
125
126 WINPR_ASSERT(ListHead);
127
128#ifdef _WIN64
129 while (1)
130 {
131 old = *ListHead;
132
133 entry = (PSLIST_ENTRY)(((ULONG_PTR)old.HeaderX64.NextEntry) << 4);
134
135 if (!entry)
136 return nullptr;
137
138 newHeader.HeaderX64.NextEntry = ((ULONG_PTR)entry->Next) >> 4;
139 newHeader.HeaderX64.Depth = old.HeaderX64.Depth - 1;
140 newHeader.HeaderX64.Sequence = old.HeaderX64.Sequence - 1;
141
142 if (InterlockedCompareExchange64((LONGLONG*)ListHead, newHeader).Alignment, old).Alignment))
143 {
144 InterlockedCompareExchange64(&((LONGLONG*)ListHead)[1], newHeader).Region,
145 old).Region);
146 break;
147 }
148 }
149#else
150 do
151 {
152 old = *ListHead;
153
154 entry = old.s.Next.Next;
155
156 if (!entry)
157 return nullptr;
158
159 newHeader.s.Next.Next = entry->Next;
160 newHeader.s.Depth = old.s.Depth - 1;
161 newHeader.s.Sequence = old.s.Sequence + 1;
162
163 if (old.Alignment > INT64_MAX)
164 return nullptr;
165 if (newHeader.Alignment > INT64_MAX)
166 return nullptr;
167 if (ListHead->Alignment > INT64_MAX)
168 return nullptr;
169 } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment,
170 (LONGLONG)newHeader.Alignment,
171 (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment);
172#endif
173 return entry;
174}
175
176WINPR_PSLIST_ENTRY InterlockedFlushSList(WINPR_PSLIST_HEADER ListHead)
177{
178 WINPR_SLIST_HEADER old = WINPR_C_ARRAY_INIT;
179 WINPR_SLIST_HEADER newHeader = WINPR_C_ARRAY_INIT;
180
181 WINPR_ASSERT(ListHead);
182 if (!QueryDepthSList(ListHead))
183 return nullptr;
184
185#ifdef _WIN64
186 newHeader).Alignment = 0;
187 newHeader).Region = 0;
188 newHeader.HeaderX64.HeaderType = 1;
189
190 while (1)
191 {
192 old = *ListHead;
193 newHeader.HeaderX64.Sequence = old.HeaderX64.Sequence + 1;
194
195 if (InterlockedCompareExchange64((LONGLONG*)ListHead, newHeader).Alignment, old).Alignment))
196 {
197 InterlockedCompareExchange64(&((LONGLONG*)ListHead)[1], newHeader).Region,
198 old).Region);
199 break;
200 }
201 }
202
203 return (PSLIST_ENTRY)(((ULONG_PTR)old.HeaderX64.NextEntry) << 4);
204#else
205 newHeader.Alignment = 0;
206
207 do
208 {
209 old = *ListHead;
210 newHeader.s.Sequence = old.s.Sequence + 1;
211
212 if (old.Alignment > INT64_MAX)
213 return nullptr;
214 if (newHeader.Alignment > INT64_MAX)
215 return nullptr;
216 if (ListHead->Alignment > INT64_MAX)
217 return nullptr;
218 } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment,
219 (LONGLONG)newHeader.Alignment,
220 (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment);
221
222 return old.s.Next.Next;
223#endif
224}
225
226USHORT QueryDepthSList(WINPR_PSLIST_HEADER ListHead)
227{
228 WINPR_ASSERT(ListHead);
229
230#ifdef _WIN64
231 return ListHead->HeaderX64.Depth;
232#else
233 return ListHead->s.Depth;
234#endif
235}
236
237#endif
238
239LONG InterlockedIncrement(LONG volatile* Addend)
240{
241 WINPR_ASSERT(Addend);
242
243#if defined(__GNUC__) || defined(__clang__)
244 WINPR_PRAGMA_DIAG_PUSH
245 WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
246 return __sync_add_and_fetch(Addend, 1);
247 WINPR_PRAGMA_DIAG_POP
248#else
249 return 0;
250#endif
251}
252
253LONG InterlockedDecrement(LONG volatile* Addend)
254{
255 WINPR_ASSERT(Addend);
256
257#if defined(__GNUC__) || defined(__clang__)
258 WINPR_PRAGMA_DIAG_PUSH
259 WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
260 return __sync_sub_and_fetch(Addend, 1);
261 WINPR_PRAGMA_DIAG_POP
262#else
263 return 0;
264#endif
265}
266
267LONG InterlockedExchange(LONG volatile* Target, LONG Value)
268{
269 WINPR_ASSERT(Target);
270
271#if defined(__GNUC__) || defined(__clang__)
272 WINPR_PRAGMA_DIAG_PUSH
273 WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
274 return __sync_val_compare_and_swap(Target, *Target, Value);
275 WINPR_PRAGMA_DIAG_POP
276#else
277 return 0;
278#endif
279}
280
281LONG InterlockedExchangeAdd(LONG volatile* Addend, LONG Value)
282{
283 WINPR_ASSERT(Addend);
284
285#if defined(__GNUC__) || defined(__clang__)
286 WINPR_PRAGMA_DIAG_PUSH
287 WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
288 return __sync_fetch_and_add(Addend, Value);
289 WINPR_PRAGMA_DIAG_POP
290#else
291 return 0;
292#endif
293}
294
295LONG InterlockedCompareExchange(LONG volatile* Destination, LONG Exchange, LONG Comperand)
296{
297 WINPR_ASSERT(Destination);
298
299#if defined(__GNUC__) || defined(__clang__)
300 WINPR_PRAGMA_DIAG_PUSH
301 WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
302 return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
303 WINPR_PRAGMA_DIAG_POP
304#else
305 return 0;
306#endif
307}
308
309PVOID InterlockedCompareExchangePointer(PVOID volatile* Destination, PVOID Exchange,
310 PVOID Comperand)
311{
312 WINPR_ASSERT(Destination);
313
314#if defined(__GNUC__) || defined(__clang__)
315 WINPR_PRAGMA_DIAG_PUSH
316 WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
317 return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
318 WINPR_PRAGMA_DIAG_POP
319#else
320 return 0;
321#endif
322}
323
324#endif /* _WIN32 */
325
326#if defined(_WIN32) && !defined(WINPR_INTERLOCKED_COMPARE_EXCHANGE64)
327
328/* InterlockedCompareExchange64 already defined */
329
330#elif defined(_WIN32) && defined(WINPR_INTERLOCKED_COMPARE_EXCHANGE64)
331
332static volatile HANDLE mutex = nullptr;
333
334BOOL static_mutex_lock(volatile HANDLE* static_mutex)
335{
336 if (*static_mutex == nullptr)
337 {
338 HANDLE handle;
339
340 if (!(handle = CreateMutex(nullptr, FALSE, nullptr)))
341 return FALSE;
342
343 if (InterlockedCompareExchangePointer((PVOID*)static_mutex, (PVOID)handle, nullptr) !=
344 nullptr)
345 (void)CloseHandle(handle);
346 }
347
348 return (WaitForSingleObject(*static_mutex, INFINITE) == WAIT_OBJECT_0);
349}
350
351LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange,
352 LONGLONG Comperand)
353{
354 LONGLONG previousValue = 0;
355 BOOL locked = static_mutex_lock(&mutex);
356
357 previousValue = *Destination;
358
359 if (*Destination == Comperand)
360 *Destination = Exchange;
361
362 if (locked)
363 (void)ReleaseMutex(mutex);
364 else
365 (void)fprintf(stderr,
366 "WARNING: InterlockedCompareExchange64 operation might have failed\n");
367
368 return previousValue;
369}
370
371#elif (defined(ANDROID) && ANDROID) || \
372 (defined(__GNUC__) && !defined(__GCC_HAVE_SYNC_COMPARE_AND_SWAP_8))
373
374#include <pthread.h>
375
376static pthread_mutex_t mutex;
377
378LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange,
379 LONGLONG Comperand)
380{
381 LONGLONG previousValue = 0;
382
383 pthread_mutex_lock(&mutex);
384
385 previousValue = *Destination;
386
387 if (*Destination == Comperand)
388 *Destination = Exchange;
389
390 pthread_mutex_unlock(&mutex);
391
392 return previousValue;
393}
394
395#else
396
397LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange,
398 LONGLONG Comperand)
399{
400 WINPR_ASSERT(Destination);
401
402#if defined(__GNUC__) || defined(__clang__)
403 WINPR_PRAGMA_DIAG_PUSH
404 WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
405 return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
406 WINPR_PRAGMA_DIAG_POP
407#else
408 return 0;
409#endif
410}
411
412#endif
413
414#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
415/* Doubly-Linked List */
416
425VOID InitializeListHead(WINPR_PLIST_ENTRY ListHead)
426{
427 WINPR_ASSERT(ListHead);
428 ListHead->Flink = ListHead->Blink = ListHead;
429}
430
431BOOL IsListEmpty(const WINPR_LIST_ENTRY* ListHead)
432{
433 WINPR_ASSERT(ListHead);
434 return (BOOL)(ListHead->Flink == ListHead);
435}
436
437BOOL RemoveEntryList(WINPR_PLIST_ENTRY Entry)
438{
439 WINPR_ASSERT(Entry);
440 WINPR_PLIST_ENTRY OldFlink = Entry->Flink;
441 WINPR_ASSERT(OldFlink);
442
443 WINPR_PLIST_ENTRY OldBlink = Entry->Blink;
444 WINPR_ASSERT(OldBlink);
445
446 OldFlink->Blink = OldBlink;
447 OldBlink->Flink = OldFlink;
448
449 return (BOOL)(OldFlink == OldBlink);
450}
451
452VOID InsertHeadList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry)
453{
454 WINPR_ASSERT(ListHead);
455 WINPR_ASSERT(Entry);
456
457 WINPR_PLIST_ENTRY OldFlink = ListHead->Flink;
458 WINPR_ASSERT(OldFlink);
459
460 Entry->Flink = OldFlink;
461 Entry->Blink = ListHead;
462 OldFlink->Blink = Entry;
463 ListHead->Flink = Entry;
464}
465
466WINPR_PLIST_ENTRY RemoveHeadList(WINPR_PLIST_ENTRY ListHead)
467{
468 WINPR_ASSERT(ListHead);
469
470 WINPR_PLIST_ENTRY Entry = ListHead->Flink;
471 WINPR_ASSERT(Entry);
472
473 WINPR_PLIST_ENTRY Flink = Entry->Flink;
474 WINPR_ASSERT(Flink);
475
476 ListHead->Flink = Flink;
477 Flink->Blink = ListHead;
478
479 return Entry;
480}
481
482VOID InsertTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry)
483{
484 WINPR_ASSERT(ListHead);
485 WINPR_ASSERT(Entry);
486
487 WINPR_PLIST_ENTRY OldBlink = ListHead->Blink;
488 WINPR_ASSERT(OldBlink);
489
490 Entry->Flink = ListHead;
491 Entry->Blink = OldBlink;
492 OldBlink->Flink = Entry;
493 ListHead->Blink = Entry;
494}
495
496WINPR_PLIST_ENTRY RemoveTailList(WINPR_PLIST_ENTRY ListHead)
497{
498 WINPR_ASSERT(ListHead);
499
500 WINPR_PLIST_ENTRY Entry = ListHead->Blink;
501 WINPR_ASSERT(Entry);
502
503 WINPR_PLIST_ENTRY Blink = Entry->Blink;
504 WINPR_ASSERT(Blink);
505
506 ListHead->Blink = Blink;
507 Blink->Flink = ListHead;
508
509 return Entry;
510}
511
512VOID AppendTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY ListToAppend)
513{
514 WINPR_ASSERT(ListHead);
515 WINPR_ASSERT(ListToAppend);
516
517 WINPR_PLIST_ENTRY ListEnd = ListHead->Blink;
518
519 ListHead->Blink->Flink = ListToAppend;
520 ListHead->Blink = ListToAppend->Blink;
521 ListToAppend->Blink->Flink = ListHead;
522 ListToAppend->Blink = ListEnd;
523}
524
525VOID PushEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead, WINPR_PSINGLE_LIST_ENTRY Entry)
526{
527 WINPR_ASSERT(ListHead);
528 WINPR_ASSERT(Entry);
529
530 Entry->Next = ListHead->Next;
531 ListHead->Next = Entry;
532}
533
534WINPR_PSINGLE_LIST_ENTRY PopEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead)
535{
536 WINPR_ASSERT(ListHead);
537 WINPR_PSINGLE_LIST_ENTRY FirstEntry = ListHead->Next;
538
539 if (FirstEntry != nullptr)
540 ListHead->Next = FirstEntry->Next;
541
542 return FirstEntry;
543}
544#endif