Coverage Report

Created: 2026-05-30 06:41

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/interlocked/interlocked.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Interlocked Singly-Linked Lists
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 *
7
 * Licensed under the Apache License, Version 2.0 (the "License");
8
 * you may not use this file except in compliance with the License.
9
 * You may obtain a copy of the License at
10
 *
11
 *     http://www.apache.org/licenses/LICENSE-2.0
12
 *
13
 * Unless required by applicable law or agreed to in writing, software
14
 * distributed under the License is distributed on an "AS IS" BASIS,
15
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
 * See the License for the specific language governing permissions and
17
 * limitations under the License.
18
 */
19
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)
38
VOID InitializeSListHead(WINPR_PSLIST_HEADER ListHead)
39
0
{
40
0
  WINPR_ASSERT(ListHead);
41
#ifdef _WIN64
42
  ListHead->s.Alignment = 0;
43
  ListHead->s.Region = 0;
44
  ListHead->Header8.Init = 1;
45
#else
46
0
  ListHead->Alignment = 0;
47
0
#endif
48
0
}
49
50
WINPR_PSLIST_ENTRY InterlockedPushEntrySList(WINPR_PSLIST_HEADER ListHead,
51
                                             WINPR_PSLIST_ENTRY ListEntry)
52
0
{
53
0
  WINPR_SLIST_HEADER old = WINPR_C_ARRAY_INIT;
54
0
  WINPR_SLIST_HEADER newHeader = WINPR_C_ARRAY_INIT;
55
56
0
  WINPR_ASSERT(ListHead);
57
0
  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
0
  newHeader.s.Next.Next = ListEntry;
81
82
0
  do
83
0
  {
84
0
    old = *ListHead;
85
0
    ListEntry->Next = old.s.Next.Next;
86
0
    newHeader.s.Depth = old.s.Depth + 1;
87
0
    newHeader.s.Sequence = old.s.Sequence + 1;
88
0
    if (old.Alignment > INT64_MAX)
89
0
      return nullptr;
90
0
    if (newHeader.Alignment > INT64_MAX)
91
0
      return nullptr;
92
0
    if (ListHead->Alignment > INT64_MAX)
93
0
      return nullptr;
94
0
  } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment,
95
0
                                        (LONGLONG)newHeader.Alignment,
96
0
                                        (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment);
97
98
0
  return old.s.Next.Next;
99
0
#endif
100
0
}
101
102
WINPR_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
0
{
107
0
  WINPR_ASSERT(ListHead);
108
0
  WINPR_ASSERT(List);
109
0
  WINPR_ASSERT(ListEnd);
110
111
0
  WLog_ERR("TODO", "TODO: implement");
112
#ifdef _WIN64
113
114
#else
115
116
0
#endif
117
0
  return nullptr;
118
0
}
119
120
WINPR_PSLIST_ENTRY InterlockedPopEntrySList(WINPR_PSLIST_HEADER ListHead)
121
0
{
122
0
  WINPR_SLIST_HEADER old = WINPR_C_ARRAY_INIT;
123
0
  WINPR_SLIST_HEADER newHeader = WINPR_C_ARRAY_INIT;
124
0
  WINPR_PSLIST_ENTRY entry = nullptr;
125
126
0
  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
0
  do
151
0
  {
152
0
    old = *ListHead;
153
154
0
    entry = old.s.Next.Next;
155
156
0
    if (!entry)
157
0
      return nullptr;
158
159
0
    newHeader.s.Next.Next = entry->Next;
160
0
    newHeader.s.Depth = old.s.Depth - 1;
161
0
    newHeader.s.Sequence = old.s.Sequence + 1;
162
163
0
    if (old.Alignment > INT64_MAX)
164
0
      return nullptr;
165
0
    if (newHeader.Alignment > INT64_MAX)
166
0
      return nullptr;
167
0
    if (ListHead->Alignment > INT64_MAX)
168
0
      return nullptr;
169
0
  } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment,
170
0
                                        (LONGLONG)newHeader.Alignment,
171
0
                                        (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment);
172
0
#endif
173
0
  return entry;
174
0
}
175
176
WINPR_PSLIST_ENTRY InterlockedFlushSList(WINPR_PSLIST_HEADER ListHead)
177
0
{
178
0
  WINPR_SLIST_HEADER old = WINPR_C_ARRAY_INIT;
179
0
  WINPR_SLIST_HEADER newHeader = WINPR_C_ARRAY_INIT;
180
181
0
  WINPR_ASSERT(ListHead);
182
0
  if (!QueryDepthSList(ListHead))
183
0
    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
0
  newHeader.Alignment = 0;
206
207
0
  do
208
0
  {
209
0
    old = *ListHead;
210
0
    newHeader.s.Sequence = old.s.Sequence + 1;
211
212
0
    if (old.Alignment > INT64_MAX)
213
0
      return nullptr;
214
0
    if (newHeader.Alignment > INT64_MAX)
215
0
      return nullptr;
216
0
    if (ListHead->Alignment > INT64_MAX)
217
0
      return nullptr;
218
0
  } while (InterlockedCompareExchange64((LONGLONG*)&ListHead->Alignment,
219
0
                                        (LONGLONG)newHeader.Alignment,
220
0
                                        (LONGLONG)old.Alignment) != (LONGLONG)old.Alignment);
221
222
0
  return old.s.Next.Next;
223
0
#endif
224
0
}
225
226
USHORT QueryDepthSList(WINPR_PSLIST_HEADER ListHead)
227
0
{
228
0
  WINPR_ASSERT(ListHead);
229
230
#ifdef _WIN64
231
  return ListHead->HeaderX64.Depth;
232
#else
233
0
  return ListHead->s.Depth;
234
0
#endif
235
0
}
236
237
#endif
238
239
LONG InterlockedIncrement(LONG volatile* Addend)
240
8.27k
{
241
8.27k
  WINPR_ASSERT(Addend);
242
243
8.27k
#if defined(__GNUC__) || defined(__clang__)
244
8.27k
  WINPR_PRAGMA_DIAG_PUSH
245
8.27k
  WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
246
8.27k
  return __sync_add_and_fetch(Addend, 1);
247
8.27k
  WINPR_PRAGMA_DIAG_POP
248
#else
249
  return 0;
250
#endif
251
8.27k
}
252
253
LONG InterlockedDecrement(LONG volatile* Addend)
254
8.27k
{
255
8.27k
  WINPR_ASSERT(Addend);
256
257
8.27k
#if defined(__GNUC__) || defined(__clang__)
258
8.27k
  WINPR_PRAGMA_DIAG_PUSH
259
8.27k
  WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
260
8.27k
  return __sync_sub_and_fetch(Addend, 1);
261
8.27k
  WINPR_PRAGMA_DIAG_POP
262
#else
263
  return 0;
264
#endif
265
8.27k
}
266
267
LONG InterlockedExchange(LONG volatile* Target, LONG Value)
268
0
{
269
0
  WINPR_ASSERT(Target);
270
271
0
#if defined(__GNUC__) || defined(__clang__)
272
0
  WINPR_PRAGMA_DIAG_PUSH
273
0
  WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
274
0
  return __sync_val_compare_and_swap(Target, *Target, Value);
275
0
  WINPR_PRAGMA_DIAG_POP
276
#else
277
  return 0;
278
#endif
279
0
}
280
281
LONG InterlockedExchangeAdd(LONG volatile* Addend, LONG Value)
282
0
{
283
0
  WINPR_ASSERT(Addend);
284
285
0
#if defined(__GNUC__) || defined(__clang__)
286
0
  WINPR_PRAGMA_DIAG_PUSH
287
0
  WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
288
0
  return __sync_fetch_and_add(Addend, Value);
289
0
  WINPR_PRAGMA_DIAG_POP
290
#else
291
  return 0;
292
#endif
293
0
}
294
295
LONG InterlockedCompareExchange(LONG volatile* Destination, LONG Exchange, LONG Comperand)
296
0
{
297
0
  WINPR_ASSERT(Destination);
298
299
0
#if defined(__GNUC__) || defined(__clang__)
300
0
  WINPR_PRAGMA_DIAG_PUSH
301
0
  WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
302
0
  return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
303
0
  WINPR_PRAGMA_DIAG_POP
304
#else
305
  return 0;
306
#endif
307
0
}
308
309
PVOID InterlockedCompareExchangePointer(PVOID volatile* Destination, PVOID Exchange,
310
                                        PVOID Comperand)
