/** * WinPR: Windows Portable Runtime * Synchronization Functions * * Copyright 2012 Marc-Andre Moreau * * 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. */ #ifdef HAVE_CONFIG_H #include "config.h" #endif #include #include #include #include #ifndef _WIN32 #include "synch.h" #ifdef HAVE_UNISTD_H #include #endif #ifdef HAVE_EVENTFD_H #include #include #endif #include "../handle/handle.h" #include "../pipe/pipe.h" #include "../log.h" #define TAG WINPR_TAG("synch.event") CRITICAL_SECTION cs = { NULL, 0, 0, NULL, NULL, 0 }; static pthread_once_t event_initialized = PTHREAD_ONCE_INIT; static HANDLE_CLOSE_CB _EventHandleCloseCb; static BOOL EventCloseHandle(HANDLE handle); static BOOL EventIsHandled(HANDLE handle) { WINPR_TIMER* pEvent = (WINPR_TIMER*) handle; if (!pEvent || pEvent->Type != HANDLE_TYPE_EVENT) { SetLastError(ERROR_INVALID_HANDLE); return FALSE; } return TRUE; } static void EventInitialize(void) { _EventHandleCloseCb.IsHandled = EventIsHandled; _EventHandleCloseCb.CloseHandle = EventCloseHandle; RegisterHandleCloseCb(&_EventHandleCloseCb); } BOOL EventCloseHandle(HANDLE handle) { WINPR_EVENT* event = (WINPR_EVENT*) handle; if (!EventIsHandled(handle)) return FALSE; if (!event->bAttached) { if (event->pipe_fd[0] != -1) { close(event->pipe_fd[0]); event->pipe_fd[0] = -1; } if (event->pipe_fd[1] != -1) { close(event->pipe_fd[1]); event->pipe_fd[1] = -1; } } free(event); return TRUE; } HANDLE CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCWSTR lpName) { WINPR_EVENT* event; if (pthread_once(&event_initialized, EventInitialize)) return NULL; event = (WINPR_EVENT*) calloc(1, sizeof(WINPR_EVENT)); if (event) { event->bAttached = FALSE; event->bManualReset = bManualReset; if (!event->bManualReset) { WLog_ERR(TAG, "auto-reset events not yet implemented"); } event->pipe_fd[0] = -1; event->pipe_fd[1] = -1; #ifdef HAVE_EVENTFD_H event->pipe_fd[0] = eventfd(0, EFD_NONBLOCK); if (event->pipe_fd[0] < 0) { WLog_ERR(TAG, "failed to create event"); free(event); return NULL; } #else if (pipe(event->pipe_fd) < 0) { WLog_ERR(TAG, "failed to create event"); free(event); return NULL; } #endif WINPR_HANDLE_SET_TYPE(event, HANDLE_TYPE_EVENT); if (bInitialState) SetEvent(event); } if (!cs.LockSemaphore) InitializeCriticalSection(&cs); return (HANDLE)event; } HANDLE CreateEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, LPCSTR lpName) { return CreateEventW(lpEventAttributes, bManualReset, bInitialState, NULL); } HANDLE CreateEventExW(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCWSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess) { return NULL; } HANDLE CreateEventExA(LPSECURITY_ATTRIBUTES lpEventAttributes, LPCSTR lpName, DWORD dwFlags, DWORD dwDesiredAccess) { return NULL; } HANDLE OpenEventW(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCWSTR lpName) { return NULL; } HANDLE OpenEventA(DWORD dwDesiredAccess, BOOL bInheritHandle, LPCSTR lpName) { return NULL; } #ifdef HAVE_EVENTFD_H #if defined(__UCLIBC__) 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 BOOL SetEvent(HANDLE hEvent) { ULONG Type; PVOID Object; int length; BOOL status; WINPR_EVENT* event; status = FALSE; if (winpr_Handle_GetInfo(hEvent, &Type, &Object)) { event = (WINPR_EVENT*) Object; #ifdef HAVE_EVENTFD_H eventfd_t val = 1; do { length = eventfd_write(event->pipe_fd[0], val); } while ((length < 0) && (errno == EINTR)); status = (length == 0) ? TRUE : FALSE; #else if (WaitForSingleObject(hEvent, 0) != WAIT_OBJECT_0) { length = write(event->pipe_fd[1], "-", 1); if (length == 1) status = TRUE; } else { status = TRUE; } #endif } return status; } BOOL ResetEvent(HANDLE hEvent) { ULONG Type; PVOID Object; int length; BOOL status; WINPR_EVENT* event; status = FALSE; if (winpr_Handle_GetInfo(hEvent, &Type, &Object)) { event = (WINPR_EVENT*) Object; while (WaitForSingleObject(hEvent, 0) == WAIT_OBJECT_0) { #ifdef HAVE_EVENTFD_H eventfd_t value; do { length = eventfd_read(event->pipe_fd[0], &value); } while ((length < 0) && (errno == EINTR)); if ((length > 0) && (!status)) status = TRUE; #else length = read(event->pipe_fd[0], &length, 1); if ((length == 1) && (!status)) status = TRUE; #endif } } return status; } #endif HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, int FileDescriptor) { #ifndef _WIN32 WINPR_EVENT* event; HANDLE handle = NULL; event = (WINPR_EVENT*) malloc(sizeof(WINPR_EVENT)); if (event) { event->bAttached = TRUE; event->bManualReset = bManualReset; event->pipe_fd[0] = FileDescriptor; event->pipe_fd[1] = -1; WINPR_HANDLE_SET_TYPE(event, HANDLE_TYPE_EVENT); handle = (HANDLE) event; } return handle; #else return NULL; #endif } HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, int FileDescriptor) { return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState, FileDescriptor); } /** * 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); #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 ULONG Type; PVOID Object; WINPR_EVENT* event; if (!winpr_Handle_GetInfo(hEvent, &Type, &Object)) return -1; event = (WINPR_EVENT*) Object; if (Type == HANDLE_TYPE_NAMED_PIPE) { WINPR_NAMED_PIPE* named = (WINPR_NAMED_PIPE*)hEvent; if (named->ServerMode) { return named->serverfd; } else { return named->clientfd; } } return event->pipe_fd[0]; #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) { #ifndef _WIN32 ULONG Type; PVOID Object; WINPR_EVENT* event; if (!winpr_Handle_GetInfo(hEvent, &Type, &Object)) return -1; event = (WINPR_EVENT*) Object; event->pipe_fd[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; void* obj; fd = GetEventFileDescriptor(hEvent); obj = ((void*)(long) fd); return obj; #else return hEvent; #endif }