/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 | } |