Coverage Report

Created: 2026-08-31 06:25

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 = nullptr;
54
55
static void dump_event(WINPR_EVENT* event, size_t index)
56
{
57
  char** msg = nullptr;
58
  size_t used = 0;
59
60
  WLog_DBG(TAG, "Event handle created still not closed! [%" PRIuz ", %p]", index, (void*)event);
61
  msg = winpr_backtrace_symbols(event->create_stack, &used);
62
63
  for (size_t i = 2; i < used; i++)
64
    WLog_DBG(TAG, "[%" PRIuz "]: %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
WINPR_ATTR_NODISCARD
73
static int eventfd_read(int fd, eventfd_t* value)
74
{
75
  return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1;
76
}
77
78
WINPR_ATTR_NODISCARD
79
static int eventfd_write(int fd, eventfd_t value)
80
{
81
  return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1;
82
}
83
#endif
84
#endif
85
86
WINPR_ATTR_NODISCARD
87
static BOOL set_non_blocking_fd(int fd)
88
0
{
89
0
  int flags = fcntl(fd, F_GETFL);
90
0
  if (flags < 0)
91
0
    return FALSE;
92
93
0
  return fcntl(fd, F_SETFL, flags | O_NONBLOCK) >= 0;
94
0
}
95
96
BOOL winpr_event_init(WINPR_EVENT_IMPL* event)
97
0
{
98
0
#ifdef WINPR_HAVE_SYS_EVENTFD_H
99
0
  event->fds[1] = -1;
100
0
  event->fds[0] = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
101
102
0
  if ((event->fds[0] < 0) && (errno == EINVAL))
103
0
  {
104
    /* kernels older than 2.6.27 only support the flag-less eventfd() call; fall back to
105
     * that and apply non-blocking/close-on-exec separately afterward */
106
0
    event->fds[0] = eventfd(0, 0);
107
0
    if (event->fds[0] >= 0)
108
0
    {
109
0
      if (!set_non_blocking_fd(event->fds[0]) || !winpr_set_cloexec(event->fds[0], TRUE))
110
0
      {
111
0
        close(event->fds[0]);
112
0
        event->fds[0] = -1;
113
0
      }
114
0
    }
115
0
  }
116
117
0
  return event->fds[0] >= 0;
118
#else
119
#ifdef WINPR_HAVE_PIPE2
120
  if (pipe2(event->fds, O_CLOEXEC) < 0)
121
    return FALSE;
122
#else
123
  if (pipe(event->fds) < 0)
124
    return FALSE;
125
126
  if (!winpr_set_cloexec(event->fds[0], TRUE) || !winpr_set_cloexec(event->fds[1], TRUE))
127
    goto out_error;
128
#endif
129
130
  if (!set_non_blocking_fd(event->fds[0]) || !set_non_blocking_fd(event->fds[1]))
131
    goto out_error;
132
133
  return TRUE;
134
135
out_error:
136
  winpr_event_uninit(event);
137
  return FALSE;
138
#endif
139
0
}
140
141
void winpr_event_init_from_fd(WINPR_EVENT_IMPL* event, int fd)
142
0
{
143
0
  event->fds[0] = fd;
144
#ifndef WINPR_HAVE_SYS_EVENTFD_H
145
  event->fds[1] = fd;
146
#endif
147
0
}
148
149
BOOL winpr_event_set(WINPR_EVENT_IMPL* event)
150
0
{
151
0
  SSIZE_T ret = 0;
152
0
  do
153
0
  {
154
0
#ifdef WINPR_HAVE_SYS_EVENTFD_H
155
0
    eventfd_t value = 1;
156
0
    ret = eventfd_write(event->fds[0], value);
157
#else
158
    ret = write(event->fds[1], "-", 1);
159
#endif
160
0
  } while (ret < 0 && errno == EINTR);
161
162
0
  return ret >= 0;
163
0
}
164
165
BOOL winpr_event_reset(WINPR_EVENT_IMPL* event)
166
0
{
167
0
  SSIZE_T ret = 0;
168
0
  do
169
0
  {
170
0
    do
171
0
    {
172
0
#ifdef WINPR_HAVE_SYS_EVENTFD_H
173
0
      eventfd_t value = 1;
174
0
      ret = eventfd_read(event->fds[0], &value);
175
#else
176
      char value;
177
      ret = read(event->fds[0], &value, 1);
178
#endif
179
0
    } while (ret < 0 && errno == EINTR);
180
0
  } while (ret >= 0);
181
182
0
  return (errno == EAGAIN);
183
0
}
184
185
void winpr_event_uninit(WINPR_EVENT_IMPL* event)
186
0
{
187
0
  if (event->fds[0] >= 0)
188
0
  {
189
0
    close(event->fds[0]);
190
0
    event->fds[0] = -1;
191
0
  }
192
193
0
  if (event->fds[1] >= 0)
194
0
  {
195
0
    close(event->fds[1]);
196
0
    event->fds[1] = -1;
197
0
  }
198
0
}
199
200
static BOOL EventCloseHandle(HANDLE handle);
201
202
WINPR_ATTR_NODISCARD
203
static BOOL EventIsHandled(HANDLE handle)
204
0
{
205
0
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_EVENT, FALSE);
206
0
}
207
208
WINPR_ATTR_NODISCARD
209
static int EventGetFd(HANDLE handle)
210
0
{
211
0
  WINPR_EVENT* event = (WINPR_EVENT*)handle;
212
213
0
  if (!EventIsHandled(handle))
214
0
    return -1;
215
216
0
  return event->impl.fds[0];
217
0
}
218
219
static BOOL EventCloseHandle_(WINPR_EVENT* event)
220
0
{
221
0
  if (!event)
222
0
    return FALSE;
223
224
0
  if (event->bAttached)
225
0
  {
226
    // don't close attached file descriptor
227
0
    event->impl.fds[0] = -1; // mark as invalid
228
0
  }
229
230
0
  winpr_event_uninit(&event->impl);
231
232
#if defined(WITH_DEBUG_EVENTS)
233
  if (global_event_list)
234
  {
235
    ArrayList_Remove(global_event_list, event);
236
    if (ArrayList_Count(global_event_list) < 1)
237
    {
238
      ArrayList_Free(global_event_list);
239
      global_event_list = nullptr;
240
    }
241
  }
242
243
  winpr_backtrace_free(event->create_stack);
244
#endif
245
0
  free(event->name);
246
0
  event->name = nullptr;
247
0
  return TRUE;
248
0
}
249
250
static BOOL EventCloseHandle(HANDLE handle)
251
0
{
252
0
  WINPR_EVENT* event = (WINPR_EVENT*)handle;
253
254
0
  if (!EventIsHandled(handle))
255
0
    return FALSE;
256
257
0
  return EventCloseHandle_(event);
258
0
}
259
260
static HANDLE_OPS ops = { EventIsHandled, EventCloseHandle, EventGetFd, nullptr, /* CleanupHandle */
261
                        nullptr,        nullptr,          nullptr,    nullptr, nullptr, nullptr,
262
                        nullptr,        nullptr,          nullptr,    nullptr, nullptr, nullptr,
263
                        nullptr,        nullptr,          nullptr,    nullptr, nullptr };
264
265
HANDLE CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
266
                    LPCWSTR lpName)
