Coverage Report

Created: 2024-05-20 06:11

/src/FreeRDP/winpr/libwinpr/utils/collections/MessageQueue.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Message Queue
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/crt.h>
23
#include <winpr/sysinfo.h>
24
#include <winpr/assert.h>
25
26
#include <winpr/collections.h>
27
28
struct s_wMessageQueue
29
{
30
  size_t head;
31
  size_t tail;
32
  size_t size;
33
  size_t capacity;
34
  BOOL closed;
35
  wMessage* array;
36
  CRITICAL_SECTION lock;
37
  HANDLE event;
38
39
  wObject object;
40
};
41
42
/**
43
 * Message Queue inspired from Windows:
44
 * http://msdn.microsoft.com/en-us/library/ms632590/
45
 */
46
47
/**
48
 * Properties
49
 */
50
51
wObject* MessageQueue_Object(wMessageQueue* queue)
52
7.99k
{
53
7.99k
  WINPR_ASSERT(queue);
54
7.99k
  return &queue->object;
55
7.99k
}
56
57
/**
58
 * Gets an event which is set when the queue is non-empty
59
 */
60
61
HANDLE MessageQueue_Event(wMessageQueue* queue)
62
0
{
63
0
  WINPR_ASSERT(queue);
64
0
  return queue->event;
65
0
}
66
67
/**
68
 * Gets the queue size
69
 */
70
71
size_t MessageQueue_Size(wMessageQueue* queue)
72
0
{
73
0
  WINPR_ASSERT(queue);
74
0
  return queue->size;
75
0
}
76
77
/**
78
 * Methods
79
 */
80
81
BOOL MessageQueue_Wait(wMessageQueue* queue)
82
0
{
83
0
  BOOL status = FALSE;
84
85
0
  WINPR_ASSERT(queue);
86
0
  if (WaitForSingleObject(queue->event, INFINITE) == WAIT_OBJECT_0)
87
0
    status = TRUE;
88
89
0
  return status;
90
0
}
91
92
static BOOL MessageQueue_EnsureCapacity(wMessageQueue* queue, size_t count)
93
37.9k
{
94
37.9k
  WINPR_ASSERT(queue);
95
96
37.9k
  if (queue->size + count >= queue->capacity)
97
37.9k
  {
98
37.9k
    wMessage* new_arr = NULL;
99
37.9k
    size_t old_capacity = queue->capacity;
100
37.9k
    size_t new_capacity = queue->capacity * 2;
101
102
37.9k
    if (new_capacity < queue->size + count)
103
37.9k
      new_capacity = queue->size + count;
104
105
37.9k
    new_arr = (wMessage*)realloc(queue->array, sizeof(wMessage) * new_capacity);
106
37.9k
    if (!new_arr)
107
0
      return FALSE;
108
37.9k
    queue->array = new_arr;
109
37.9k
    queue->capacity = new_capacity;
110
37.9k
    ZeroMemory(&(queue->array[old_capacity]), (new_capacity - old_capacity) * sizeof(wMessage));
111
112
    /* rearrange wrapped entries */
113
37.9k
    if (queue->tail <= queue->head)
114
37.9k
    {
115
37.9k
      CopyMemory(&(queue->array[old_capacity]), queue->array, queue->tail * sizeof(wMessage));
116
37.9k
      queue->tail += old_capacity;
117
37.9k
    }
118
37.9k
  }
119
120
37.9k
  return TRUE;
121
37.9k
}
122
123
BOOL MessageQueue_Dispatch(wMessageQueue* queue, const wMessage* message)
124
0
{
125
0
  wMessage* dst = NULL;
126
0
  BOOL ret = FALSE;
127
0
  WINPR_ASSERT(queue);
128
129
0
  if (!message)
130
0
    return FALSE;
131
132
0
  WINPR_ASSERT(queue);
133
0
  EnterCriticalSection(&queue->lock);
134
135
0
  if (queue->closed)
136
0
    goto out;
137
138
0
  if (!MessageQueue_EnsureCapacity(queue, 1))
139
0
    goto out;
140
141
0
  dst = &(queue->array[queue->tail]);
142
0
  *dst = *message;
143
0
  dst->time = GetTickCount64();
144
145
0
  queue->tail = (queue->tail + 1) % queue->capacity;
146
0
  queue->size++;
147
148
0
  if (queue->size > 0)
149
0
    SetEvent(queue->event);
150
151
0
  if (message->id == WMQ_QUIT)
152
0
    queue->closed = TRUE;
153
154
0
  ret = TRUE;
155
0
out:
156
0
  LeaveCriticalSection(&queue->lock);
157
0
  return ret;
158
0
}
159
160
BOOL MessageQueue_Post(wMessageQueue* queue, void* context, UINT32 type, void* wParam, void* lParam)
161
0
{
162
0
  wMessage message = { 0 };
163
164
0
  message.context = context;
165
0
  message.id = type;
166
0
  message.wParam = wParam;
167
0
  message.lParam = lParam;
168
0
  message.Free = NULL;
169
170
0
  return MessageQueue_Dispatch(queue, &message);
171
0
}
172
173
BOOL MessageQueue_PostQuit(wMessageQueue* queue, int nExitCode)
174
0
{
175
0
  return MessageQueue_Post(queue, NULL, WMQ_QUIT, (void*)(size_t)nExitCode, NULL);
176
0
}
177
178
int MessageQueue_Get(wMessageQueue* queue, wMessage* message)
179
0
{
180
0
  int status = -1;
181
182
0
  if (!MessageQueue_Wait(queue))
183
0
    return status;
184
185
0
  EnterCriticalSection(&queue->lock);
186
187
0
  if (queue->size > 0)
188
0
  {
189
0
    CopyMemory(message, &(queue->array[queue->head]), sizeof(wMessage));
190
0
    ZeroMemory(&(queue->array[queue->head]), sizeof(wMessage));
191
0
    queue->head = (queue->head + 1) % queue->capacity;
192
0
    queue->size--;
193
194
0
    if (queue->size < 1)
195
0
      ResetEvent(queue->event);
196
197
0
    status = (message->id != WMQ_QUIT) ? 1 : 0;
198
0
  }
199
200
0
  LeaveCriticalSection(&queue->lock);
201
202
0
  return status;
203
0
}
204
205
int MessageQueue_Peek(wMessageQueue* queue, wMessage* message, BOOL remove)
206
0
{
207
0
  int status = 0;
208
209
0
  WINPR_ASSERT(queue);
210
0
  EnterCriticalSection(&queue->lock);
211
212
0
  if (queue->size > 0)
213
0
  {
214
0
    CopyMemory(message, &(queue->array[queue->head]), sizeof(wMessage));
215
0
    status = 1;
216
217
0
    if (remove)
218
0
    {
219
0
      ZeroMemory(&(queue->array[queue->head]), sizeof(wMessage));
220
0
      queue->head = (queue->head + 1) % queue->capacity;
221
0
      queue->size--;
222
223
0
      if (queue->size < 1)
224
0
        ResetEvent(queue->event);
225
0
    }
226
0
  }
227
228
0
  LeaveCriticalSection(&queue->lock);
229
230
0
  return status;
231
0
}
232
233
/**
234
 * Construction, Destruction
235
 */
