Coverage Report

Created: 2025-07-11 06:43

/src/FreeRDP/winpr/libwinpr/synch/event.c
Line
Count
Source (jump to first uncovered line)
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
388k
{
98
388k
#ifdef WINPR_HAVE_SYS_EVENTFD_H
99
388k
  event->fds[1] = -1;
100
388k
  event->fds[0] = eventfd(0, EFD_NONBLOCK);
101
102
388k
  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
388k
}
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
372k
{
128
372k
  int ret = 0;
129
372k
  do
130
372k
  {
131
372k
#ifdef WINPR_HAVE_SYS_EVENTFD_H
132
372k
    eventfd_t value = 1;
133
372k
    ret = eventfd_write(event->fds[0], value);
134
#else
135
    ret = write(event->fds[1], "-", 1);
136
#endif
137
372k
  } while (ret < 0 && errno == EINTR);
138
139
372k
  return ret >= 0;
140
372k
}
141
142
BOOL winpr_event_reset(WINPR_EVENT_IMPL* event)
143
365k
{
144
365k
  int ret = 0;
145
365k
  do
146
365k
  {
147
365k
    do
148
365k
    {
149
365k
#ifdef WINPR_HAVE_SYS_EVENTFD_H
150
365k
      eventfd_t value = 1;
151
365k
      ret = eventfd_read(event->fds[0], &value);
152
#else
153
      char value;
154
      ret = read(event->fds[0], &value, 1);
155
#endif
156
365k
    } while (ret < 0 && errno == EINTR);
157
365k
  } while (ret >= 0);
158
159
365k
  return (errno == EAGAIN);
160
365k
}
161
162
void winpr_event_uninit(WINPR_EVENT_IMPL* event)
163
388k
{
164
388k
  if (event->fds[0] >= 0)
165
388k
  {
166
388k
    close(event->fds[0]);
167
388k
    event->fds[0] = -1;
168
388k
  }
169
170
388k
  if (event->fds[1] >= 0)
171
0
  {
172
0
    close(event->fds[1]);
173
0
    event->fds[1] = -1;
174
0
  }
175
388k
}
176
177
static BOOL EventCloseHandle(HANDLE handle);
178
179
static BOOL EventIsHandled(HANDLE handle)
180
742k
{
181
742k
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_EVENT, FALSE);
182
742k
}
183
184
static int EventGetFd(HANDLE handle)
185
709k
{
186
709k
  WINPR_EVENT* event = (WINPR_EVENT*)handle;
187
188
709k
  if (!EventIsHandled(handle))
189
0
    return -1;
190
191
709k
  return event->impl.fds[0];
192
709k
}
193
194
static BOOL EventCloseHandle_(WINPR_EVENT* event)
195
33.2k
{
196
33.2k
  if (!event)
197
0
    return FALSE;
198
199
33.2k
  if (event->bAttached)
200
0
  {
201
    // don't close attached file descriptor
202
0
    event->impl.fds[0] = -1; // mark as invalid
203
0
  }
204
205
33.2k
  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
33.2k
  free(event->name);
221
33.2k
  free(event);
222
33.2k
  return TRUE;
223
33.2k
}
224
225
static BOOL EventCloseHandle(HANDLE handle)
226
33.2k
{
227
33.2k
  WINPR_EVENT* event = (WINPR_EVENT*)handle;
228
229
33.2k
  if (!EventIsHandled(handle))
230
0
    return FALSE;
231
232
33.2k
  return EventCloseHandle_(event);
233
33.2k
}
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
33.2k
{
278
33.2k
  WINPR_EVENT* event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT));
279
280
33.2k
  if (lpEventAttributes)
281
0
    WLog_WARN(TAG, "[%s] does not support lpEventAttributes", lpName);
282
283
33.2k
  if (!event)
284
0
    return NULL;
285
286
33.2k
  if (lpName)
287
0
    event->name = strdup(lpName);
288
289
33.2k
  event->impl.fds[0] = -1;
290
33.2k
  event->impl.fds[1] = -1;
291
33.2k
  event->bAttached = FALSE;
292
33.2k
  event->bManualReset = bManualReset;
293
33.2k
  event->common.ops = &ops;
294
33.2k
  WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, FD_READ);
295
296
33.2k
  if (!event->bManualReset)
297
0
    WLog_ERR(TAG, "auto-reset events not yet implemented");
298
299
33.2k
  if (!winpr_event_init(&event->impl))
300
0
    goto fail;
