Coverage Report

Created: 2026-02-26 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/FreeRDP/winpr/libwinpr/synch/event.c
Line
Count
Source
1
/**
2
 * WinPR: Windows Portable Runtime
3
 * Synchronization Functions
4
 *
5
 * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2017 Armin Novak <armin.novak@thincast.com>
7
 * Copyright 2017 Thincast Technologies GmbH
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *     http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
#include <winpr/config.h>
23
24
#include <stdio.h>
25
#include <string.h>
26
#include <stdlib.h>
27
28
#include <winpr/synch.h>
29
30
#ifndef _WIN32
31
32
#include "synch.h"
33
34
#ifdef WINPR_HAVE_UNISTD_H
35
#include <unistd.h>
36
#endif
37
38
#ifdef WINPR_HAVE_SYS_EVENTFD_H
39
#include <sys/eventfd.h>
40
#endif
41
42
#include <fcntl.h>
43
#include <errno.h>
44
45
#include "../handle/handle.h"
46
#include "../pipe/pipe.h"
47
48
#include "../log.h"
49
#include "event.h"
50
#define TAG WINPR_TAG("synch.event")
51
52
#if defined(WITH_DEBUG_EVENTS)
53
static wArrayList* global_event_list = NULL;
54
55
static void dump_event(WINPR_EVENT* event, size_t index)
56
{
57
  char** msg = NULL;
58
  size_t used = 0;
59
60
  WLog_DBG(TAG, "Event handle created still not closed! [%" PRIuz ", %p]", index, event);
61
  msg = winpr_backtrace_symbols(event->create_stack, &used);
62
63
  for (size_t i = 2; i < used; i++)
64
    WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]);
65
66
  free(msg);
67
}
68
#endif /* WITH_DEBUG_EVENTS */
69
70
#ifdef WINPR_HAVE_SYS_EVENTFD_H
71
#if !defined(WITH_EVENTFD_READ_WRITE)
72
static int eventfd_read(int fd, eventfd_t* value)
73
{
74
  return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
75
}
76
77
static int eventfd_write(int fd, eventfd_t value)
78
{
79
  return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
80
}
81
#endif
82
#endif
83
84
#ifndef WINPR_HAVE_SYS_EVENTFD_H
85
static BOOL set_non_blocking_fd(int fd)
86
{
87
  int flags;
88
  flags = fcntl(fd, F_GETFL);
89
  if (flags < 0)
90
    return FALSE;
91
92
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK) >= 0;
93
}
94
#endif /* !WINPR_HAVE_SYS_EVENTFD_H */
95
96
BOOL winpr_event_init(WINPR_EVENT_IMPL* event)
97
158k
{
98
158k
#ifdef WINPR_HAVE_SYS_EVENTFD_H
99
158k
  event->fds[1] = -1;
100
158k
  event->fds[0] = eventfd(0, EFD_NONBLOCK);
101
102
158k
  return event->fds[0] >= 0;
103
#else
104
  if (pipe(event->fds) < 0)
105
    return FALSE;
106
107
  if (!set_non_blocking_fd(event->fds[0]) || !set_non_blocking_fd(event->fds[1]))
108
    goto out_error;
109
110
  return TRUE;
111
112
out_error:
113
  winpr_event_uninit(event);
114
  return FALSE;
115
#endif
116
158k
}
117
118
void winpr_event_init_from_fd(WINPR_EVENT_IMPL* event, int fd)
119
0
{
120
0
  event->fds[0] = fd;
121
#ifndef WINPR_HAVE_SYS_EVENTFD_H
122
  event->fds[1] = fd;
123
#endif
124
0
}
125
126
BOOL winpr_event_set(WINPR_EVENT_IMPL* event)
127
29.8k
{
128
29.8k
  int ret = 0;
129
29.8k
  do
130
29.8k
  {
131
29.8k
#ifdef WINPR_HAVE_SYS_EVENTFD_H
132
29.8k
    eventfd_t value = 1;
133
29.8k
    ret = eventfd_write(event->fds[0], value);
134
#else
135
    ret = write(event->fds[1], "-", 1);
136
#endif
137
29.8k
  } while (ret < 0 && errno == EINTR);
138
139
29.8k
  return ret >= 0;
140
29.8k
}
141
142
BOOL winpr_event_reset(WINPR_EVENT_IMPL* event)
143
64.8k
{
144
64.8k
  int ret = 0;
145
64.8k
  do
146
78.3k
  {
147
78.3k
    do
148
78.3k
    {
149
78.3k
#ifdef WINPR_HAVE_SYS_EVENTFD_H
150
78.3k
      eventfd_t value = 1;
151
78.3k
      ret = eventfd_read(event->fds[0], &value);
152
#else
153
      char value;
154
      ret = read(event->fds[0], &value, 1);
155
#endif
156
78.3k
    } while (ret < 0 && errno == EINTR);
157
78.3k
  } while (ret >= 0);
158
159
64.8k
  return (errno == EAGAIN);
160
64.8k
}
161
162
void winpr_event_uninit(WINPR_EVENT_IMPL* event)
163
158k
{
164
158k
  if (event->fds[0] >= 0)
165
152k
  {
166
152k
    close(event->fds[0]);
167
152k
    event->fds[0] = -1;
168
152k
  }
169
170
158k
  if (event->fds[1] >= 0)
171
0
  {
172
0
    close(event->fds[1]);
173
0
    event->fds[1] = -1;
174
0
  }
175
158k
}
176
177
static BOOL EventCloseHandle(HANDLE handle);
178
179
static BOOL EventIsHandled(HANDLE handle)
180
173k
{
181
173k
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_EVENT, FALSE);
182
173k
}
183
184
static int EventGetFd(HANDLE handle)
185
28.9k
{
186
28.9k
  WINPR_EVENT* event = (WINPR_EVENT*)handle;
187
188
28.9k
  if (!EventIsHandled(handle))
189
0
    return -1;
190
191
28.9k
  return event->impl.fds[0];
192
28.9k
}
193
194
static BOOL EventCloseHandle_(WINPR_EVENT* event)
195
144k
{
196
144k
  if (!event)
197
0
    return FALSE;
198
199
144k
  if (event->bAttached)
200
6.40k
  {
201
    // don't close attached file descriptor
202
6.40k
    event->impl.fds[0] = -1; // mark as invalid
203
6.40k
  }
204
205
144k
  winpr_event_uninit(&event->impl);
206
207
#if defined(WITH_DEBUG_EVENTS)
208
  if (global_event_list)
209
  {
210
    ArrayList_Remove(global_event_list, event);
211
    if (ArrayList_Count(global_event_list) < 1)
212
    {
213
      ArrayList_Free(global_event_list);
214
      global_event_list = NULL;
215
    }
216
  }
217
218
  winpr_backtrace_free(event->create_stack);
219
#endif
220
144k
  free(event->name);
221
144k
  free(event);
222
144k
  return TRUE;
223
144k
}
224
225
static BOOL EventCloseHandle(HANDLE handle)
226
144k
{
227
144k
  WINPR_EVENT* event = (WINPR_EVENT*)handle;
228
229
144k
  if (!EventIsHandled(handle))
230
0
    return FALSE;
231
232
144k
  return EventCloseHandle_(event);
233
144k
}
234
235
static HANDLE_OPS ops = { EventIsHandled,
236
                        EventCloseHandle,
237
                        EventGetFd,
238
                        NULL, /* CleanupHandle */
239
                        NULL,
240
                        NULL,
241
                        NULL,
242
                        NULL,
243
                        NULL,
244
                        NULL,
245
                        NULL,
246
                        NULL,
247
                        NULL,
248
                        NULL,
249
                        NULL,
250
                        NULL,
251
                        NULL,
252
                        NULL,
253
                        NULL,
254
                        NULL,
255
                        NULL };