267
0
{
268
0
  HANDLE handle = nullptr;
269
0
  char* name = nullptr;
270
271
0
  if (lpName)
272
0
  {
273
0
    name = ConvertWCharToUtf8Alloc(lpName, nullptr);
274
0
    if (!name)
275
0
      return nullptr;
276
0
  }
277
278
0
  handle = CreateEventA(lpEventAttributes, bManualReset, bInitialState, name);
279
0
  free(name);
280
0
  return handle;
281
0
}
282
283
HANDLE CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState,
284
                    LPCSTR lpName)
285
0
{
286
0
  WINPR_EVENT* event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT));
287
288
0
  if (lpEventAttributes)
289
0
    WLog_WARN(TAG, "[%s] does not support lpEventAttributes", lpName);
290
291
0
  if (!event)
292
0
    return nullptr;
293
294
0
  if (lpName)
295
0
    event->name = strdup(lpName);
296
297
0
  event->impl.fds[0] = -1;
298
0
  event->impl.fds[1] = -1;
299
0
  event->bAttached = FALSE;
300
0
  event->bManualReset = bManualReset;
301
0
  event->common.ops = &ops;
302
0
  WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, FD_READ);
303
304
0
  if (!event->bManualReset)
305
0
    WLog_ERR(TAG, "auto-reset events not yet implemented");
306
307
0
  if (!winpr_event_init(&event->impl))
308
0
    goto fail;
309
310
0
  if (bInitialState)
311
0
  {
312
0
    if (!SetEvent(event))
313
0
      goto fail;
314
0
  }
315
316
#if defined(WITH_DEBUG_EVENTS)
317
  event->create_stack = winpr_backtrace(20);
318
  if (!global_event_list)
319
    global_event_list = ArrayList_New(TRUE);
320
321
  if (global_event_list)
322
    ArrayList_Append(global_event_list, event);
323
#endif
324
0
  return (HANDLE)event;
325
0
fail:
326
0
  EventCloseHandle_(event);
327
0
  free(event);
328
0
  return nullptr;
