Coverage Report

Created: 2024-05-20 06:11

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