256
257
HANDLE CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
258
                    LPCWSTR lpName)
259
0
{
260
0
  HANDLE handle = NULL;
261
0
  char* name = NULL;
262
263
0
  if (lpName)
264
0
  {
265
0
    name = ConvertWCharToUtf8Alloc(lpName, NULL);
266
0
    if (!name)
267
0
      return NULL;
268
0
  }
269
270
0
  handle = CreateEventA(lpEventAttributes, bManualReset, bInitialState, name);
271
0
  free(name);
272
0
  return handle;
273
0
}
274
275
HANDLE CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
276
                    LPCSTR lpName)
277
144k
{
278
144k
  WINPR_EVENT* event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT));
279
280
144k
  if (lpEventAttributes)
281
0
    WLog_WARN(TAG, "[%s] does not support lpEventAttributes", lpName);
282
283
144k
  if (!event)
284
0
    return NULL;
285
286
144k
  if (lpName)
287
0
    event->name = strdup(lpName);
288
289
144k
  event->impl.fds[0] = -1;
290
144k
  event->impl.fds[1] = -1;
291
144k
  event->bAttached = FALSE;
292
144k
  event->bManualReset = bManualReset;
293
144k
  event->common.ops = &ops;
294
144k
  WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, FD_READ);
295
296
144k
  if (!event->bManualReset)
