Added write event support to handle functions.
Allows the WinPR HANDLE functions WaitForSingleObject and WaitForMultipleObjects to signal in case of write events. This is used by CreateFileDescriptor and SetEventFileDescriptor, which got an API change accomodating for this new feature.
This commit is contained in:
parent
7dc96c412f
commit
6243a9374b
@ -41,8 +41,12 @@ extern "C" {
|
||||
|
||||
WINPR_API BOOL CloseHandle(HANDLE hObject);
|
||||
|
||||
WINPR_API BOOL DuplicateHandle(HANDLE hSourceProcessHandle, HANDLE hSourceHandle, HANDLE hTargetProcessHandle,
|
||||
LPHANDLE lpTargetHandle, DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwOptions);
|
||||
WINPR_API BOOL DuplicateHandle(HANDLE hSourceProcessHandle,
|
||||
HANDLE hSourceHandle,
|
||||
HANDLE hTargetProcessHandle,
|
||||
LPHANDLE lpTargetHandle,
|
||||
DWORD dwDesiredAccess,
|
||||
BOOL bInheritHandle, DWORD dwOptions);
|
||||
|
||||
WINPR_API BOOL GetHandleInformation(HANDLE hObject, LPDWORD lpdwFlags);
|
||||
WINPR_API BOOL SetHandleInformation(HANDLE hObject, DWORD dwMask, DWORD dwFlags);
|
||||
|
@ -30,6 +30,7 @@
|
||||
#include <winpr/wtypes.h>
|
||||
#include <winpr/error.h>
|
||||
#include <winpr/handle.h>
|
||||
#include <winpr/winsock.h>
|
||||
|
||||
#include <winpr/nt.h>
|
||||
|
||||
@ -338,9 +339,9 @@ WINPR_API BOOL WINAPI DeleteSynchronizationBarrier(LPSYNCHRONIZATION_BARRIER lpB
|
||||
WINPR_API VOID USleep(DWORD dwMicroseconds);
|
||||
|
||||
WINPR_API HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
BOOL bManualReset, BOOL bInitialState, int FileDescriptor);
|
||||
BOOL bManualReset, BOOL bInitialState, int FileDescriptor, ULONG mode);
|
||||
WINPR_API HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
BOOL bManualReset, BOOL bInitialState, int FileDescriptor);
|
||||
BOOL bManualReset, BOOL bInitialState, int FileDescriptor, ULONG mode);
|
||||
|
||||
WINPR_API HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
BOOL bManualReset, BOOL bInitialState, void* pObject);
|
||||
@ -352,7 +353,7 @@ WINPR_API HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
#endif
|
||||
|
||||
WINPR_API int GetEventFileDescriptor(HANDLE hEvent);
|
||||
WINPR_API int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor);
|
||||
WINPR_API int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode);
|
||||
|
||||
WINPR_API void* GetEventWaitObject(HANDLE hEvent);
|
||||
|
||||
|
@ -48,7 +48,7 @@ BOOL CloseHandle(HANDLE hObject)
|
||||
ULONG Type;
|
||||
WINPR_HANDLE *Object;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hObject, &Type, (PVOID*) &Object))
|
||||
if (!winpr_Handle_GetInfo(hObject, &Type, &Object))
|
||||
return FALSE;
|
||||
|
||||
if (!Object)
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <winpr/handle.h>
|
||||
#include <winpr/file.h>
|
||||
#include <winpr/synch.h>
|
||||
#include <winpr/winsock.h>
|
||||
|
||||
#define HANDLE_TYPE_NONE 0
|
||||
#define HANDLE_TYPE_PROCESS 1
|
||||
@ -41,6 +42,7 @@
|
||||
|
||||
#define WINPR_HANDLE_DEF() \
|
||||
ULONG Type; \
|
||||
ULONG Mode; \
|
||||
HANDLE_OPS *ops
|
||||
|
||||
typedef BOOL (*pcIsHandled)(HANDLE handle);
|
||||
@ -68,10 +70,16 @@ struct winpr_handle
|
||||
};
|
||||
typedef struct winpr_handle WINPR_HANDLE;
|
||||
|
||||
#define WINPR_HANDLE_SET_TYPE(_handle, _type) \
|
||||
_handle->Type = _type
|
||||
static INLINE void WINPR_HANDLE_SET_TYPE_AND_MODE(void* _handle,
|
||||
ULONG _type, ULONG _mode)
|
||||
{
|
||||
WINPR_HANDLE* hdl = (WINPR_HANDLE*)_handle;
|
||||
|
||||
static INLINE BOOL winpr_Handle_GetInfo(HANDLE handle, ULONG* pType, PVOID* pObject)
|
||||
hdl->Type = _type;
|
||||
hdl->Mode = _mode;
|
||||
}
|
||||
|
||||
static INLINE BOOL winpr_Handle_GetInfo(HANDLE handle, ULONG* pType, WINPR_HANDLE** pObject)
|
||||
{
|
||||
WINPR_HANDLE* wHandle;
|
||||
|
||||
@ -91,7 +99,7 @@ static INLINE int winpr_Handle_getFd(HANDLE handle)
|
||||
WINPR_HANDLE *hdl;
|
||||
ULONG type;
|
||||
|
||||
if (!winpr_Handle_GetInfo(handle, &type, (PVOID*)&hdl))
|
||||
if (!winpr_Handle_GetInfo(handle, &type, &hdl))
|
||||
return -1;
|
||||
|
||||
if (!hdl || !hdl->ops->GetFd)
|
||||
@ -105,7 +113,7 @@ static INLINE DWORD winpr_Handle_cleanup(HANDLE handle)
|
||||
WINPR_HANDLE *hdl;
|
||||
ULONG type;
|
||||
|
||||
if (!winpr_Handle_GetInfo(handle, &type, (PVOID*)&hdl))
|
||||
if (!winpr_Handle_GetInfo(handle, &type, &hdl))
|
||||
return WAIT_FAILED;
|
||||
|
||||
if (!hdl)
|
||||
|
@ -78,23 +78,22 @@ BOOL EventCloseHandle(HANDLE 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->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;
|
||||
}
|
||||
}
|
||||
|
||||
if (event->pipe_fd[1] != -1)
|
||||
{
|
||||
close(event->pipe_fd[1]);
|
||||
event->pipe_fd[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
free(event);
|
||||
return TRUE;
|
||||
free(event);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static HANDLE_OPS ops = {
|
||||
@ -114,7 +113,7 @@ HANDLE CreateEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset,
|
||||
event->bAttached = FALSE;
|
||||
event->bManualReset = bManualReset;
|
||||
event->ops = &ops;
|
||||
WINPR_HANDLE_SET_TYPE(event, HANDLE_TYPE_EVENT);
|
||||
WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, FD_READ);
|
||||
|
||||
if (!event->bManualReset)
|
||||
{
|
||||
@ -200,7 +199,7 @@ static int eventfd_write(int fd, eventfd_t value)
|
||||
BOOL SetEvent(HANDLE hEvent)
|
||||
{
|
||||
ULONG Type;
|
||||
PVOID Object;
|
||||
WINPR_HANDLE* Object;
|
||||
int length;
|
||||
BOOL status;
|
||||
WINPR_EVENT* event;
|
||||
@ -241,7 +240,7 @@ BOOL SetEvent(HANDLE hEvent)
|
||||
BOOL ResetEvent(HANDLE hEvent)
|
||||
{
|
||||
ULONG Type;
|
||||
PVOID Object;
|
||||
WINPR_HANDLE* Object;
|
||||
int length;
|
||||
BOOL status = TRUE;
|
||||
WINPR_EVENT* event;
|
||||
@ -274,7 +273,9 @@ BOOL ResetEvent(HANDLE hEvent)
|
||||
#endif
|
||||
|
||||
|
||||
HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, int FileDescriptor)
|
||||
HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
BOOL bManualReset, BOOL bInitialState,
|
||||
int FileDescriptor, ULONG mode)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
WINPR_EVENT* event;
|
||||
@ -288,7 +289,7 @@ HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL
|
||||
event->pipe_fd[0] = FileDescriptor;
|
||||
event->pipe_fd[1] = -1;
|
||||
event->ops = &ops;
|
||||
WINPR_HANDLE_SET_TYPE(event, HANDLE_TYPE_EVENT);
|
||||
WINPR_HANDLE_SET_TYPE_AND_MODE(event, HANDLE_TYPE_EVENT, mode);
|
||||
handle = (HANDLE) event;
|
||||
}
|
||||
|
||||
@ -298,9 +299,12 @@ HANDLE CreateFileDescriptorEventW(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL
|
||||
#endif
|
||||
}
|
||||
|
||||
HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes, BOOL bManualReset, BOOL bInitialState, int FileDescriptor)
|
||||
HANDLE CreateFileDescriptorEventA(LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
BOOL bManualReset, BOOL bInitialState,
|
||||
int FileDescriptor, ULONG mode)
|
||||
{
|
||||
return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState, FileDescriptor);
|
||||
return CreateFileDescriptorEventW(lpEventAttributes, bManualReset,
|
||||
bInitialState, FileDescriptor, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -310,7 +314,8 @@ HANDLE CreateWaitObjectEvent(LPSECURITY_ATTRIBUTES lpEventAttributes,
|
||||
BOOL bManualReset, BOOL bInitialState, void* pObject)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
return CreateFileDescriptorEventW(lpEventAttributes, bManualReset, bInitialState, (int)(ULONG_PTR) pObject);
|
||||
return CreateFileDescriptorEventW(lpEventAttributes, bManualReset,
|
||||
bInitialState, (int)(ULONG_PTR) pObject, FD_READ);
|
||||
#else
|
||||
HANDLE hEvent = NULL;
|
||||
DuplicateHandle(GetCurrentProcess(), pObject, GetCurrentProcess(), &hEvent, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
@ -327,7 +332,7 @@ int GetEventFileDescriptor(HANDLE hEvent)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
ULONG Type;
|
||||
PVOID Object;
|
||||
WINPR_HANDLE* Object;
|
||||
WINPR_EVENT* event;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hEvent, &Type, &Object))
|
||||
@ -360,17 +365,19 @@ int GetEventFileDescriptor(HANDLE hEvent)
|
||||
* This file descriptor is not usable on Windows
|
||||
*/
|
||||
|
||||
int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor)
|
||||
int SetEventFileDescriptor(HANDLE hEvent, int FileDescriptor, ULONG mode)
|
||||
{
|
||||
#ifndef _WIN32
|
||||
ULONG Type;
|
||||
PVOID Object;
|
||||
WINPR_HANDLE* Object;
|
||||
WINPR_EVENT* event;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hEvent, &Type, &Object))
|
||||
return -1;
|
||||
|
||||
event = (WINPR_EVENT*) Object;
|
||||
event->bAttached = TRUE;
|
||||
event->Mode = mode;
|
||||
event->pipe_fd[0] = FileDescriptor;
|
||||
return 0;
|
||||
#else
|
||||
|
@ -44,6 +44,7 @@
|
||||
#include "synch.h"
|
||||
#include "../thread/thread.h"
|
||||
#include <winpr/thread.h>
|
||||
#include <winpr/debug.h>
|
||||
|
||||
#include "../log.h"
|
||||
#define TAG WINPR_TAG("sync.wait")
|
||||
@ -132,6 +133,19 @@ static int pthread_mutex_timedlock(pthread_mutex_t *mutex, const struct timespec
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
static DWORD handle_mode_to_pollevent(ULONG mode)
|
||||
{
|
||||
DWORD event = 0;
|
||||
if (mode & FD_READ)
|
||||
event |= POLLIN;
|
||||
if (mode & FD_WRITE)
|
||||
event |= POLLOUT;
|
||||
|
||||
return event;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ts_add_ms(struct timespec *ts, DWORD dwMilliseconds)
|
||||
{
|
||||
ts->tv_sec += dwMilliseconds / 1000L;
|
||||
@ -140,13 +154,13 @@ static void ts_add_ms(struct timespec *ts, DWORD dwMilliseconds)
|
||||
ts->tv_nsec = ts->tv_nsec % 1000000000L;
|
||||
}
|
||||
|
||||
static int waitOnFd(int fd, DWORD dwMilliseconds)
|
||||
static int waitOnFd(int fd, ULONG mode, DWORD dwMilliseconds)
|
||||
{
|
||||
int status;
|
||||
#ifdef HAVE_POLL_H
|
||||
struct pollfd pollfds;
|
||||
pollfds.fd = fd;
|
||||
pollfds.events = POLLIN;
|
||||
pollfds.events = handle_mode_to_pollevent(mode);
|
||||
pollfds.revents = 0;
|
||||
|
||||
do
|
||||
@ -157,11 +171,21 @@ static int waitOnFd(int fd, DWORD dwMilliseconds)
|
||||
|
||||
#else
|
||||
struct timeval timeout;
|
||||
fd_set rfds;
|
||||
fd_set rfds, wfds;
|
||||
fd_set* prfds = NULL;
|
||||
fd_set* pwfds = NULL;
|
||||
fd_set* pefds = NULL;
|
||||
FD_ZERO(&rfds);
|
||||
FD_SET(fd, &rfds);
|
||||
FD_ZERO(&wfds);
|
||||
FD_SET(fd, &wfds);
|
||||
ZeroMemory(&timeout, sizeof(timeout));
|
||||
|
||||
if (mode & FD_READ)
|
||||
prfds = &rfds;
|
||||
if (mode & FD_WRITE)
|
||||
pwfds = &wfds;
|
||||
|
||||
if ((dwMilliseconds != INFINITE) && (dwMilliseconds != 0))
|
||||
{
|
||||
timeout.tv_sec = dwMilliseconds / 1000;
|
||||
@ -170,7 +194,7 @@ static int waitOnFd(int fd, DWORD dwMilliseconds)
|
||||
|
||||
do
|
||||
{
|
||||
status = select(fd + 1, &rfds, NULL, NULL, (dwMilliseconds == INFINITE) ? NULL : &timeout);
|
||||
status = select(fd + 1, prfds, pwfds, pefds, (dwMilliseconds == INFINITE) ? NULL : &timeout);
|
||||
}
|
||||
while (status < 0 && (errno == EINTR));
|
||||
|
||||
@ -181,7 +205,7 @@ static int waitOnFd(int fd, DWORD dwMilliseconds)
|
||||
DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
|
||||
{
|
||||
ULONG Type;
|
||||
PVOID Object;
|
||||
WINPR_HANDLE* Object;
|
||||
|
||||
if (!winpr_Handle_GetInfo(hHandle, &Type, &Object))
|
||||
{
|
||||
@ -193,8 +217,10 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
|
||||
{
|
||||
WINPR_PROCESS *process;
|
||||
process = (WINPR_PROCESS *) Object;
|
||||
int rc;
|
||||
|
||||
if (waitpid(process->pid, &(process->status), 0) != -1)
|
||||
rc = waitpid(process->pid, &(process->status), 0);
|
||||
if (rc != process->pid)
|
||||
{
|
||||
WLog_ERR(TAG, "waitpid failure [%d] %s", errno, strerror(errno));
|
||||
return WAIT_FAILED;
|
||||
@ -233,7 +259,7 @@ DWORD WaitForSingleObject(HANDLE hHandle, DWORD dwMilliseconds)
|
||||
if (fd < 0)
|
||||
return WAIT_FAILED;
|
||||
|
||||
status = waitOnFd(fd, dwMilliseconds);
|
||||
status = waitOnFd(fd, Object->Mode, dwMilliseconds);
|
||||
|
||||
if (status < 0)
|
||||
{
|
||||
@ -270,12 +296,14 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAl
|
||||
int index;
|
||||
int status;
|
||||
ULONG Type;
|
||||
PVOID Object;
|
||||
BOOL signal_handled = FALSE;
|
||||
WINPR_HANDLE* Object;
|
||||
#ifdef HAVE_POLL_H
|
||||
struct pollfd *pollfds;
|
||||
#else
|
||||
int maxfd;
|
||||
fd_set fds;
|
||||
fd_set rfds;
|
||||
fd_set wfds;
|
||||
struct timeval timeout;
|
||||
#endif
|
||||
|
||||
@ -300,14 +328,18 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAl
|
||||
|
||||
do
|
||||
{
|
||||
#ifndef HAVE_POLL_H
|
||||
fd_set* prfds = NULL;
|
||||
fd_set* pwfds = NULL;
|
||||
maxfd = 0;
|
||||
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
ZeroMemory(&timeout, sizeof(timeout));
|
||||
#endif
|
||||
if (bWaitAll && (dwMilliseconds != INFINITE))
|
||||
clock_gettime(CLOCK_MONOTONIC, &starttime);
|
||||
|
||||
#ifndef HAVE_POLL_H
|
||||
maxfd = 0;
|
||||
FD_ZERO(&fds);
|
||||
ZeroMemory(&timeout, sizeof(timeout));
|
||||
#endif
|
||||
polled = 0;
|
||||
|
||||
for (index = 0; index < nCount; index++)
|
||||
@ -336,10 +368,16 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAl
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
pollfds[polled].fd = fd;
|
||||
pollfds[polled].events = POLLIN;
|
||||
pollfds[polled].events = handle_mode_to_pollevent(Object->Mode);
|
||||
pollfds[polled].revents = 0;
|
||||
#else
|
||||
FD_SET(fd, &fds);
|
||||
FD_SET(fd, &rfds);
|
||||
FD_SET(fd, &wfds);
|
||||
|
||||
if (Object->Mode & FD_READ)
|
||||
prfds = &rfds;
|
||||
if (Object->Mode & FD_WRITE)
|
||||
pwfds = &wfds;
|
||||
|
||||
if (fd > maxfd)
|
||||
maxfd = fd;
|
||||
@ -366,8 +404,8 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAl
|
||||
|
||||
do
|
||||
{
|
||||
status = select(maxfd + 1, &fds, 0, 0,
|
||||
(dwMilliseconds == INFINITE) ? NULL : &timeout);
|
||||
status = select(maxfd + 1, prfds, pwfds, 0,
|
||||
(dwMilliseconds == INFINITE) ? NULL : &timeout);
|
||||
}
|
||||
while (status < 0 && errno == EINTR);
|
||||
|
||||
@ -376,12 +414,13 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAl
|
||||
if (status < 0)
|
||||
{
|
||||
#ifdef HAVE_POLL_H
|
||||
WLog_ERR(TAG, "poll() failure [%d] %s", errno,
|
||||
WLog_ERR(TAG, "poll() handle %d (%d) failure [%d] %s", index, nCount, errno,
|
||||
strerror(errno));
|
||||
#else
|
||||
WLog_ERR(TAG, "select() failure [%d] %s", errno,
|
||||
WLog_ERR(TAG, "select() handle %d (%d) failure [%d] %s", index, nCount, errno,
|
||||
strerror(errno));
|
||||
#endif
|
||||
winpr_log_backtrace(TAG, WLOG_ERROR, 20);
|
||||
return WAIT_FAILED;
|
||||
}
|
||||
|
||||
@ -399,9 +438,11 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAl
|
||||
dwMilliseconds -= (diff / 1000);
|
||||
}
|
||||
|
||||
signal_handled = FALSE;
|
||||
for (index = 0; index < polled; index++)
|
||||
{
|
||||
DWORD idx;
|
||||
BOOL signal_set = FALSE;
|
||||
|
||||
if (bWaitAll)
|
||||
idx = poll_map[index];
|
||||
@ -423,11 +464,14 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAl
|
||||
}
|
||||
|
||||
#ifdef HAVE_POLL_H
|
||||
|
||||
if (pollfds[index].revents & POLLIN)
|
||||
signal_set = pollfds[index].revents & pollfds[index].events;
|
||||
#else
|
||||
if (FD_ISSET(fd, &fds))
|
||||
if (Object->Mode & FD_READ)
|
||||
signal_set = FD_ISSET(fd, &rfds);
|
||||
if (Object->Mode & FD_WRITE)
|
||||
signal_set = FD_ISSET(fd, &wfds);
|
||||
#endif
|
||||
if (signal_set)
|
||||
{
|
||||
DWORD rc = winpr_Handle_cleanup(lpHandles[idx]);
|
||||
if (rc != WAIT_OBJECT_0)
|
||||
@ -450,10 +494,12 @@ DWORD WaitForMultipleObjects(DWORD nCount, const HANDLE *lpHandles, BOOL bWaitAl
|
||||
|
||||
if (bWaitAll && (signalled >= nCount))
|
||||
return (WAIT_OBJECT_0);
|
||||
|
||||
signal_handled = TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (bWaitAll);
|
||||
while (bWaitAll || !signal_handled);
|
||||
|
||||
WLog_ERR(TAG, "failed (unknown error)");
|
||||
return WAIT_FAILED;
|
||||
|
Loading…
x
Reference in New Issue
Block a user