Coverage Report

Created: 2025-07-01 06:46

/src/FreeRDP/winpr/libwinpr/thread/apc.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * APC implementation
4
 *
5
 * Copyright 2021 David Fort <contact@hardening-consulting.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
#ifndef _WIN32
20
21
#include "apc.h"
22
#include "thread.h"
23
#include "../log.h"
24
#include "../synch/pollset.h"
25
#include <winpr/assert.h>
26
27
#define TAG WINPR_TAG("apc")
28
29
BOOL apc_init(APC_QUEUE* apc)
30
0
{
31
0
  pthread_mutexattr_t attr;
32
0
  BOOL ret = FALSE;
33
34
0
  WINPR_ASSERT(apc);
35
36
0
  pthread_mutexattr_init(&attr);
37
0
  if (pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE) != 0)
38
0
  {
39
0
    WLog_ERR(TAG, "failed to initialize mutex attributes to recursive");
40
0
    return FALSE;
41
0
  }
42
43
0
  memset(apc, 0, sizeof(*apc));
44
45
0
  if (pthread_mutex_init(&apc->mutex, &attr) != 0)
46
0
  {
47
0
    WLog_ERR(TAG, "failed to initialize main thread APC mutex");
48
0
    goto out;
49
0
  }
50
51
0
  ret = TRUE;
52
0
out:
53
0
  pthread_mutexattr_destroy(&attr);
54
0
  return ret;
55
0
}
56
57
BOOL apc_uninit(APC_QUEUE* apc)
58
0
{
59
0
  WINPR_ASSERT(apc);
60
0
  return pthread_mutex_destroy(&apc->mutex) == 0;
61
0
}
62
63
void apc_register(WINPR_THREAD* thread, WINPR_APC_ITEM* addItem)
64
0
{
65
0
  WINPR_APC_ITEM** nextp = NULL;
66
0
  APC_QUEUE* apc = NULL;
67
68
0
  WINPR_ASSERT(thread);
69
0
  WINPR_ASSERT(addItem);
70
71
0
  apc = &thread->apc;
72
0
  WINPR_ASSERT(apc);
73
74
0
  pthread_mutex_lock(&apc->mutex);
75
0
  if (apc->tail)
76
0
  {
77
0
    nextp = &apc->tail->next;
78
0
    addItem->last = apc->tail;
79
0
  }
80
0
  else
81
0
  {
82
0
    nextp = &apc->head;
83
0
  }
84
85
0
  *nextp = addItem;
86
0
  apc->tail = addItem;
87
0
  apc->length++;
88
89
0
  addItem->markedForRemove = FALSE;
90
0
  addItem->boundThread = GetCurrentThreadId();
91
0
  addItem->linked = TRUE;
92
0
  pthread_mutex_unlock(&apc->mutex);
93
0
}
94
95
static INLINE void apc_item_remove(APC_QUEUE* apc, WINPR_APC_ITEM* item)
96
0
{
97
0
  WINPR_ASSERT(apc);
98
0
  WINPR_ASSERT(item);
99
100
0
  if (!item->last)
101
0
    apc->head = item->next;
102
0
  else
103
0
    item->last->next = item->next;
104
105
0
  if (!item->next)
106
0
    apc->tail = item->last;
107
0
  else
108
0
    item->next->last = item->last;
109
110
0
  apc->length--;
111
0
}
112
113
APC_REMOVE_RESULT apc_remove(WINPR_APC_ITEM* item)
114
0
{
115
0
  WINPR_THREAD* thread = winpr_GetCurrentThread();
116
0
  APC_QUEUE* apc = NULL;
117
0
  APC_REMOVE_RESULT ret = APC_REMOVE_OK;
118
119
0
  WINPR_ASSERT(item);
120
121
0
  if (!item->linked)
122
0
    return APC_REMOVE_OK;
123
124
0
  if (item->boundThread != GetCurrentThreadId())
125
0
  {
126
0
    WLog_ERR(TAG, "removing an APC entry should be done in the creating thread");
127
0
    return APC_REMOVE_ERROR;
128
0
  }
129
130
0
  if (!thread)
131
0
  {
132
0
    WLog_ERR(TAG, "unable to retrieve current thread");
133
0
    return APC_REMOVE_ERROR;
134
0
  }
135
136
0
  apc = &thread->apc;
137
0
  WINPR_ASSERT(apc);
138
139
0
  pthread_mutex_lock(&apc->mutex);
140
0
  if (apc->treatingCompletions)
141
0
  {
142
0
    item->markedForRemove = TRUE;
143
0
    ret = APC_REMOVE_DELAY_FREE;
144
0
    goto out;
145
0
  }
146
147
0
  apc_item_remove(apc, item);
148
149
0
out:
150
0
  pthread_mutex_unlock(&apc->mutex);
151
0
  item->boundThread = 0xFFFFFFFF;
152
0
  item->linked = FALSE;
153
0
  return ret;
154
0
}
155
156
BOOL apc_collectFds(WINPR_THREAD* thread, WINPR_POLL_SET* set, BOOL* haveAutoSignaled)
157
0
{
158
0
  WINPR_APC_ITEM* item = NULL;
159
0
  BOOL ret = FALSE;
160
0
  APC_QUEUE* apc = NULL;
161
162
0
  WINPR_ASSERT(thread);
163
0
  WINPR_ASSERT(haveAutoSignaled);
164
165
0
  apc = &thread->apc;
166
0
  WINPR_ASSERT(apc);
167
168
0
  *haveAutoSignaled = FALSE;
169
0
  pthread_mutex_lock(&apc->mutex);
170
0
  item = apc->head;
171
0
  for (; item; item = item->next)
172
0
  {
173
0
    if (item->alwaysSignaled)
174
0
    {
175
0
      *haveAutoSignaled = TRUE;
176
0
    }
177
0
    else if (!pollset_add(set, item->pollFd, item->pollMode))
178
0
      goto out;
179
0
  }
180
181
0
  ret = TRUE;
182
0
out:
183
0
  pthread_mutex_unlock(&apc->mutex);
184
0
  return ret;
185
0
}
186
187
int apc_executeCompletions(WINPR_THREAD* thread, WINPR_POLL_SET* set, size_t startIndex)
188
0
{
189
0
  APC_QUEUE* apc = NULL;
190
0
  WINPR_APC_ITEM* nextItem = NULL;
191
0
  size_t idx = startIndex;
192
0
  int ret = 0;
193
194
0
  WINPR_ASSERT(thread);
195
196
0
  apc = &thread->apc;
197
0
  WINPR_ASSERT(apc);
198
199
0
  pthread_mutex_lock(&apc->mutex);
200
0
  apc->treatingCompletions = TRUE;
201
202
  /* first pass to compute signaled items */
203
0
  for (WINPR_APC_ITEM* item = apc->head; item; item = item->next)
204
0
  {
205
0
    item->isSignaled = item->alwaysSignaled || pollset_isSignaled(set, idx);
206
0
    if (!item->alwaysSignaled)
207
0
      idx++;
208
0
  }
209
210
  /* second pass: run completions */
211
0
  for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem)