297
0
    WLog_ERR(TAG, "auto-reset events not yet implemented");
298
299
144k
  if (!winpr_event_init(&event->impl))
300
0
    goto fail;
301
302
144k
  if (bInitialState)
303
0
  {
304
0
    if (!SetEvent(event))
305
0
      goto fail;
306
0
  }
307
308
#if defined(WITH_DEBUG_EVENTS)
309
  event->create_stack = winpr_backtrace(20);
310
  if (!global_event_list)
311
    global_event_list = ArrayList_New(TRUE);
312
313
  if (global_event_list)
314
    ArrayList_Append(global_event_list, event);
315
#endif
316
144k
  return (HANDLE)event;
317
0
fail:
318
0
  EventCloseHandle_(event);
319
0
  return NULL;
320
144k
}
321
322
HANDLE CreateEventExW(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, DWORD dwFlags,
323
                      DWORD dwDesiredAccess)
324
0
{
325
0
  BOOL initial = FALSE;
326
0
  BOOL manual = FALSE;
327
328
0
  if (dwFlags & CREATE_EVENT_INITIAL_SET)
329
0
    initial = TRUE;
330
331
0
  if (dwFlags & CREATE_EVENT_MANUAL_RESET)
332
0
    manual = TRUE;
333
334
0
  if (dwDesiredAccess != 0)
335
0
  {
336
0
    char name[MAX_PATH] = WINPR_C_ARRAY_INIT;
337
0
    ConvertWCharToUtf8(lpName, name, sizeof(name) - 1);
338
0
    WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, name, dwDesiredAccess);
339
0
  }
340
341
0
  return CreateEventW(lpEventAttributes, manual, initial, lpName);
342
0
}
343
344
HANDLE CreateEventExA(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags,
345
                      DWORD dwDesiredAccess)
346
0
{
347
0
  BOOL initial = FALSE;
348
0
  BOOL manual = FALSE;
349
350
0
  if (dwFlags & CREATE_EVENT_INITIAL_SET)
351
0
    initial = TRUE;
352
353
0
  if (dwFlags & CREATE_EVENT_MANUAL_RESET)
354
0
    manual = TRUE;
355
356
0
  if (dwDesiredAccess != 0)
357
0
    WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName,
358
0
              dwDesiredAccess);
359
360
0
  return CreateEventA(lpEventAttributes, manual, initial, lpName);
