Coverage Report

Created: 2024-09-08 06:18

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