212
0
  {
213
0
    if (item->isSignaled)
214
0
    {
215
0
      if (item->completion && !item->markedForRemove)
216
0
        item->completion(item->completionArgs);
217
0
      ret++;
218
0
    }
219
220
0
    nextItem = item->next;
221
0
  }
222
223
  /* third pass: to do final cleanup */
224
0
  for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem)
225
0
  {
226
0
    nextItem = item->next;
227
228
0
    if (item->markedForRemove)
229
0
    {
230
0
      apc_item_remove(apc, item);
231
0
      if (item->markedForFree)
232
0
        free(item);
233
0
    }
234
0
  }
235
236
0
  apc->treatingCompletions = FALSE;
237
0
  pthread_mutex_unlock(&apc->mutex);
238
239
0
  return ret;
240
0
}
241
242
void apc_cleanupThread(WINPR_THREAD* thread)
243
0
{
244
0
  WINPR_APC_ITEM* item = NULL;
245
0
  WINPR_APC_ITEM* nextItem = NULL;
246
0
  APC_QUEUE* apc = NULL;
247
248
0
  WINPR_ASSERT(thread);
249
250
0
  apc = &thread->apc;
251
0
  WINPR_ASSERT(apc);
252
253
0
  pthread_mutex_lock(&apc->mutex);
254
0
  item = apc->head;
255
0
  for (; item; item = nextItem)
256
0
  {
257
0
    nextItem = item->next;
258
259
0
    if (item->type == APC_TYPE_HANDLE_FREE)
260
0
      item->completion(item->completionArgs);
261
262
0
    item->last = item->next = NULL;
263
0
    item->linked = FALSE;
264
0
    if (item->markedForFree)
265
0
      free(item);
266
0
  }
267
268
0
  apc->head = apc->tail = NULL;
269
0
  pthread_mutex_unlock(&apc->mutex);
270
0
}
271
272
#endif