diff options
Diffstat (limited to 'winpr/libwinpr/synch/event.c')
-rw-r--r-- | winpr/libwinpr/synch/event.c | 584 |
1 files changed, 584 insertions, 0 deletions
diff --git a/winpr/libwinpr/synch/event.c b/winpr/libwinpr/synch/event.c new file mode 100644 index 0000000..7204add --- /dev/null +++ b/winpr/libwinpr/synch/event.c @@ -0,0 +1,584 @@ +/** + * WinPR: Windows Portable Runtime + * Synchronization Functions + * + * Copyright 2012 Marc-Andre Moreau <marcandre.moreau@gmail.com> + * Copyright 2017 Armin Novak <armin.novak@thincast.com> + * Copyright 2017 Thincast Technologies GmbH + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <winpr/config.h> + +#include <stdio.h> +#include <string.h> +#include <stdlib.h> + +#include <winpr/synch.h> + +#ifndef _WIN32 + +#include "synch.h" + +#ifdef WINPR_HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifdef WINPR_HAVE_SYS_EVENTFD_H +#include <sys/eventfd.h> +#endif + +#include <fcntl.h> +#include <errno.h> + +#include "../handle/handle.h" +#include "../pipe/pipe.h" + +#include "../log.h" +#include "event.h" +#define TAG WINPR_TAG("synch.event") + +#if defined(WITH_DEBUG_EVENTS) +static wArrayList* global_event_list = NULL; + +static void dump_event(WINPR_EVENT* event, size_t index) +{ + char** msg = NULL; + size_t used = 0; +#if 0 + void* stack = winpr_backtrace(20); + WLog_DBG(TAG, "Called from:"); + msg = winpr_backtrace_symbols(stack, &used); + + for (size_t i = 0; i < used; i++) + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + + free(msg); + winpr_backtrace_free(stack); +#endif + WLog_DBG(TAG, "Event handle created still not closed! [%" PRIuz ", %p]", index, event); + msg = winpr_backtrace_symbols(event->create_stack, &used); + + for (size_t i = 2; i < used; i++) + WLog_DBG(TAG, "[%" PRIdz "]: %s", i, msg[i]); + + free(msg); +} +#endif /* WITH_DEBUG_EVENTS */ + +#ifdef WINPR_HAVE_SYS_EVENTFD_H +#if !defined(WITH_EVENTFD_READ_WRITE) +static int eventfd_read(int fd, eventfd_t* value) +{ + return (read(fd, value, sizeof(*value)) == sizeof(*value)) ? 0 : -1; +} + +static int eventfd_write(int fd, eventfd_t value) +{ + return (write(fd, &value, sizeof(value)) == sizeof(value)) ? 0 : -1; +} +#endif +#endif + +#ifndef WINPR_HAVE_SYS_EVENTFD_H +static BOOL set_non_blocking_fd(int fd) +{ + int flags; + flags = fcntl(fd, F_GETFL); + if (flags < 0) + return FALSE; + + return fcntl(fd, F_SETFL, flags | O_NONBLOCK) >= 0; +} +#endif /* !WINPR_HAVE_SYS_EVENTFD_H */ + +BOOL winpr_event_init(WINPR_EVENT_IMPL* event) +{ +#ifdef WINPR_HAVE_SYS_EVENTFD_H + event->fds[1] = -1; + event->fds[0] = eventfd(0, EFD_NONBLOCK); + + return event->fds[0] >= 0; +#else + int flags; + + if (pipe(event->fds) < 0) + return FALSE; + + if (!set_non_blocking_fd(event->fds[0]) || !set_non_blocking_fd(event->fds[1])) + goto out_error; + + return TRUE; + +out_error: + winpr_event_uninit(event); + return FALSE; +#endif +} + +void winpr_event_init_from_fd(WINPR_EVENT_IMPL* event, int fd) +{ + event->fds[0] = fd; +#ifndef WINPR_HAVE_SYS_EVENTFD_H + event->fds[1] = fd; +#endif +} + +BOOL winpr_event_set(WINPR_EVENT_IMPL* event) +{ + int ret = 0; + do + { +#ifdef WINPR_HAVE_SYS_EVENTFD_H + eventfd_t value = 1; + ret = eventfd_write(event->fds[0], value); +#else + ret = write(event->fds[1], "-", 1); +#endif + } while (ret < 0 && errno == EINTR); + + return ret >= 0; +} + +BOOL winpr_event_reset(WINPR_EVENT_IMPL* event) +{ + int ret = 0; + do + { + do + { +#ifdef WINPR_HAVE_SYS_EVENTFD_H + eventfd_t value = 1; + ret = eventfd_read(event->fds[0], &value); +#else + char value; + ret = read(event->fds[0], &value, 1); +#endif + } while (ret < 0 && errno == EINTR); + } while (ret >= 0); + + return (errno == EAGAIN); +} + +void winpr_event_uninit(WINPR_EVENT_IMPL* event) +{ + if (event->fds[0] != -1) + { + close(event->fds[0]); + event->fds[0] = -1; + } + + if (event->fds[1] != -1) + { + close(event->fds[1]); + event->fds[1] = -1; + } +} + +static BOOL EventCloseHandle(HANDLE handle); + +static BOOL EventIsHandled(HANDLE handle) +{ + return WINPR_HANDLE_IS_HANDLED(handle, HANDLE_TYPE_EVENT, FALSE); +} + +static int EventGetFd(HANDLE handle) +{ + WINPR_EVENT* event = (WINPR_EVENT*)handle; + + if (!EventIsHandled(handle)) + return -1; + + return event->impl.fds[0]; +} + +static BOOL EventCloseHandle_(WINPR_EVENT* event) +{ + if (!event) + return FALSE; + + if (event->bAttached) + { + // don't close attached file descriptor + event->impl.fds[0] = -1; // mark as invalid + } + + winpr_event_uninit(&event->impl); + +#if defined(WITH_DEBUG_EVENTS) + if (global_event_list) + { + ArrayList_Remove(global_event_list, event); + if (ArrayList_Count(global_event_list) < 1) + { + ArrayList_Free(global_event_list); + global_event_list = NULL; + } + } + + winpr_backtrace_free(event->create_stack); +#endif + free(event->name); + free(event); + return TRUE; +} + +static BOOL EventCloseHandle(HANDLE handle) +{ + WINPR_EVENT* event = (WINPR_EVENT*)handle; + + if (!EventIsHandled(handle)) + return FALSE; + + return EventCloseHandle_(event); +} + +static HANDLE_OPS ops = { EventIsHandled, + EventCloseHandle, + EventGetFd, + NULL, /* CleanupHandle */ + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL, + NULL }; + +HANDLE CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, + LPCWSTR lpName) +{ + HANDLE handle = NULL; + char* name = NULL; + + if (lpName) + { + name = ConvertWCharToUtf8Alloc(lpName, NULL); + if (!name) + return NULL; + } + + handle = CreateEventA(lpEventAttributes, bManualReset, bInitialState, name); + free(name); + return handle; +} + +HANDLE CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, + LPCSTR lpName) +{ + WINPR_EVENT* event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT)); + + if (lpEventAttributes) + WLog_WARN(TAG, "[%s] does not support lpEventAttributes", lpName); + + if (!event) + return NULL; + + if (lpName) + event->name = strdup(lpName); + + event->impl.fds[0] = -1; + event->impl.fds[1] = -1; + event->bAttached = FALSE; + event->bManualReset = bManualReset; + event->common.ops = &ops; + WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, FD_READ); + + if (!event->bManualReset) + WLog_ERR(TAG, "auto-reset events not yet implemented"); + + if (!winpr_event_init(&event->impl)) + goto fail; + + if (bInitialState) + { + if (!SetEvent(event)) + goto fail; + } + +#if defined(WITH_DEBUG_EVENTS) + event->create_stack = winpr_backtrace(20); + if (!global_event_list) + global_event_list = ArrayList_New(TRUE); + + if (global_event_list) + ArrayList_Append(global_event_list, event); +#endif + return (HANDLE)event; +fail: + EventCloseHandle_(event); + return NULL; +} + +HANDLE CreateEventExW(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, DWORD dwFlags, + DWORD dwDesiredAccess) +{ + BOOL initial = FALSE; + BOOL manual = FALSE; + + if (dwFlags & CREATE_EVENT_INITIAL_SET) + initial = TRUE; + + if (dwFlags & CREATE_EVENT_MANUAL_RESET) + manual = TRUE; + + if (dwDesiredAccess != 0) + WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName, + dwDesiredAccess); + + return CreateEventW(lpEventAttributes, manual, initial, lpName); +} + +HANDLE CreateEventExA(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags, + DWORD dwDesiredAccess) +{ + BOOL initial = FALSE; + BOOL manual = FALSE; + + if (dwFlags & CREATE_EVENT_INITIAL_SET) + initial = TRUE; + + if (dwFlags & CREATE_EVENT_MANUAL_RESET) + manual = TRUE; + + if (dwDesiredAccess != 0) + WLog_WARN(TAG, "[%s] does not support dwDesiredAccess 0x%08" PRIx32, lpName, + dwDesiredAccess); + + return CreateEventA(lpEventAttributes, manual, initial, lpName); +} + +HANDLE OpenEventW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName) +{ + /* TODO: Implement */ + WINPR_UNUSED(dwDesiredAccess); + WINPR_UNUSED(bInheritHandle); + WINPR_UNUSED(lpName); + WLog_ERR(TAG, "not implemented"); + return NULL; +} + +HANDLE OpenEventA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName) +{ + /* TODO: Implement */ + WINPR_UNUSED(dwDesiredAccess); + WINPR_UNUSED(bInheritHandle); + WINPR_UNUSED(lpName); + WLog_ERR(TAG, "not implemented"); + return NULL; +} + +BOOL SetEvent(HANDLE hEvent) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_EVENT* event = NULL; + + if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT) + { + WLog_ERR(TAG, "SetEvent: hEvent is not an event"); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + event = (WINPR_EVENT*)Object; + return winpr_event_set(&event->impl); +} + +BOOL ResetEvent(HANDLE hEvent) +{ + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_EVENT* event = NULL; + + if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT) + { + WLog_ERR(TAG, "ResetEvent: hEvent is not an event"); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + event = (WINPR_EVENT*)Object; + return winpr_event_reset(&event->impl); +} + +#endif + +HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, + BOOL bInitialState, int FileDescriptor, ULONG mode) +{ +#ifndef _WIN32 + WINPR_EVENT* event = NULL; + HANDLE handle = NULL; + event = (WINPR_EVENT*)calloc(1, sizeof(WINPR_EVENT)); + + if (event) + { + event->impl.fds[0] = -1; + event->impl.fds[1] = -1; + event->bAttached = TRUE; + event->bManualReset = bManualReset; + winpr_event_init_from_fd(&event->impl, FileDescriptor); + event->common.ops = &ops; + WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, mode); + handle = (HANDLE)event; + } + + return handle; +#else + return NULL; +#endif +} + +HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, + BOOL bInitialState, int FileDescriptor, ULONG mode) +{ + return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState, + FileDescriptor, mode); +} + +/** + * Returns an event based on the handle returned by GetEventWaitObject() + */ +HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, + BOOL bInitialState, void* pObject) +{ +#ifndef _WIN32 + return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState, + (int)(ULONG_PTR)pObject, WINPR_FD_READ); +#else + HANDLE hEvent = NULL; + DuplicateHandle(GetCurrentProcess(), pObject, GetCurrentProcess(), &hEvent, 0, FALSE, + DUPLICATE_SAME_ACCESS); + return hEvent; +#endif +} + +/* + * Returns inner file descriptor for usage with select() + * This file descriptor is not usable on Windows + */ + +int GetEventFileDescriptor(HANDLE hEvent) +{ +#ifndef _WIN32 + return winpr_Handle_getFd(hEvent); +#else + return -1; +#endif +} + +/* + * Set inner file descriptor for usage with select() + * This file descriptor is not usable on Windows + */ + +int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode) +{ +#ifndef _WIN32 + ULONG Type = 0; + WINPR_HANDLE* Object = NULL; + WINPR_EVENT* event = NULL; + + if (!winpr_Handle_GetInfo(hEvent, &Type, &Object) || Type != HANDLE_TYPE_EVENT) + { + WLog_ERR(TAG, "SetEventFileDescriptor: hEvent is not an event"); + SetLastError(ERROR_INVALID_PARAMETER); + return -1; + } + + event = (WINPR_EVENT*)Object; + + if (!event->bAttached && event->impl.fds[0] >= 0 && event->impl.fds[0] != FileDescriptor) + close(event->impl.fds[0]); + + event->bAttached = TRUE; + event->common.Mode = mode; + event->impl.fds[0] = FileDescriptor; + return 0; +#else + return -1; +#endif +} + +/** + * Returns platform-specific wait object as a void pointer + * + * On Windows, the returned object is the same as the hEvent + * argument and is an event HANDLE usable in WaitForMultipleObjects + * + * On other platforms, the returned object can be cast to an int + * to obtain a file descriptor usable in select() + */ + +void* GetEventWaitObject(HANDLE hEvent) +{ +#ifndef _WIN32 + int fd = 0; + void* obj = NULL; + fd = GetEventFileDescriptor(hEvent); + obj = ((void*)(long)fd); + return obj; +#else + return hEvent; +#endif +} +#if defined(WITH_DEBUG_EVENTS) +#include <unistd.h> +#include <fcntl.h> +#include <sys/time.h> +#include <sys/resource.h> + +static BOOL dump_handle_list(void* data, size_t index, va_list ap) +{ + WINPR_EVENT* event = data; + dump_event(event, index); + return TRUE; +} + +void DumpEventHandles_(const char* fkt, const char* file, size_t line) +{ + struct rlimit r = { 0 }; + int rc = getrlimit(RLIMIT_NOFILE, &r); + if (rc >= 0) + { + size_t count = 0; + for (rlim_t x = 0; x < r.rlim_cur; x++) + { + int flags = fcntl(x, F_GETFD); + if (flags >= 0) + count++; + } + WLog_INFO(TAG, "------- limits [%d/%d] open files %" PRIuz, r.rlim_cur, r.rlim_max, count); + } + WLog_DBG(TAG, "--------- Start dump [%s %s:%" PRIuz "]", fkt, file, line); + if (global_event_list) + { + ArrayList_Lock(global_event_list); + ArrayList_ForEach(global_event_list, dump_handle_list); + ArrayList_Unlock(global_event_list); + } + WLog_DBG(TAG, "--------- End dump [%s %s:%" PRIuz "]", fkt, file, line); +} +#endif |