Coverage Report

Created: 2024-05-20 06:11

/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
365k
{
31
365k
  pthread_mutexattr_t attr;
32
365k
  BOOL ret = FALSE;
33
34
365k
  WINPR_ASSERT(apc);
35
36
365k
  pthread_mutexattr_init(&attr);
37
365k
  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
365k
  memset(apc, 0, sizeof(*apc));
44
45
365k
  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
365k
  ret = TRUE;
52
365k
out:
53
365k
  pthread_mutexattr_destroy(&attr);
54
365k
  return ret;
55
365k
}
56
57
BOOL apc_uninit(APC_QUEUE* apc)
58
365k
{
59
365k
  WINPR_ASSERT(apc);
60
365k
  return pthread_mutex_destroy(&apc->mutex) == 0;
61
365k
}
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 idx)
188
0
{
189
0
  APC_QUEUE* apc = NULL;
190
0
  WINPR_APC_ITEM* nextItem = NULL;
191
0
  int ret = 0;
192
193
0
  WINPR_ASSERT(thread);
194
195
0
  apc = &thread->apc;
196
0
  WINPR_ASSERT(apc);
197
198
0
  pthread_mutex_lock(&apc->mutex);
199
0
  apc->treatingCompletions = TRUE;
200
201
  /* first pass to compute signaled items */
202
0
  for (WINPR_APC_ITEM* item = apc->head; item; item = item->next)
203
0
  {
204
0
    item->isSignaled = item->alwaysSignaled || pollset_isSignaled(set, idx);
205
0
    if (!item->alwaysSignaled)
206
0
      idx++;
207
0
  }
208
209
  /* second pass: run completions */
210
0
  for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem)
211
0
  {
212
0
    if (item->isSignaled)
213
0
    {
214
0
      if (item->completion && !item->markedForRemove)
215
0
        item->completion(item->completionArgs);
216
0
      ret++;
217
0
    }
218
219
0
    nextItem = item->next;
220
0
  }
221
222
  /* third pass: to do final cleanup */
223
0
  for (WINPR_APC_ITEM* item = apc->head; item; item = nextItem)
224
0
  {
225
0
    nextItem = item->next;
226
227
0
    if (item->markedForRemove)
228
0
    {
229
0
      apc_item_remove(apc, item);
230
0
      if (item->markedForFree)
231
0
        free(item);
232
0
    }
233
0
  }
234
235
0
  apc->treatingCompletions = FALSE;
236
0
  pthread_mutex_unlock(&apc->mutex);
237
238
0
  return ret;
239
0
}
240
241
void apc_cleanupThread(WINPR_THREAD* thread)
242
363k
{
243
363k
  WINPR_APC_ITEM* item = NULL;
244
363k
  WINPR_APC_ITEM* nextItem = NULL;
245
363k
  APC_QUEUE* apc = NULL;
246
247
363k
  WINPR_ASSERT(thread);
248
249
363k
  apc = &thread->apc;
250
363k
  WINPR_ASSERT(apc);
251
252
363k
  pthread_mutex_lock(&apc->mutex);
253
363k
  item = apc->head;
254
363k
  for (; item; item = nextItem)
255
0
  {
256
0
    nextItem = item->next;
257
258
0
    if (item->type == APC_TYPE_HANDLE_FREE)
259
0
      item->completion(item->completionArgs);
260
261
0
    item->last = item->next = NULL;
262
0
    item->linked = FALSE;
263
0
    if (item->markedForFree)
264
0
      free(item);
265
0
  }
266
267
363k
  apc->head = apc->tail = NULL;
268
363k
  pthread_mutex_unlock(&apc->mutex);
269
363k
}
270
271
#endif