311
4
{
312
4
  WINPR_ASSERT(Destination);
313
314
4
#if defined(__GNUC__) || defined(__clang__)
315
4
  WINPR_PRAGMA_DIAG_PUSH
316
4
  WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
317
4
  return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
318
4
  WINPR_PRAGMA_DIAG_POP
319
#else
320
  return 0;
321
#endif
322
4
}
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
332
static volatile HANDLE mutex = nullptr;
333
334
BOOL 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
351
LONGLONG 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
376
static pthread_mutex_t mutex;
377
378
LONGLONG 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
397
LONGLONG InterlockedCompareExchange64(LONGLONG volatile* Destination, LONGLONG Exchange,
398
                                      LONGLONG Comperand)
399
0
{
400
0
  WINPR_ASSERT(Destination);
401
402
0
#if defined(__GNUC__) || defined(__clang__)
403
0
  WINPR_PRAGMA_DIAG_PUSH
404
0
  WINPR_PRAGMA_DIAG_IGNORED_ATOMIC_SEQ_CST
405
0
  return __sync_val_compare_and_swap(Destination, Comperand, Exchange);
406
0
  WINPR_PRAGMA_DIAG_POP
407
#else
408
  return 0;
409
#endif
410
0
}
411
412
#endif
413
414
#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
415
/* Doubly-Linked List */
416
417
/**
418
 * Kernel-Mode Basics: Windows Linked Lists:
419
 * http://www.osronline.com/article.cfm?article=499
420
 *
421
 * Singly and Doubly Linked Lists:
422
 * http://msdn.microsoft.com/en-us/library/windows/hardware/ff563802/
423
 */
