Coverage Report

Created: 2026-03-04 06:14

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