361
0
}
362
363
HANDLE OpenEventW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName)
364
0
{
365
  /* TODO: Implement */
366
0
  WINPR_UNUSED(dwDesiredAccess);
367
0
  WINPR_UNUSED(bInheritHandle);
368
0
  WINPR_UNUSED(lpName);
369
0
  WLog_ERR(TAG, "not implemented");
370
0
  return NULL;
371
0
}
372
373
HANDLE OpenEventA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName)
374
0
{
375
  /* TODO: Implement */
376
0
  WINPR_UNUSED(dwDesiredAccess);
377
0
  WINPR_UNUSED(bInheritHandle);
378
0
  WINPR_UNUSED(lpName);
379
0
  WLog_ERR(TAG, "not implemented");
380
0
  return NULL;
381
0
}
382
383
BOOL SetEvent(HANDLE hEvent)
384
15.3k
{
385
15.3k
  ULONG Type = 0;
386
15.3k
  WINPR_HANDLE* Object = NULL;
387
15.3k
  WINPR_EVENT* event = NULL;
388
389
15.3k
  if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
390
0
  {
391
0
    WLog_ERR(TAG, "SetEvent: hEvent is not an event");
392
0
    SetLastError(ERROR_INVALID_PARAMETER);
393
0
    return FALSE;
394
0
  }
395
396
15.3k
  event = (WINPR_EVENT*)Object;
397
15.3k
  return winpr_event_set(&event->impl);
398
15.3k
}
399
400
BOOL ResetEvent(HANDLE hEvent)
401
50.3k
{
402
50.3k
  ULONG Type = 0;
403
50.3k
  WINPR_HANDLE* Object = NULL;
404
50.3k
  WINPR_EVENT* event = NULL;
405
406
50.3k
  if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
407
0
  {
408
0
    WLog_ERR(TAG, "ResetEvent: hEvent is not an event");
409
0
    SetLastError(ERROR_INVALID_PARAMETER);
410
0
    return FALSE;
411
0
  }
412
413
50.3k
  event = (WINPR_EVENT*)Object;
414
50.3k
  return winpr_event_reset(&event->impl);
415
50.3k
}
416
417
#endif
418
419
HANDLE CreateFileDescriptorEventW(WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpEventAttributes,
420
                                  BOOL bManualReset, WINPR_ATTR_UNUSED BOOL bInitialState,
421
                                  int FileDescriptor, ULONG mode)
422
0
{
423
0
#ifndef _WIN32
424
0
  WINPR_EVENT* event = NULL;
425
0
  HANDLE handle = NULL;
426
0
  event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT));
427
428
0
  if (event)
429
0
  {
430
0
    event->impl.fds[0] = -1;
431
0
    event->impl.fds[1] = -1;
432
0
    event->bAttached = TRUE;
433
0
    event->bManualReset = bManualReset;
434
0
    winpr_event_init_from_fd(&event->impl, FileDescriptor);
435
0
    event->common.ops = &ops;
436
0
    WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, mode);
437
0
    handle = (HANDLE)event;
438
0
  }
439
440
0
  return handle;
441
#else
442
  return NULL;
443
#endif
444
0
}
445
446
HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
447
                                  BOOL bInitialState, int FileDescriptor, ULONG mode)
448
0
{
449
0
  return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState,
450
0
                                    FileDescriptor, mode);
451
0
}
452
453
/**
454
 * Returns an event based on the handle returned by GetEventWaitObject()
455
 */
456
HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
457
                             BOOL bInitialState, void* pObject)
458
0
{
459
0
#ifndef _WIN32
460
0
  return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState,
461
0
                                    (int)(ULONG_PTR)pObject, WINPR_FD_READ);
462
#else
463
  HANDLE hEvent = NULL;
464
  DuplicateHandle(GetCurrentProcess(), pObject, GetCurrentProcess(), &hEvent, 0, FALSE,
465
                  DUPLICATE_SAME_ACCESS);
466
  return hEvent;