301
302
33.2k
  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
33.2k
  return (HANDLE)event;
317
0
fail:
318
0
  EventCloseHandle_(event);
319
0
  return NULL;
320
33.2k
}
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
    WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName,
336
0
              dwDesiredAccess);
337
338
0
  return CreateEventW(lpEventAttributes, manual, initial, lpName);
339
0
}
340
341
HANDLE CreateEventExA(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags,
342
                      DWORD dwDesiredAccess)
343
0
{
344
0
  BOOL initial = FALSE;
345
0
  BOOL manual = FALSE;
346
347
0
  if (dwFlags & CREATE_EVENT_INITIAL_SET)
348
0
    initial = TRUE;
349
350
0
  if (dwFlags & CREATE_EVENT_MANUAL_RESET)
351
0
    manual = TRUE;
352
353
0
  if (dwDesiredAccess != 0)
354
0
    WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName,
355
0
              dwDesiredAccess);
356
357
0
  return CreateEventA(lpEventAttributes, manual, initial, lpName);
358
0
}
359
360
HANDLE OpenEventW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName)
361
0
{
362
  /* TODO: Implement */
363
0
  WINPR_UNUSED(dwDesiredAccess);
364
0
  WINPR_UNUSED(bInheritHandle);
365
0
  WINPR_UNUSED(lpName);
366
0
  WLog_ERR(TAG, "not implemented");
367
0
  return NULL;
368
0
}
369
370
HANDLE OpenEventA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName)
371
0
{
372
  /* TODO: Implement */
373
0
  WINPR_UNUSED(dwDesiredAccess);
374
0
  WINPR_UNUSED(bInheritHandle);
375
0
  WINPR_UNUSED(lpName);
376
0
  WLog_ERR(TAG, "not implemented");
377
0
  return NULL;
378
0
}
379
380
BOOL SetEvent(HANDLE hEvent)
381
22.1k
{
382
22.1k
  ULONG Type = 0;
383
22.1k
  WINPR_HANDLE* Object = NULL;
384
22.1k
  WINPR_EVENT* event = NULL;
385
386
22.1k
  if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
387
0
  {
388
0
    WLog_ERR(TAG, "SetEvent: hEvent is not an event");
389
0
    SetLastError(ERROR_INVALID_PARAMETER);
390
0
    return FALSE;
391
0
  }
392
393
22.1k
  event = (WINPR_EVENT*)Object;
394
22.1k
  return winpr_event_set(&event->impl);
395
22.1k
}
396
397
BOOL ResetEvent(HANDLE hEvent)
398
11.0k
{
399
11.0k
  ULONG Type = 0;
400
11.0k
  WINPR_HANDLE* Object = NULL;
401
11.0k
  WINPR_EVENT* event = NULL;
402
403
11.0k
  if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
404
0
  {
405
0
    WLog_ERR(TAG, "ResetEvent: hEvent is not an event");
406
0
    SetLastError(ERROR_INVALID_PARAMETER);
407
0
    return FALSE;
408
0
  }
409
410
11.0k
  event = (WINPR_EVENT*)Object;
411
11.0k
  return winpr_event_reset(&event->impl);
412
11.0k
}
413
414
#endif
415
416
HANDLE CreateFileDescriptorEventW(WINPR_ATTR_UNUSED LPSECURITY_ATTRIBUTES lpEventAttributes,
417
                                  BOOL bManualReset, WINPR_ATTR_UNUSED BOOL bInitialState,
418
                                  int FileDescriptor, ULONG mode)
419
0
{
420
0
#ifndef _WIN32
421
0
  WINPR_EVENT* event = NULL;
422
0
  HANDLE handle = NULL;
423
0
  event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT));
424
425
0
  if (event)
426
0
  {
427
0
    event->impl.fds[0] = -1;
428
0
    event->impl.fds[1] = -1;
429
0
    event->bAttached = TRUE;
430
0
    event->bManualReset = bManualReset;
431
0
    winpr_event_init_from_fd(&event->impl, FileDescriptor);
432
0
    event->common.ops = &ops;
433
0
    WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, mode);
434
0
    handle = (HANDLE)event;
435
0
  }
436
437
0
  return handle;
438
#else
439
  return NULL;
440
#endif
441
0
}
442
443
HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
444
                                  BOOL bInitialState, int FileDescriptor, ULONG mode)
445
0
{
446
0
  return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState,
447
0
                                    FileDescriptor, mode);