236
237
wMessageQueue* MessageQueue_New(const wObject* callback)
238
37.9k
{
239
37.9k
  wMessageQueue* queue = NULL;
240
241
37.9k
  queue = (wMessageQueue*)calloc(1, sizeof(wMessageQueue));
242
37.9k
  if (!queue)
243
0
    return NULL;
244
245
37.9k
  if (!InitializeCriticalSectionAndSpinCount(&queue->lock, 4000))
246
0
    goto fail;
247
248
37.9k
  if (!MessageQueue_EnsureCapacity(queue, 32))
249
0
    goto fail;
250
251
37.9k
  queue->event = CreateEvent(NULL, TRUE, FALSE, NULL);
252
37.9k
  if (!queue->event)
253
0
    goto fail;
254
255
37.9k
  if (callback)
256
29.9k
    queue->object = *callback;
257
258
37.9k
  return queue;
259
260
0
fail:
261
0
  WINPR_PRAGMA_DIAG_PUSH
262
0
  WINPR_PRAGMA_DIAG_IGNORED_MISMATCHED_DEALLOC
263
0
  MessageQueue_Free(queue);
264
0
  WINPR_PRAGMA_DIAG_POP
265
0
  return NULL;
266
37.9k
}
267
268
void MessageQueue_Free(wMessageQueue* queue)
269
37.9k
{
270
37.9k
  if (!queue)
271
0
    return;
272
273
37.9k
  if (queue->event)
274
37.9k
    MessageQueue_Clear(queue);
275
276
37.9k
  CloseHandle(queue->event);
277
37.9k
  DeleteCriticalSection(&queue->lock);
278
279
37.9k
  free(queue->array);
280
37.9k
  free(queue);
281
37.9k
}
282
283
int MessageQueue_Clear(wMessageQueue* queue)
284
37.9k
{
285
37.9k
  int status = 0;
286
287
37.9k
  WINPR_ASSERT(queue);
288
37.9k
  WINPR_ASSERT(queue->event);
289
290
37.9k
  EnterCriticalSection(&queue->lock);
291
292
37.9k
  while (queue->size > 0)
293
0
  {
294
0
    wMessage* msg = &(queue->array[queue->head]);
295
296
    /* Free resources of message. */
297
0
    if (queue->object.fnObjectUninit)
298
0
      queue->object.fnObjectUninit(msg);
299
0
    if (queue->object.fnObjectFree)
300
0
      queue->object.fnObjectFree(msg);
301
302
0
    ZeroMemory(msg, sizeof(wMessage));
303
304
0
    queue->head = (queue->head + 1) % queue->capacity;
305
0
    queue->size--;
306
0
  }
307
37.9k
  ResetEvent(queue->event);
308
37.9k
  queue->closed = FALSE;
309
310
37.9k
  LeaveCriticalSection(&queue->lock);
311
312
37.9k
  return status;
313
37.9k
}