424
425
VOID InitializeListHead(WINPR_PLIST_ENTRY ListHead)
426
0
{
427
0
  WINPR_ASSERT(ListHead);
428
0
  ListHead->Flink = ListHead->Blink = ListHead;
429
0
}
430
431
BOOL IsListEmpty(const WINPR_LIST_ENTRY* ListHead)
432
0
{
433
0
  WINPR_ASSERT(ListHead);
434
0
  return (BOOL)(ListHead->Flink == ListHead);
435
0
}
436
437
BOOL RemoveEntryList(WINPR_PLIST_ENTRY Entry)
438
0
{
439
0
  WINPR_ASSERT(Entry);
440
0
  WINPR_PLIST_ENTRY OldFlink = Entry->Flink;
441
0
  WINPR_ASSERT(OldFlink);
442
443
0
  WINPR_PLIST_ENTRY OldBlink = Entry->Blink;
444
0
  WINPR_ASSERT(OldBlink);
445
446
0
  OldFlink->Blink = OldBlink;
447
0
  OldBlink->Flink = OldFlink;
448
449
0
  return (BOOL)(OldFlink == OldBlink);
450
0
}
451
452
VOID InsertHeadList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry)
453
0
{
454
0
  WINPR_ASSERT(ListHead);
455
0
  WINPR_ASSERT(Entry);
456
457
0
  WINPR_PLIST_ENTRY OldFlink = ListHead->Flink;
458
0
  WINPR_ASSERT(OldFlink);
459
460
0
  Entry->Flink = OldFlink;
461
0
  Entry->Blink = ListHead;
462
0
  OldFlink->Blink = Entry;
463
0
  ListHead->Flink = Entry;
464
0
}
465
466
WINPR_PLIST_ENTRY RemoveHeadList(WINPR_PLIST_ENTRY ListHead)
467
0
{
468
0
  WINPR_ASSERT(ListHead);
469
470
0
  WINPR_PLIST_ENTRY Entry = ListHead->Flink;
471
0
  WINPR_ASSERT(Entry);
472
473
0
  WINPR_PLIST_ENTRY Flink = Entry->Flink;
474
0
  WINPR_ASSERT(Flink);
475
476
0
  ListHead->Flink = Flink;
477
0
  Flink->Blink = ListHead;
478
479
0
  return Entry;
480
0
}
481
482
VOID InsertTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY Entry)
483
0
{
484
0
  WINPR_ASSERT(ListHead);
485
0
  WINPR_ASSERT(Entry);
486
487
0
  WINPR_PLIST_ENTRY OldBlink = ListHead->Blink;
488
0
  WINPR_ASSERT(OldBlink);
489
490
0
  Entry->Flink = ListHead;
491
0
  Entry->Blink = OldBlink;
492
0
  OldBlink->Flink = Entry;
493
0
  ListHead->Blink = Entry;
494
0
}
495
496
WINPR_PLIST_ENTRY RemoveTailList(WINPR_PLIST_ENTRY ListHead)
497
0
{
498
0
  WINPR_ASSERT(ListHead);
499
500
0
  WINPR_PLIST_ENTRY Entry = ListHead->Blink;
501
0
  WINPR_ASSERT(Entry);
502
503
0
  WINPR_PLIST_ENTRY Blink = Entry->Blink;
504
0
  WINPR_ASSERT(Blink);
505
506
0
  ListHead->Blink = Blink;
507
0
  Blink->Flink = ListHead;
508
509
0
  return Entry;
510
0
}
511
512
VOID AppendTailList(WINPR_PLIST_ENTRY ListHead, WINPR_PLIST_ENTRY ListToAppend)
513
0
{
514
0
  WINPR_ASSERT(ListHead);
515
0
  WINPR_ASSERT(ListToAppend);
516
517
0
  WINPR_PLIST_ENTRY ListEnd = ListHead->Blink;
518
519
0
  ListHead->Blink->Flink = ListToAppend;
520
0
  ListHead->Blink = ListToAppend->Blink;
521
0
  ListToAppend->Blink->Flink = ListHead;
522
0
  ListToAppend->Blink = ListEnd;
523
0
}
524
525
VOID PushEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead, WINPR_PSINGLE_LIST_ENTRY Entry)
526
0
{
527
0
  WINPR_ASSERT(ListHead);
528
0
  WINPR_ASSERT(Entry);
529
530
0
  Entry->Next = ListHead->Next;
531
0
  ListHead->Next = Entry;
532
0
}
533
534
WINPR_PSINGLE_LIST_ENTRY PopEntryList(WINPR_PSINGLE_LIST_ENTRY ListHead)
535
0
{
536
0
  WINPR_ASSERT(ListHead);
537
0
  WINPR_PSINGLE_LIST_ENTRY FirstEntry = ListHead->Next;
538
539
0
  if (FirstEntry != nullptr)
540
0
    ListHead->Next = FirstEntry->Next;
541
542
0
  return FirstEntry;
543
0
}
544
#endif