Coverage Report

Created: 2023-09-25 06:56

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