329
0
}
330
331
HANDLE CreateEventExW(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, DWORD dwFlags,
332
                      DWORD dwDesiredAccess)
333
0
{
334
0
  BOOL initial = FALSE;
335
0
  BOOL manual = FALSE;
336
337
0
  if (dwFlags & CREATE_EVENT_INITIAL_SET)
338
0
    initial = TRUE;
339
340
0
  if (dwFlags & CREATE_EVENT_MANUAL_RESET)
341
0
    manual = TRUE;
342
343
0
  if (dwDesiredAccess != 0)
344
0
  {
345
0
    char name[MAX_PATH] = WINPR_C_ARRAY_INIT;
346
0
    ConvertWCharToUtf8(lpName, name, sizeof(name) - 1);
347
0
    WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, name, dwDesiredAccess);
348
0
  }
349
350
0
  return CreateEventW(lpEventAttributes, manual, initial, lpName);
351
0
}
352
353
HANDLE CreateEventExA(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags,
354
                      DWORD dwDesiredAccess)
355
0
{
356
0
  BOOL initial = FALSE;
357
0
  BOOL manual = FALSE;
358
359
0
  if (dwFlags & CREATE_EVENT_INITIAL_SET)
360
0
    initial = TRUE;
361
362
0
  if (dwFlags & CREATE_EVENT_MANUAL_RESET)
363
0
    manual = TRUE;
364
365
0
  if (dwDesiredAccess != 0)
366
0
    WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName,
367
0
              dwDesiredAccess);
368
369
0
  return CreateEventA(lpEventAttributes, manual, initial, lpName);
370
0
}
371
372
HANDLE OpenEventW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName)
373
0
{
374
  /* TODO: Implement */
375
0
  WINPR_UNUSED(dwDesiredAccess);
376
0
  WINPR_UNUSED(bInheritHandle);
377
0
  WINPR_UNUSED(lpName);
378
0
  WLog_ERR(TAG, "not implemented");
379
0
  return nullptr;
380
0
}
381
382
HANDLE OpenEventA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName)
383
0
{
384
  /* TODO: Implement */
385
0
  WINPR_UNUSED(dwDesiredAccess);
386
0
  WINPR_UNUSED(bInheritHandle);
387
0
  WINPR_UNUSED(lpName);
388
0
  WLog_ERR(TAG, "not implemented");
389
0
  return nullptr;
390
0
}
391
392
BOOL SetEvent(HANDLE hEvent)
393
0
{
394
0
  ULONG Type = 0;
395
0
  WINPR_HANDLE* Object = nullptr;
396
0
  WINPR_EVENT* event = nullptr;
397
398
0
  if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
399
0
  {
400
0
    WLog_ERR(TAG, "SetEvent: hEvent is not an event");
401
0
    SetLastError(ERROR_INVALID_PARAMETER);
402
0
    return FALSE;
403
0
  }
404
405
0
  event = (WINPR_EVENT*)Object;
406
0
  return winpr_event_set(&event->impl);
407
0
}
408
409
BOOL ResetEvent(HANDLE hEvent)
410
0
{
411
0
  ULONG Type = 0;
412
0
  WINPR_HANDLE* Object = nullptr;
413
0
  WINPR_EVENT* event = nullptr;
414
415
0
  if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
416
0
  {
417
0
    WLog_ERR(TAG, "ResetEvent: hEvent is not an event");
418
0
    SetLastError(ERROR_INVALID_PARAMETER);
419
0
    return FALSE;
420
0
  }
421
422
0
  event = (WINPR_EVENT*)Object;
423
0
  return winpr_event_reset(&event->impl);
424
0
}
425
426
#endif
427
428
HANDLE CreateFileDescriptorEventW(WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpEventAttributes,
429
                                  BOOL bManualReset, WINPR_ATTR_UNUSED BOOL bInitialState,
430
                                  int FileDescriptor, ULONG mode)
431
0
{
432
0
#ifndef _WIN32
433
0
  WINPR_EVENT* event = nullptr;
434
0
  HANDLE handle = nullptr;
435
0
  event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT));
436
437
0
  if (event)
438
0
  {
439
0
    event->impl.fds[0] = -1;
440
0
    event->impl.fds[1] = -1;
441
0
    event->bAttached = TRUE;
442
0
    event->bManualReset = bManualReset;
443
0
    winpr_event_init_from_fd(&event->impl, FileDescriptor);
444
0
    event->common.ops = &ops;
445
0
    WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, mode);
446
0
    handle = (HANDLE)event;
447
0
  }
448
449
0
  return handle;
450
#else
451
  return nullptr;
452
#endif
453
0
}
454
455
HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
456
                                  BOOL bInitialState, int FileDescriptor, ULONG mode)