467
#endif
468
0
}
469
470
/*
471
 * Returns inner file descriptor for usage with select()
472
 * This file descriptor is not usable on Windows
473
 */
474
475
int GetEventFileDescriptor(HANDLE hEvent)
476
0
{
477
0
#ifndef _WIN32
478
0
  return winpr_Handle_getFd(hEvent);
479
#else
480
  return -1;
481
#endif
482
0
}
483
484
/*
485
 * Set inner file descriptor for usage with select()
486
 * This file descriptor is not usable on Windows
487
 */
488
489
int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode)
490
6.40k
{
491
6.40k
#ifndef _WIN32
492
6.40k
  ULONG Type = 0;
493
6.40k
  WINPR_HANDLE* Object = NULL;
494
6.40k
  WINPR_EVENT* event = NULL;
495
496
6.40k
  if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
497
0
  {
498
0
    WLog_ERR(TAG, "SetEventFileDescriptor: hEvent is not an event");
499
0
    SetLastError(ERROR_INVALID_PARAMETER);
500
0
    return -1;
501
0
  }
502
503
6.40k
  event = (WINPR_EVENT*)Object;
504
505
6.40k
  if (!event->bAttached && event->impl.fds[0] >= 0 && event->impl.fds[0] != FileDescriptor)
506
6.40k
    close(event->impl.fds[0]);
507
508
6.40k
  event->bAttached = TRUE;
509
6.40k
  event->common.Mode = mode;
510
6.40k
  event->impl.fds[0] = FileDescriptor;
511
6.40k
  return 0;
512
#else
513
  return -1;
514
#endif
515
6.40k
}
516
517
/**
518
 * Returns platform-specific wait object as a void pointer
519
 *
520
 * On Windows, the returned object is the same as the hEvent
521
 * argument and is an event HANDLE usable in WaitForMultipleObjects
522
 *
523
 * On other platforms, the returned object can be cast to an int
524
 * to obtain a file descriptor usable in select()
525
 */
526
527
void* GetEventWaitObject(HANDLE hEvent)
528
0
{
529
0
#ifndef _WIN32
530
0
  int fd = 0;
531
0
  void* obj = NULL;
532
0
  fd = GetEventFileDescriptor(hEvent);
533
0
  obj = ((void*)(long)fd);
534
0
  return obj;
535
#else
536
  return hEvent;
537
#endif
538
0
}
539
#if defined(WITH_DEBUG_EVENTS)
540
#include <unistd.h>
541
#include <fcntl.h>
542
#include <sys/time.h>
543
#include <sys/resource.h>
544
545
static BOOL dump_handle_list(void* data, size_t index, va_list ap)
546
{
547
  WINPR_EVENT* event = data;
548
  dump_event(event, index);
549
  return TRUE;
550
}
551
552
void DumpEventHandles_(const char* fkt, const char* file, size_t line)
553
{
554
  struct rlimit r = WINPR_C_ARRAY_INIT;
555
  int rc = getrlimit(RLIMIT_NOFILE, &r);
556
  if (rc >= 0)
557
  {
558
    size_t count = 0;
559
    for (rlim_t x = 0; x < r.rlim_cur; x++)
560
    {
561
      int flags = fcntl(x, F_GETFD);
562
      if (flags >= 0)
563
        count++;
564
    }
565
    WLog_INFO(TAG, "------- limits [%d/%d] open files %" PRIuz, r.rlim_cur, r.rlim_max, count);
566
  }
567
  WLog_DBG(TAG, "--------- Start dump [%s %s:%" PRIuz "]", fkt, file, line);
568
  if (global_event_list)
569
  {
570
    ArrayList_Lock(global_event_list);
571
    ArrayList_ForEach(global_event_list, dump_handle_list);
572
    ArrayList_Unlock(global_event_list);
573
  }
574
  WLog_DBG(TAG, "--------- End dump   [%s %s:%" PRIuz "]", fkt, file, line);
575
}
576
#endif