| /* |
| * BRLTTY - A background process providing access to the console screen (when in |
| * text mode) for a blind person using a refreshable braille display. |
| * |
| * Copyright (C) 1995-2023 by The BRLTTY Developers. |
| * |
| * BRLTTY comes with ABSOLUTELY NO WARRANTY. |
| * |
| * This is free software, placed under the terms of the |
| * GNU Lesser General Public License, as published by the Free Software |
| * Foundation; either version 2.1 of the License, or (at your option) any |
| * later version. Please see the file LICENSE-LGPL for details. |
| * |
| * Web Page: http://brltty.app/ |
| * |
| * This software is maintained by Dave Mielke <dave@mielke.cc>. |
| */ |
| |
| #include "prologue.h" |
| |
| #include <string.h> |
| #include <errno.h> |
| |
| #include "get_select.h" |
| |
| #ifdef __MINGW32__ |
| #if _WIN32_WINNT < _WIN32_WINNT_VISTA |
| #define CancelIoEx(handle, ol) CancelIo((handle)) |
| #endif /* _WIN32_WINNT < _WIN32_WINNT_VISTA */ |
| #endif /* __MINGW32__ */ |
| |
| #ifdef __MSDOS__ |
| #include "system_msdos.h" |
| #endif /* __MSDOS__ */ |
| |
| #undef ASYNC_CAN_MONITOR_IO |
| #if defined(__MINGW32__) |
| #define ASYNC_CAN_MONITOR_IO |
| |
| typedef HANDLE MonitorEntry; |
| |
| #elif defined(HAVE_SYS_POLL_H) |
| #define ASYNC_CAN_MONITOR_IO |
| |
| #include <poll.h> |
| typedef struct pollfd MonitorEntry; |
| |
| #elif defined(GOT_SELECT) |
| #define ASYNC_CAN_MONITOR_IO |
| |
| typedef struct { |
| int size; |
| fd_set set; |
| } SelectDescriptor; |
| |
| static SelectDescriptor selectDescriptor_read; |
| static SelectDescriptor selectDescriptor_write; |
| static SelectDescriptor selectDescriptor_exception; |
| |
| typedef struct { |
| fd_set *selectSet; |
| FileDescriptor fileDescriptor; |
| } MonitorEntry; |
| |
| #endif /* monitor definitions */ |
| |
| #include "log.h" |
| #include "async_io.h" |
| #include "async_internal.h" |
| #include "timing.h" |
| |
| typedef struct FunctionEntryStruct FunctionEntry; |
| |
| typedef struct { |
| AsyncMonitorCallback *callback; |
| } MonitorExtension; |
| |
| typedef union { |
| struct { |
| AsyncInputCallback *callback; |
| unsigned end:1; |
| } input; |
| |
| struct { |
| AsyncOutputCallback *callback; |
| } output; |
| } TransferDirectionUnion; |
| |
| typedef struct { |
| TransferDirectionUnion direction; |
| size_t size; |
| size_t length; |
| unsigned char buffer[]; |
| } TransferExtension; |
| |
| typedef struct { |
| FunctionEntry *function; |
| void *extension; |
| void *data; |
| |
| MonitorEntry *monitor; |
| int error; |
| |
| unsigned active:1; |
| unsigned cancel:1; |
| unsigned finished:1; |
| } OperationEntry; |
| |
| typedef struct { |
| const char *functionName; |
| |
| void (*beginFunction) (FunctionEntry *function); |
| void (*endFunction) (FunctionEntry *function); |
| |
| void (*startOperation) (OperationEntry *operation); |
| void (*finishOperation) (OperationEntry *operation); |
| void (*cancelOperation) (OperationEntry *operation); |
| |
| int (*invokeCallback) (OperationEntry *operation); |
| } FunctionMethods; |
| |
| struct FunctionEntryStruct { |
| FileDescriptor fileDescriptor; |
| const FunctionMethods *methods; |
| Queue *operations; |
| |
| #if defined(__MINGW32__) |
| struct { |
| OVERLAPPED overlapped; |
| } windows; |
| |
| #elif defined(HAVE_SYS_POLL_H) |
| struct { |
| short int events; |
| } poll; |
| |
| #elif defined(HAVE_SELECT) |
| struct { |
| SelectDescriptor *descriptor; |
| } select; |
| |
| #endif /* monitor paradigms */ |
| }; |
| |
| typedef struct { |
| FileDescriptor fileDescriptor; |
| const FunctionMethods *methods; |
| } FunctionKey; |
| |
| typedef struct { |
| MonitorEntry *const array; |
| unsigned int count; |
| } MonitorGroup; |
| |
| struct AsyncIoDataStruct { |
| Queue *functionQueue; |
| }; |
| |
| void |
| asyncDeallocateIoData (AsyncIoData *iod) { |
| if (iod) { |
| if (iod->functionQueue) deallocateQueue(iod->functionQueue); |
| free(iod); |
| } |
| } |
| |
| static AsyncIoData * |
| getIoData (void) { |
| AsyncThreadSpecificData *tsd = asyncGetThreadSpecificData(); |
| if (!tsd) return NULL; |
| |
| if (!tsd->ioData) { |
| AsyncIoData *iod; |
| |
| if (!(iod = malloc(sizeof(*iod)))) { |
| logMallocError(); |
| return NULL; |
| } |
| |
| memset(iod, 0, sizeof(*iod)); |
| iod->functionQueue = NULL; |
| tsd->ioData = iod; |
| } |
| |
| return tsd->ioData; |
| } |
| |
| static void |
| logOperation (const OperationEntry *operation, void *callback) { |
| logSymbol(LOG_CATEGORY(ASYNC_EVENTS), |
| callback, |
| "%s", operation->function->methods->functionName); |
| } |
| |
| #ifdef __MINGW32__ |
| static void |
| prepareMonitors (void) { |
| } |
| |
| static int |
| awaitMonitors (const MonitorGroup *monitors, int timeout) { |
| if (monitors->count) { |
| DWORD result = WaitForMultipleObjects(monitors->count, monitors->array, FALSE, timeout); |
| if ((result >= WAIT_OBJECT_0) && (result < (WAIT_OBJECT_0 + monitors->count))) return 1; |
| |
| if (result == WAIT_FAILED) { |
| logWindowsSystemError("WaitForMultipleObjects"); |
| } |
| } else { |
| approximateDelay(timeout); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| initializeMonitor (MonitorEntry *monitor, const FunctionEntry *function, const OperationEntry *operation) { |
| *monitor = function->windows.overlapped.hEvent; |
| if (*monitor == INVALID_HANDLE_VALUE) *monitor = function->fileDescriptor; |
| } |
| |
| static int |
| testMonitor (const MonitorEntry *monitor, int *error) { |
| DWORD result = WaitForSingleObject(*monitor, 0); |
| if (result == WAIT_OBJECT_0) return 1; |
| |
| if (result == WAIT_FAILED) { |
| logWindowsSystemError("WaitForSingleObject"); |
| } |
| |
| return 0; |
| } |
| |
| static int |
| allocateWindowsEvent (HANDLE *event) { |
| if (*event == INVALID_HANDLE_VALUE) { |
| HANDLE handle = CreateEvent(NULL, TRUE, FALSE, NULL); |
| if (!handle) return 0; |
| *event = handle; |
| } |
| |
| return ResetEvent(*event); |
| } |
| |
| static void |
| deallocateWindowsEvent (HANDLE *event) { |
| if (*event != INVALID_HANDLE_VALUE) { |
| CloseHandle(*event); |
| *event = INVALID_HANDLE_VALUE; |
| } |
| } |
| |
| static int |
| allocateWindowsResources (OperationEntry *operation) { |
| FunctionEntry *function = operation->function; |
| |
| if (allocateWindowsEvent(&function->windows.overlapped.hEvent)) { |
| return 1; |
| } |
| |
| operation->finished = 1; |
| operation->error = GetLastError(); |
| return 0; |
| } |
| |
| static void |
| setWindowsTransferResult (OperationEntry *operation, DWORD success, DWORD count) { |
| TransferExtension *extension = operation->extension; |
| |
| if (success) { |
| extension->length += count; |
| } else { |
| DWORD error = GetLastError(); |
| |
| if ((error == ERROR_HANDLE_EOF) || (error == ERROR_BROKEN_PIPE)) { |
| extension->direction.input.end = 1; |
| } else { |
| setErrno(error); |
| operation->error = errno; |
| |
| if (error == ERROR_IO_PENDING) return; |
| if (error == ERROR_IO_INCOMPLETE) return; |
| } |
| } |
| |
| operation->finished = 1; |
| } |
| |
| static void |
| beginWindowsFunction (FunctionEntry *function) { |
| ZeroMemory(&function->windows.overlapped, sizeof(function->windows.overlapped)); |
| function->windows.overlapped.hEvent = INVALID_HANDLE_VALUE; |
| } |
| |
| static void |
| endWindowsFunction (FunctionEntry *function) { |
| deallocateWindowsEvent(&function->windows.overlapped.hEvent); |
| } |
| |
| static void |
| startWindowsRead (OperationEntry *operation) { |
| FunctionEntry *function = operation->function; |
| TransferExtension *extension = operation->extension; |
| |
| if (allocateWindowsResources(operation)) { |
| DWORD count; |
| BOOL success = ReadFile(function->fileDescriptor, |
| &extension->buffer[extension->length], |
| extension->size - extension->length, |
| &count, &function->windows.overlapped); |
| |
| setWindowsTransferResult(operation, success, count); |
| } |
| } |
| |
| static void |
| startWindowsWrite (OperationEntry *operation) { |
| FunctionEntry *function = operation->function; |
| TransferExtension *extension = operation->extension; |
| |
| if (allocateWindowsResources(operation)) { |
| DWORD count; |
| BOOL success = WriteFile(function->fileDescriptor, |
| &extension->buffer[extension->length], |
| extension->size - extension->length, |
| &count, &function->windows.overlapped); |
| |
| setWindowsTransferResult(operation, success, count); |
| } |
| } |
| |
| static void |
| finishWindowsTransferOperation (OperationEntry *operation) { |
| FunctionEntry *function = operation->function; |
| DWORD count; |
| BOOL success = GetOverlappedResult(function->fileDescriptor, &function->windows.overlapped, &count, FALSE); |
| |
| setWindowsTransferResult(operation, success, count); |
| } |
| |
| static void |
| cancelWindowsTransferOperation (OperationEntry *operation) { |
| FunctionEntry *function = operation->function; |
| DWORD count; |
| |
| if (CancelIoEx(function->fileDescriptor, &function->windows.overlapped)) { |
| GetOverlappedResult(function->fileDescriptor, &function->windows.overlapped, &count, TRUE); |
| } |
| } |
| |
| #else /* __MINGW32__ */ |
| |
| #ifdef HAVE_SYS_POLL_H |
| static void |
| prepareMonitors (void) { |
| } |
| |
| static int |
| awaitMonitors (const MonitorGroup *monitors, int timeout) { |
| int result = poll(monitors->array, monitors->count, timeout); |
| if (result > 0) return 1; |
| |
| if (result == -1) { |
| if (errno != EINTR) logSystemError("poll"); |
| } |
| |
| return 0; |
| } |
| |
| static void |
| initializeMonitor (MonitorEntry *monitor, const FunctionEntry *function, const OperationEntry *operation) { |
| monitor->fd = function->fileDescriptor; |
| monitor->events = function->poll.events; |
| monitor->revents = 0; |
| } |
| |
| static int |
| testMonitor (const MonitorEntry *monitor, int *error) { |
| if (!monitor->revents) return 0; |
| |
| if (!(monitor->revents & monitor->events)) { |
| if (monitor->revents & POLLHUP) { |
| *error = ENODEV; |
| } else { |
| *error = EIO; |
| } |
| } |
| |
| return 1; |
| } |
| |
| static void |
| beginUnixInputFunction (FunctionEntry *function) { |
| function->poll.events = POLLIN; |
| } |
| |
| static void |
| beginUnixOutputFunction (FunctionEntry *function) { |
| function->poll.events = POLLOUT; |
| } |
| |
| static void |
| beginUnixAlertFunction (FunctionEntry *function) { |
| function->poll.events = POLLPRI; |
| } |
| |
| #elif defined(HAVE_SELECT) |
| |
| static void |
| prepareSelectDescriptor (SelectDescriptor *descriptor) { |
| FD_ZERO(&descriptor->set); |
| descriptor->size = 0; |
| } |
| |
| static void |
| prepareMonitors (void) { |
| prepareSelectDescriptor(&selectDescriptor_read); |
| prepareSelectDescriptor(&selectDescriptor_write); |
| prepareSelectDescriptor(&selectDescriptor_exception); |
| } |
| |
| static fd_set * |
| getSelectSet (SelectDescriptor *descriptor) { |
| return descriptor->size? &descriptor->set: NULL; |
| } |
| |
| static int |
| doSelect (int setSize, fd_set *readSet, fd_set *writeSet, fd_set *exceptionSet, int timeout) { |
| struct timeval time = { |
| .tv_sec = timeout / MSECS_PER_SEC, |
| .tv_usec = (timeout % MSECS_PER_SEC) * USECS_PER_MSEC |
| }; |
| |
| { |
| int result = select(setSize, readSet, writeSet, exceptionSet, &time); |
| if (result > 0) return 1; |
| |
| if (result == -1) { |
| if (errno != EINTR) logSystemError("select"); |
| } |
| |
| return 0; |
| } |
| } |
| |
| static int |
| awaitMonitors (const MonitorGroup *monitors, int timeout) { |
| fd_set *readSet = getSelectSet(&selectDescriptor_read); |
| fd_set *writeSet = getSelectSet(&selectDescriptor_write); |
| fd_set *exceptionSet = getSelectSet(&selectDescriptor_exception); |
| |
| int setSize = selectDescriptor_read.size; |
| setSize = MAX(setSize, selectDescriptor_write.size); |
| setSize = MAX(setSize, selectDescriptor_exception.size); |
| |
| #ifdef __MSDOS__ |
| int elapsed = 0; |
| |
| do { |
| fd_set readSet1, writeSet1, exceptionSet1; |
| |
| if (readSet) readSet1 = *readSet; |
| if (writeSet) writeSet1 = *writeSet; |
| if (exceptionSet) exceptionSet1 = *exceptionSet; |
| |
| if (doSelect(setSize, |
| (readSet? &readSet1: NULL), |
| (writeSet? &writeSet1: NULL), |
| (exceptionSet? &exceptionSet1: NULL), |
| 0)) { |
| if (readSet) *readSet = readSet1; |
| if (writeSet) *writeSet = writeSet1; |
| if (exceptionSet) *exceptionSet = exceptionSet1; |
| return 1; |
| } |
| } while ((elapsed += msdosUSleep(USECS_PER_MSEC)) < timeout); |
| #else /* __MSDOS__ */ |
| if (doSelect(setSize, readSet, writeSet, exceptionSet, timeout)) return 1; |
| #endif /* __MSDOS__ */ |
| |
| return 0; |
| } |
| |
| static void |
| initializeMonitor (MonitorEntry *monitor, const FunctionEntry *function, const OperationEntry *operation) { |
| monitor->selectSet = &function->select.descriptor->set; |
| monitor->fileDescriptor = function->fileDescriptor; |
| FD_SET(function->fileDescriptor, &function->select.descriptor->set); |
| |
| if (function->fileDescriptor >= function->select.descriptor->size) { |
| function->select.descriptor->size = function->fileDescriptor + 1; |
| } |
| } |
| |
| static int |
| testMonitor (const MonitorEntry *monitor, int *error) { |
| return FD_ISSET(monitor->fileDescriptor, monitor->selectSet); |
| } |
| |
| static void |
| beginUnixInputFunction (FunctionEntry *function) { |
| function->select.descriptor = &selectDescriptor_read; |
| } |
| |
| static void |
| beginUnixOutputFunction (FunctionEntry *function) { |
| function->select.descriptor = &selectDescriptor_write; |
| } |
| |
| static void |
| beginUnixAlertFunction (FunctionEntry *function) { |
| function->select.descriptor = &selectDescriptor_exception; |
| } |
| |
| #endif /* Unix I/O monitoring capabilities */ |
| |
| #ifdef ASYNC_CAN_MONITOR_IO |
| static void |
| setUnixTransferResult (OperationEntry *operation, ssize_t result) { |
| TransferExtension *extension = operation->extension; |
| |
| if (result == -1) { |
| operation->error = errno; |
| } else if (result == 0) { |
| extension->direction.input.end = 1; |
| } else { |
| extension->length += result; |
| } |
| |
| operation->finished = 1; |
| } |
| |
| static void |
| finishUnixRead (OperationEntry *operation) { |
| FunctionEntry *function = operation->function; |
| TransferExtension *extension = operation->extension; |
| ssize_t result = read(function->fileDescriptor, |
| &extension->buffer[extension->length], |
| extension->size - extension->length); |
| |
| setUnixTransferResult(operation, result); |
| } |
| |
| static void |
| finishUnixWrite (OperationEntry *operation) { |
| FunctionEntry *function = operation->function; |
| TransferExtension *extension = operation->extension; |
| ssize_t result = write(function->fileDescriptor, |
| &extension->buffer[extension->length], |
| extension->size - extension->length); |
| |
| setUnixTransferResult(operation, result); |
| } |
| #endif /* ASYNC_CAN_MONITOR_IO */ |
| #endif /* __MINGW32__ */ |
| |
| #ifdef ASYNC_CAN_MONITOR_IO |
| static void |
| deallocateFunctionEntry (void *item, void *data) { |
| FunctionEntry *function = item; |
| |
| if (function->operations) deallocateQueue(function->operations); |
| if (function->methods->endFunction) function->methods->endFunction(function); |
| free(function); |
| } |
| |
| static Queue * |
| getFunctionQueue (int create) { |
| AsyncIoData *iod = getIoData(); |
| if (!iod) return NULL; |
| |
| if (!iod->functionQueue && create) { |
| iod->functionQueue = newQueue(deallocateFunctionEntry, NULL); |
| } |
| |
| return iod->functionQueue; |
| } |
| |
| static int |
| invokeMonitorCallback (OperationEntry *operation) { |
| MonitorExtension *extension = operation->extension; |
| AsyncMonitorCallback *callback = extension->callback; |
| |
| logOperation(operation, callback); |
| |
| if (callback) { |
| const AsyncMonitorCallbackParameters parameters = { |
| .error = operation->error, |
| .data = operation->data |
| }; |
| |
| if (callback(¶meters)) { |
| if (!operation->error) { |
| return 1; |
| } |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| invokeInputCallback (OperationEntry *operation) { |
| TransferExtension *extension = operation->extension; |
| AsyncInputCallback *callback = extension->direction.input.callback; |
| size_t count; |
| |
| logOperation(operation, callback); |
| |
| if (!callback) return 0; |
| |
| { |
| const AsyncInputCallbackParameters parameters = { |
| .data = operation->data, |
| .buffer = extension->buffer, |
| .size = extension->size, |
| .length = extension->length, |
| .error = operation->error, |
| .end = extension->direction.input.end |
| }; |
| |
| count = callback(¶meters); |
| } |
| |
| if (operation->error) return 0; |
| if (extension->direction.input.end) return 0; |
| |
| operation->finished = 0; |
| if (count) { |
| memmove(extension->buffer, &extension->buffer[count], |
| extension->length -= count); |
| if (extension->length > 0) operation->finished = 1; |
| } |
| |
| return 1; |
| } |
| |
| static int |
| invokeOutputCallback (OperationEntry *operation) { |
| TransferExtension *extension = operation->extension; |
| AsyncOutputCallback *callback = extension->direction.output.callback; |
| |
| logOperation(operation, callback); |
| |
| if (!operation->error && (extension->length < extension->size)) { |
| operation->finished = 0; |
| return 1; |
| } |
| |
| if (callback) { |
| const AsyncOutputCallbackParameters parameters = { |
| .data = operation->data, |
| .buffer = extension->buffer, |
| .size = extension->size, |
| .error = operation->error |
| }; |
| |
| callback(¶meters); |
| } |
| |
| return 0; |
| } |
| |
| static Element * |
| getActiveOperationElement (const FunctionEntry *function) { |
| Queue *queue = function->operations; |
| |
| if (function->methods->invokeCallback == invokeMonitorCallback) return getStackHead(queue); |
| return getQueueHead(queue); |
| } |
| |
| static OperationEntry * |
| getActiveOperation (const FunctionEntry *function) { |
| Element *element = getActiveOperationElement(function); |
| |
| if (element) return getElementItem(element); |
| return NULL; |
| } |
| |
| static void |
| startOperation (OperationEntry *operation) { |
| if (operation->function->methods->startOperation) { |
| operation->function->methods->startOperation(operation); |
| } |
| } |
| |
| static void |
| finishOperation (OperationEntry *operation) { |
| if (operation->function->methods->finishOperation) { |
| operation->function->methods->finishOperation(operation); |
| } |
| } |
| |
| static int |
| addFunctionMonitor (void *item, void *data) { |
| const FunctionEntry *function = item; |
| MonitorGroup *monitors = data; |
| OperationEntry *operation = getActiveOperation(function); |
| |
| if (operation) { |
| operation->monitor = NULL; |
| |
| if (!operation->active) { |
| if (operation->finished) return 1; |
| |
| operation->monitor = &monitors->array[monitors->count++]; |
| initializeMonitor(operation->monitor, function, operation); |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| testFunctionMonitor (void *item, void *data) { |
| FunctionEntry *function = item; |
| OperationEntry *operation = getActiveOperation(function); |
| |
| if (operation && operation->monitor) { |
| int *error = &operation->error; |
| |
| *error = 0; |
| if (testMonitor(operation->monitor, error)) return 1; |
| } |
| |
| return 0; |
| } |
| |
| int |
| asyncExecuteIoCallback (AsyncIoData *iod, long int timeout) { |
| if (iod) { |
| Queue *functions = iod->functionQueue; |
| unsigned int functionCount = functions? getQueueSize(functions): 0; |
| |
| prepareMonitors(); |
| |
| if (functionCount) { |
| MonitorEntry monitorArray[functionCount]; |
| MonitorGroup monitors = { |
| .array = monitorArray, |
| .count = 0 |
| }; |
| |
| int executed = 0; |
| Element *functionElement = processQueue(functions, addFunctionMonitor, &monitors); |
| |
| if (!functionElement) { |
| if (!monitors.count) { |
| approximateDelay(timeout); |
| } else if (awaitMonitors(&monitors, timeout)) { |
| functionElement = processQueue(functions, testFunctionMonitor, NULL); |
| } |
| } |
| |
| if (functionElement) { |
| FunctionEntry *function = getElementItem(functionElement); |
| Element *operationElement = getActiveOperationElement(function); |
| OperationEntry *operation = getElementItem(operationElement); |
| |
| if (!operation->finished) finishOperation(operation); |
| |
| operation->active = 1; |
| if (!function->methods->invokeCallback(operation)) operation->cancel = 1; |
| operation->active = 0; |
| executed = 1; |
| |
| if (operation->cancel) { |
| deleteElement(operationElement); |
| } else { |
| operation->error = 0; |
| } |
| |
| if ((operationElement = getActiveOperationElement(function))) { |
| operation = getElementItem(operationElement); |
| if (!operation->finished) startOperation(operation); |
| requeueElement(functionElement); |
| } else { |
| deleteElement(functionElement); |
| } |
| } |
| |
| return executed; |
| } |
| } |
| |
| approximateDelay(timeout); |
| return 0; |
| } |
| |
| static void |
| deallocateOperationEntry (void *item, void *data) { |
| OperationEntry *operation = item; |
| if (operation->extension) free(operation->extension); |
| free(operation); |
| } |
| |
| static void |
| cancelOperation (Element *operationElement) { |
| OperationEntry *operation = getElementItem(operationElement); |
| |
| if (operation->active) { |
| operation->cancel = 1; |
| } else { |
| FunctionEntry *function = operation->function; |
| int isFirstOperation = operationElement == getActiveOperationElement(function); |
| |
| if (isFirstOperation) { |
| if (!operation->finished) { |
| if (operation->function->methods->cancelOperation) { |
| operation->function->methods->cancelOperation(operation); |
| } |
| } |
| } |
| |
| if (getQueueSize(function->operations) == 1) { |
| deleteElement(findElementWithItem(getFunctionQueue(0), function)); |
| } else { |
| deleteElement(operationElement); |
| |
| if (isFirstOperation) { |
| operationElement = getActiveOperationElement(function); |
| operation = getElementItem(operationElement); |
| |
| if (!operation->finished) startOperation(operation); |
| } |
| } |
| } |
| } |
| |
| static int |
| testFunctionEntry (const void *item, void *data) { |
| const FunctionEntry *function = item; |
| const FunctionKey *key = data; |
| return (function->fileDescriptor == key->fileDescriptor) && |
| (function->methods == key->methods); |
| } |
| |
| static Element * |
| getFunctionElement (FileDescriptor fileDescriptor, const FunctionMethods *methods, int create) { |
| Queue *functions = getFunctionQueue(create); |
| |
| if (functions) { |
| { |
| FunctionKey key = { |
| .fileDescriptor = fileDescriptor, |
| .methods = methods |
| }; |
| |
| { |
| Element *element = findElement(functions, testFunctionEntry, &key); |
| if (element) return element; |
| } |
| } |
| |
| if (create) { |
| FunctionEntry *function; |
| |
| if ((function = malloc(sizeof(*function)))) { |
| function->fileDescriptor = fileDescriptor; |
| function->methods = methods; |
| |
| if ((function->operations = newQueue(deallocateOperationEntry, NULL))) { |
| { |
| static AsyncQueueMethods methods = { |
| .cancelRequest = cancelOperation |
| }; |
| |
| setQueueData(function->operations, &methods); |
| } |
| |
| if (methods->beginFunction) methods->beginFunction(function); |
| |
| { |
| Element *element = enqueueItem(functions, function); |
| if (element) return element; |
| } |
| |
| deallocateQueue(function->operations); |
| } |
| |
| free(function); |
| } else { |
| logMallocError(); |
| } |
| } |
| } |
| |
| return NULL; |
| } |
| |
| static Element * |
| newOperation ( |
| FileDescriptor fileDescriptor, |
| const FunctionMethods *methods, |
| void *extension, |
| void *data |
| ) { |
| OperationEntry *operation; |
| |
| if ((operation = malloc(sizeof(*operation)))) { |
| Element *functionElement; |
| |
| if ((functionElement = getFunctionElement(fileDescriptor, methods, 1))) { |
| FunctionEntry *function = getElementItem(functionElement); |
| int isFirstOperation = !getQueueSize(function->operations); |
| Element *operationElement = enqueueItem(function->operations, operation); |
| |
| if (operationElement) { |
| operation->function = function; |
| operation->extension = extension; |
| operation->data = data; |
| |
| operation->monitor = NULL; |
| operation->error = 0; |
| |
| operation->active = 0; |
| operation->cancel = 0; |
| operation->finished = 0; |
| |
| if (isFirstOperation) startOperation(operation); |
| return operationElement; |
| } |
| |
| if (isFirstOperation) deleteElement(functionElement); |
| } |
| |
| free(operation); |
| } else { |
| logMallocError(); |
| } |
| |
| return NULL; |
| } |
| |
| typedef struct { |
| FileDescriptor fileDescriptor; |
| const FunctionMethods *methods; |
| AsyncMonitorCallback *callback; |
| void *data; |
| } MonitorFileOperationParameters; |
| |
| static Element * |
| newFileMonitorOperation (const void *parameters) { |
| const MonitorFileOperationParameters *mop = parameters; |
| MonitorExtension *extension; |
| |
| if ((extension = malloc(sizeof(*extension)))) { |
| extension->callback = mop->callback; |
| |
| { |
| Element *element = newOperation(mop->fileDescriptor, mop->methods, extension, mop->data); |
| |
| if (element) return element; |
| } |
| |
| free(extension); |
| } else { |
| logMallocError(); |
| } |
| |
| return NULL; |
| } |
| |
| static Element * |
| newTransferOperation ( |
| FileDescriptor fileDescriptor, |
| const FunctionMethods *methods, |
| const TransferDirectionUnion *direction, |
| size_t size, const void *buffer, |
| void *data |
| ) { |
| TransferExtension *extension; |
| |
| if ((extension = malloc(sizeof(*extension) + size))) { |
| extension->direction = *direction; |
| extension->size = size; |
| extension->length = 0; |
| if (buffer) memcpy(extension->buffer, buffer, size); |
| |
| { |
| Element *element = newOperation(fileDescriptor, methods, extension, data); |
| |
| if (element) return element; |
| } |
| |
| free(extension); |
| } else { |
| logMallocError(); |
| } |
| |
| return NULL; |
| } |
| |
| typedef struct { |
| FileDescriptor fileDescriptor; |
| size_t size; |
| AsyncInputCallback *callback; |
| void *data; |
| } InputOperationParameters; |
| |
| static Element * |
| newInputOperation (const void *parameters) { |
| const InputOperationParameters *iop = parameters; |
| |
| TransferDirectionUnion direction = { |
| .input = { |
| .callback = iop->callback, |
| .end = 0 |
| } |
| }; |
| |
| static const FunctionMethods methods = { |
| .functionName = "input transferred", |
| |
| #ifdef __MINGW32__ |
| .beginFunction = beginWindowsFunction, |
| .endFunction = endWindowsFunction, |
| .startOperation = startWindowsRead, |
| .finishOperation = finishWindowsTransferOperation, |
| .cancelOperation = cancelWindowsTransferOperation, |
| #else /* __MINGW32__ */ |
| .beginFunction = beginUnixInputFunction, |
| .finishOperation = finishUnixRead, |
| #endif /* __MINGW32__ */ |
| |
| .invokeCallback = invokeInputCallback |
| }; |
| |
| return newTransferOperation(iop->fileDescriptor, &methods, &direction, iop->size, NULL, iop->data); |
| } |
| |
| typedef struct { |
| FileDescriptor fileDescriptor; |
| size_t size; |
| const void *buffer; |
| AsyncOutputCallback *callback; |
| void *data; |
| } OutputOperationParameters; |
| |
| static Element * |
| newOutputOperation (const void *parameters) { |
| const OutputOperationParameters *oop = parameters; |
| |
| TransferDirectionUnion direction = { |
| .output = { |
| .callback = oop->callback |
| } |
| }; |
| |
| static const FunctionMethods methods = { |
| .functionName = "output transferred", |
| |
| #ifdef __MINGW32__ |
| .beginFunction = beginWindowsFunction, |
| .endFunction = endWindowsFunction, |
| .startOperation = startWindowsWrite, |
| .finishOperation = finishWindowsTransferOperation, |
| .cancelOperation = cancelWindowsTransferOperation, |
| #else /* __MINGW32__ */ |
| .beginFunction = beginUnixOutputFunction, |
| .finishOperation = finishUnixWrite, |
| #endif /* __MINGW32__ */ |
| |
| .invokeCallback = invokeOutputCallback |
| }; |
| |
| return newTransferOperation(oop->fileDescriptor, &methods, &direction, oop->size, oop->buffer, oop->data); |
| } |
| |
| #else /* ASYNC_CAN_MONITOR_IO */ |
| int |
| asyncHandleOperation (AsyncThreadSpecificData *tsd, long int timeout) { |
| approximateDelay(timeout); |
| return 0; |
| } |
| #endif /* ASYNC_CAN_MONITOR_IO */ |
| |
| int |
| asyncMonitorFileInput ( |
| AsyncHandle *handle, |
| FileDescriptor fileDescriptor, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| #ifdef ASYNC_CAN_MONITOR_IO |
| static const FunctionMethods methods = { |
| .functionName = "file input monitor", |
| |
| #ifdef __MINGW32__ |
| .beginFunction = beginWindowsFunction, |
| .endFunction = endWindowsFunction, |
| #else /* __MINGW32__ */ |
| .beginFunction = beginUnixInputFunction, |
| #endif /* __MINGW32__ */ |
| |
| .invokeCallback = invokeMonitorCallback |
| }; |
| |
| const MonitorFileOperationParameters mop = { |
| .fileDescriptor = fileDescriptor, |
| .methods = &methods, |
| .callback = callback, |
| .data = data |
| }; |
| |
| return asyncMakeHandle(handle, newFileMonitorOperation, &mop); |
| #else /* ASYNC_CAN_MONITOR_IO */ |
| logUnsupportedFunction(); |
| return 0; |
| #endif /* ASYNC_CAN_MONITOR_IO */ |
| } |
| |
| int |
| asyncMonitorFileOutput ( |
| AsyncHandle *handle, |
| FileDescriptor fileDescriptor, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| #ifdef ASYNC_CAN_MONITOR_IO |
| static const FunctionMethods methods = { |
| .functionName = "file output monitor", |
| |
| #ifdef __MINGW32__ |
| .beginFunction = beginWindowsFunction, |
| .endFunction = endWindowsFunction, |
| #else /* __MINGW32__ */ |
| .beginFunction = beginUnixOutputFunction, |
| #endif /* __MINGW32__ */ |
| |
| .invokeCallback = invokeMonitorCallback |
| }; |
| |
| const MonitorFileOperationParameters mop = { |
| .fileDescriptor = fileDescriptor, |
| .methods = &methods, |
| .callback = callback, |
| .data = data |
| }; |
| |
| return asyncMakeHandle(handle, newFileMonitorOperation, &mop); |
| #else /* ASYNC_CAN_MONITOR_IO */ |
| logUnsupportedFunction(); |
| return 0; |
| #endif /* ASYNC_CAN_MONITOR_IO */ |
| } |
| |
| int |
| asyncMonitorFileAlert ( |
| AsyncHandle *handle, |
| FileDescriptor fileDescriptor, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| #ifdef ASYNC_CAN_MONITOR_IO |
| static const FunctionMethods methods = { |
| .functionName = "file alert monitor", |
| |
| #ifdef __MINGW32__ |
| .beginFunction = beginWindowsFunction, |
| .endFunction = endWindowsFunction, |
| #else /* __MINGW32__ */ |
| .beginFunction = beginUnixAlertFunction, |
| #endif /* __MINGW32__ */ |
| |
| .invokeCallback = invokeMonitorCallback |
| }; |
| |
| const MonitorFileOperationParameters mop = { |
| .fileDescriptor = fileDescriptor, |
| .methods = &methods, |
| .callback = callback, |
| .data = data |
| }; |
| |
| return asyncMakeHandle(handle, newFileMonitorOperation, &mop); |
| #else /* ASYNC_CAN_MONITOR_IO */ |
| logUnsupportedFunction(); |
| return 0; |
| #endif /* ASYNC_CAN_MONITOR_IO */ |
| } |
| |
| int |
| asyncReadFile ( |
| AsyncHandle *handle, |
| FileDescriptor fileDescriptor, |
| size_t size, |
| AsyncInputCallback *callback, void *data |
| ) { |
| #ifdef ASYNC_CAN_MONITOR_IO |
| const InputOperationParameters iop = { |
| .fileDescriptor = fileDescriptor, |
| .size = size, |
| .callback = callback, |
| .data = data |
| }; |
| |
| return asyncMakeHandle(handle, newInputOperation, &iop); |
| #else /* ASYNC_CAN_MONITOR_IO */ |
| logUnsupportedFunction(); |
| return 0; |
| #endif /* ASYNC_CAN_MONITOR_IO */ |
| } |
| |
| int |
| asyncWriteFile ( |
| AsyncHandle *handle, |
| FileDescriptor fileDescriptor, |
| const void *buffer, size_t size, |
| AsyncOutputCallback *callback, void *data |
| ) { |
| #ifdef ASYNC_CAN_MONITOR_IO |
| const OutputOperationParameters oop = { |
| .fileDescriptor = fileDescriptor, |
| .size = size, |
| .buffer = buffer, |
| .callback = callback, |
| .data = data |
| }; |
| |
| return asyncMakeHandle(handle, newOutputOperation, &oop); |
| #else /* ASYNC_CAN_MONITOR_IO */ |
| logUnsupportedFunction(); |
| return 0; |
| #endif /* ASYNC_CAN_MONITOR_IO */ |
| } |
| |
| #ifdef __MINGW32__ |
| int |
| asyncMonitorSocketInput ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| logUnsupportedFunction(); |
| return 0; |
| } |
| |
| int |
| asyncMonitorSocketOutput ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| logUnsupportedFunction(); |
| return 0; |
| } |
| |
| int |
| asyncMonitorSocketAlert ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| logUnsupportedFunction(); |
| return 0; |
| } |
| |
| int |
| asyncReadSocket ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| size_t size, |
| AsyncInputCallback *callback, void *data |
| ) { |
| logUnsupportedFunction(); |
| return 0; |
| } |
| |
| int |
| asyncWriteSocket ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| const void *buffer, size_t size, |
| AsyncOutputCallback *callback, void *data |
| ) { |
| logUnsupportedFunction(); |
| return 0; |
| } |
| |
| #else /* __MINGW32__ */ |
| int |
| asyncMonitorSocketInput ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| return asyncMonitorFileInput(handle, socketDescriptor, callback, data); |
| } |
| |
| int |
| asyncMonitorSocketOutput ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| return asyncMonitorFileOutput(handle, socketDescriptor, callback, data); |
| } |
| |
| int |
| asyncMonitorSocketAlert ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| AsyncMonitorCallback *callback, void *data |
| ) { |
| return asyncMonitorFileAlert(handle, socketDescriptor, callback, data); |
| } |
| |
| int |
| asyncReadSocket ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| size_t size, |
| AsyncInputCallback *callback, void *data |
| ) { |
| return asyncReadFile(handle, socketDescriptor, size, callback, data); |
| } |
| |
| int |
| asyncWriteSocket ( |
| AsyncHandle *handle, |
| SocketDescriptor socketDescriptor, |
| const void *buffer, size_t size, |
| AsyncOutputCallback *callback, void *data |
| ) { |
| return asyncWriteFile(handle, socketDescriptor, buffer, size, callback, data); |
| } |
| #endif /* __MINGW32__ */ |