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