Coverage Report

Created: 2024-05-20 06:11

/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
609k
{
108
609k
#ifdef WINPR_HAVE_SYS_EVENTFD_H
109
609k
  event->fds[1] = -1;
110
609k
  event->fds[0] = eventfd(0, EFD_NONBLOCK);
111
112
609k
  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
609k
}
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
385k
{
140
385k
  int ret = 0;
141
385k
  do
142
385k
  {
143
385k
#ifdef WINPR_HAVE_SYS_EVENTFD_H
144
385k
    eventfd_t value = 1;
145
385k
    ret = eventfd_write(event->fds[0], value);
146
#else
147
    ret = write(event->fds[1], "-", 1);
148
#endif
149
385k
  } while (ret < 0 && errno == EINTR);
150
151
385k
  return ret >= 0;
152
385k
}
153
154
BOOL winpr_event_reset(WINPR_EVENT_IMPL* event)
155
415k
{
156
415k
  int ret = 0;
157
415k
  do
158
415k
  {
159
415k
    do
160
415k
    {
161
415k
#ifdef WINPR_HAVE_SYS_EVENTFD_H
162
415k
      eventfd_t value = 1;
163
415k
      ret = eventfd_read(event->fds[0], &value);
164
#else
165
      char value;
166
      ret = read(event->fds[0], &value, 1);
167
#endif
168
415k
    } while (ret < 0 && errno == EINTR);
169
415k
  } while (ret >= 0);
170
171
415k
  return (errno == EAGAIN);
172
415k
}
173
174
void winpr_event_uninit(WINPR_EVENT_IMPL* event)
175
609k
{
176
609k
  if (event->fds[0] != -1)
177
602k
  {
178
602k
    close(event->fds[0]);
179
602k
    event->fds[0] = -1;
180
602k
  }
181
182
609k
  if (event->fds[1] != -1)
183
0
  {
184
0
    close(event->fds[1]);
185
0
    event->fds[1] = -1;
186
0
  }
187
609k
}
188
189
static BOOL EventCloseHandle(HANDLE handle);
190
191
static BOOL EventIsHandled(HANDLE handle)
192
975k
{
193
975k
  return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_EVENT, FALSE);
194
975k
}
195
196
static int EventGetFd(HANDLE handle)
197
731k
{
198
731k
  WINPR_EVENT* event = (WINPR_EVENT*)handle;
199
200
731k
  if (!EventIsHandled(handle))
201
0
    return -1;
202
203
731k
  return event->impl.fds[0];
204
731k
}
205
206
static BOOL EventCloseHandle_(WINPR_EVENT* event)
207
243k
{
208
243k
  if (!event)
209
0
    return FALSE;
210
211
243k
  if (event->bAttached)
212
6.97k
  {
213
    // don't close attached file descriptor
214
6.97k
    event->impl.fds[0] = -1; // mark as invalid
215
6.97k
  }
216
217
243k
  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
243k
  free(event->name);
233
243k
  free(event);
234
243k
  return TRUE;
235
243k
}
236
237
static BOOL EventCloseHandle(HANDLE handle)
238
243k
{
239
243k
  WINPR_EVENT* event = (WINPR_EVENT*)handle;
240
241
243k
  if (!EventIsHandled(handle))
242
0
    return FALSE;
243
244
243k
  return EventCloseHandle_(event);
245
243k
}
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
243k
{
290
243k
  WINPR_EVENT* event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT));
291
292
243k
  if (lpEventAttributes)
293
243k
    WLog_WARN(TAG, "[%s] does not support lpEventAttributes", lpName);
294
295
243k
  if (!event)
296
0
    return NULL;
297
298
243k
  if (lpName)
299
0
    event->name = strdup(lpName);
300
301
243k
  event->impl.fds[0] = -1;
302
243k
  event->impl.fds[1] = -1;
303
243k
  event->bAttached = FALSE;
304
243k
  event->bManualReset = bManualReset;
305
243k
  event->common.ops = &ops;
306
243k
  WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, FD_READ);
307
308
243k
  if (!event->bManualReset)
309
243k
    WLog_ERR(TAG, "auto-reset events not yet implemented");
310
311
243k
  if (!winpr_event_init(&event->impl))
312
0
    goto fail;
313
314
243k
  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
243k
  return (HANDLE)event;
329
0
fail:
330
0
  EventCloseHandle_(event);
331
0
  return NULL;
332
243k
}
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
24.3k
{
394
24.3k
  ULONG Type = 0;
395
24.3k
  WINPR_HANDLE* Object = NULL;
396
24.3k
  WINPR_EVENT* event = NULL;
397
398
24.3k
  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
24.3k
  event = (WINPR_EVENT*)Object;
406
24.3k
  return winpr_event_set(&event->impl);
407
24.3k
}
408
409
BOOL ResetEvent(HANDLE hEvent)
410
49.3k
{
411
49.3k
  ULONG Type = 0;
412
49.3k
  WINPR_HANDLE* Object = NULL;
413
49.3k
  WINPR_EVENT* event = NULL;
414
415
49.3k
  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
49.3k
  event = (WINPR_EVENT*)Object;
423
49.3k
  return winpr_event_reset(&event->impl);
424
49.3k
}
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
6.97k
{
499
6.97k
#ifndef _WIN32
500
6.97k
  ULONG Type = 0;
501
6.97k
  WINPR_HANDLE* Object = NULL;
502
6.97k
  WINPR_EVENT* event = NULL;
503
504
6.97k
  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
6.97k
  event = (WINPR_EVENT*)Object;
512
513
6.97k
  if (!event->bAttached && event->impl.fds[0] >= 0 && event->impl.fds[0] != FileDescriptor)
514
6.97k
    close(event->impl.fds[0]);
515
516
6.97k
  event->bAttached = TRUE;
517
6.97k
  event->common.Mode = mode;
518
6.97k
  event->impl.fds[0] = FileDescriptor;
519
6.97k
  return 0;
520
#else
521
  return -1;
522
#endif
523
6.97k
}
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