448
0
}
449
450
/**
451
 * Returns an event based on the handle returned by GetEventWaitObject()
452
 */
453
HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
454
                             BOOL bInitialState, void* pObject)
455
0
{
456
0
#ifndef _WIN32
457
0
  return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState,
458
0
                                    (int)(ULONG_PTR)pObject, WINPR_FD_READ);
459
#else
460
  HANDLE hEvent = NULL;
461
  DuplicateHandle(GetCurrentProcess(), pObject, GetCurrentProcess(), &hEvent, 0, FALSE,
462
                  DUPLICATE_SAME_ACCESS);
463
  return hEvent;
464
#endif
465
0
}
466
467
/*
468
 * Returns inner file descriptor for usage with select()
469
 * This file descriptor is not usable on Windows
470
 */
471
472
int GetEventFileDescriptor(HANDLE hEvent)
473
0
{
474
0
#ifndef _WIN32
475
0
  return winpr_Handle_getFd(hEvent);
476
#else
477
  return -1;
478
#endif
479
0
}
480
481
/*
482
 * Set inner file descriptor for usage with select()
483
 * This file descriptor is not usable on Windows
484
 */
485
486
int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode)
487
0
{
488
0
#ifndef _WIN32
489
0
  ULONG Type = 0;
490
0
  WINPR_HANDLE* Object = NULL;
491
0
  WINPR_EVENT* event = NULL;
492
493
0
  if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT)
494
0
  {
495
0
    WLog_ERR(TAG, "SetEventFileDescriptor: hEvent is not an event");
496
0
    SetLastError(ERROR_INVALID_PARAMETER);
497
0
    return -1;
498
0
  }
499
500
0
  event = (WINPR_EVENT*)Object;
501
502
0
  if (!event->bAttached && event->impl.fds[0] >= 0 && event->impl.fds[0] != FileDescriptor)
503
0
    close(event->impl.fds[0]);
504
505
0
  event->bAttached = TRUE;
506
0
  event->common.Mode = mode;
507
0
  event->impl.fds[0] = FileDescriptor;
508
0
  return 0;
509
#else
510
  return -1;
511
#endif
512
0
}
513
514
/**
515
 * Returns platform-specific wait object as a void pointer
516
 *
517
 * On Windows, the returned object is the same as the hEvent
518
 * argument and is an event HANDLE usable in WaitForMultipleObjects
519
 *
520
 * On other platforms, the returned object can be cast to an int
521
 * to obtain a file descriptor usable in select()
522
 */
523
524
void* GetEventWaitObject(HANDLE hEvent)
525
0
{
526
0
#ifndef _WIN32
527
0
  int fd = 0;
528
0
  void* obj = NULL;
529
0
  fd = GetEventFileDescriptor(hEvent);
530
0
  obj = ((void*)(long)fd);
531
0
  return obj;
532
#else
533
  return hEvent;
534
#endif
535
0
}
536
#if defined(WITH_DEBUG_EVENTS)
537
#include <unistd.h>
538
#include <fcntl.h>
539
#include <sys/time.h>
540
#include <sys/resource.h>
541
542
static BOOL dump_handle_list(void* data, size_t index, va_list ap)
543
{
544
  WINPR_EVENT* event = data;
545
  dump_event(event, index);
546
  return TRUE;
547
}
548
549
void DumpEventHandles_(const char* fkt, const char* file, size_t line)
550
{
551
  struct rlimit r = { 0 };
552
  int rc = getrlimit(RLIMIT_NOFILE, &r);
553
  if (rc >= 0)
554
  {
555
    size_t count = 0;
556
    for (rlim_t x = 0; x < r.rlim_cur; x++)
557
    {
558
      int flags = fcntl(x, F_GETFD);
559
      if (flags >= 0)
560
        count++;
561
    }
562
    WLog_INFO(TAG, "------- limits [%d/%d] open files %" PRIuz, r.rlim_cur, r.rlim_max, count);
563
  }
564
  WLog_DBG(TAG, "--------- Start dump [%s %s:%" PRIuz "]", fkt, file, line);
565
  if (global_event_list)
566
  {
567
    ArrayList_Lock(global_event_list);
568
    ArrayList_ForEach(global_event_list, dump_handle_list);
569
    ArrayList_Unlock(global_event_list);
570
  }
571
  WLog_DBG(TAG, "--------- End dump   [%s %s:%" PRIuz "]", fkt, file, line);
572
}
573
#endif