457
0
{
458
0
  return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState,
459
0
                                    FileDescriptor, mode);
460
0
}
461
462
/**
463
 * Returns an event based on the handle returned by GetEventWaitObject()
464
 */
465
HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
466
                             BOOL bInitialState, void* pObject)
467
0
{
468
0
#ifndef _WIN32
469
0
  return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState,
470
0
                                    (int)(ULONG_PTR)pObject, WINPR_FD_READ);
471
#else
472
  HANDLE hEvent = nullptr;
473
  DuplicateHandle(GetCurrentProcess(), pObject, GetCurrentProcess(), &hEvent, 0, FALSE,
474
                  DUPLICATE_SAME_ACCESS);
475
  return hEvent;
476
#endif
477
0
}
478
479
/*
480
 * Returns inner file descriptor for usage with select()
481
 * This file descriptor is not usable on Windows
482
 */
483
484
int GetEventFileDescriptor(HANDLE hEvent)
485
0
{
486
0
#ifndef _WIN32
487
0
  return winpr_Handle_getFd(hEvent);
488
#else
489
  return -1;
490
#endif
491
0
}
492
493
/*
494
 * Set inner file descriptor for usage with select()
495
 * This file descriptor is not usable on Windows
496
 */
497
498
int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode)
499
0
{
500
0
#ifndef _WIN32
501
0
  ULONG Type = 0;
502
0
  WINPR_HANDLE* Object = nullptr;
503
0
  WINPR_EVENT* event = nullptr;
504
505
0
  if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
506
0
  {
507
0
    WLog_ERR(TAG, "SetEventFileDescriptor: hEvent is not an event");
508
0
    SetLastError(ERROR_INVALID_PARAMETER);
509
0
    return -1;
510
0
  }
511
512
0
  event = (WINPR_EVENT*)Object;
513
514
0
  if (!event->bAttached && event->impl.fds[0] >= 0 && event->impl.fds[0] != FileDescriptor)
515
0
    close(event->impl.fds[0]);
516
517
0
  event->bAttached = TRUE;
518
0
  event->common.Mode = mode;
519
0
  event->impl.fds[0] = FileDescriptor;
520
0
  return 0;
521
#else
522
  return -1;
523
#endif
524
0
}
525
526
/**
527
 * Returns platform-specific wait object as a void pointer
528
 *
529
 * On Windows, the returned object is the same as the hEvent
530
 * argument and is an event HANDLE usable in WaitForMultipleObjects
531
 *
532
 * On other platforms, the returned object can be cast to an int
533
 * to obtain a file descriptor usable in select()
534
 */
535
536
void* GetEventWaitObject(HANDLE hEvent)
537
0
{
538
0
#ifndef _WIN32
539
0
  int fd = 0;
540
0
  void* obj = nullptr;
541
0
  fd = GetEventFileDescriptor(hEvent);
542
0
  obj = ((void*)(long)fd);
543
0
  return obj;
544
#else
545
  return hEvent;
546
#endif
547
0
}
548
#if defined(WITH_DEBUG_EVENTS)
549
#include <unistd.h>
550
#include <fcntl.h>
551
#include <sys/time.h>
552
#include <sys/resource.h>
553
554
static BOOL dump_handle_list(void* data, size_t index, WINPR_ATTR_UNUSED va_list ap)
555
{
556
  WINPR_EVENT* event = data;
557
  dump_event(event, index);
558
  return TRUE;
559
}
560
561
void DumpEventHandles_(const char* fkt, const char* file, size_t line)
562
{
563
  struct rlimit r = WINPR_C_ARRAY_INIT;
564
  int rc = getrlimit(RLIMIT_NOFILE, &r);
565
  if (rc >= 0)
566
  {
567
    size_t count = 0;
568
    for (rlim_t x = 0; x < r.rlim_cur; x++)
569
    {
570
      const int fd = WINPR_ASSERTING_INT_CAST(int, x);
571
      int flags = fcntl(fd, F_GETFD);
572
      if (flags >= 0)
573
        count++;
574
    }
575
    WLog_INFO(TAG, "------- limits [%lu/%lu] open files %" PRIuz, (unsigned long)r.rlim_cur,
576
              (unsigned long)r.rlim_max, count);
577
  }
578
  WLog_DBG(TAG, "--------- Start dump [%s %s:%" PRIuz "]", fkt, file, line);
579
  if (global_event_list)
580
  {
581
    ArrayList_Lock(global_event_list);
582
    (void)ArrayList_ForEach(global_event_list, dump_handle_list);
583
    ArrayList_Unlock(global_event_list);
584
  }
585
  WLog_DBG(TAG, "--------- End dump   [%s %s:%" PRIuz "]", fkt, file, line);
586
}
587
#endif