From 1c32a46d5a93d8b88b35a7289ab275ac8c371959 Mon Sep 17 00:00:00 2001 From: Michael McCormack Date: Thu, 22 Mar 2001 20:09:34 +0000 Subject: [PATCH] Use poll() on the client-side during server waits to implement overlapped I/O. --- dlls/kernel/comm.c | 176 ++++++++++++++++++------------ files/file.c | 254 +++++++++++++++++++++++++------------------- include/file.h | 17 +++ include/server.h | 21 +--- include/thread.h | 3 +- scheduler/synchro.c | 125 +++++++++++++++++++++- server/async.c | 253 +------------------------------------------ server/object.h | 12 +-- server/request.h | 2 - server/serial.c | 65 ++---------- server/trace.c | 14 +-- 11 files changed, 407 insertions(+), 535 deletions(-) diff --git a/dlls/kernel/comm.c b/dlls/kernel/comm.c index 3c6fd9d84da..b70c41bf739 100644 --- a/dlls/kernel/comm.c +++ b/dlls/kernel/comm.c @@ -50,6 +50,7 @@ #endif #include #include +#include #include "windef.h" #ifdef HAVE_SYS_MODEM_H @@ -2814,19 +2815,102 @@ BOOL WINAPI GetCommModemStatus( #endif } -VOID COMM_WaitCommEventService(void **args) +/*********************************************************************** + * COMM_WaitCommEventService (INTERNAL) + * + * This function is called while the client is waiting on the + * server, so we can't make any server calls here. + */ +static void COMM_WaitCommEventService(async_private *ovp, int events) { - LPOVERLAPPED lpOverlapped = (LPOVERLAPPED)args[0]; - LPDWORD buffer = (LPDWORD)args[1]; - DWORD events = (DWORD)args[2]; + LPOVERLAPPED lpOverlapped = ovp->lpOverlapped; - TRACE("overlapped %p wait complete %p <- %lx\n",lpOverlapped,buffer,events); - if(buffer) - *buffer = events; + TRACE("overlapped %p wait complete %p <- %x\n",lpOverlapped,ovp->buffer,events); + if(events&POLLNVAL) + { + lpOverlapped->Internal = STATUS_HANDLES_CLOSED; + return; + } + if(ovp->buffer) + { + if(events&POLLIN) + *ovp->buffer = EV_RXCHAR; + } lpOverlapped->Internal = STATUS_SUCCESS; - SetEvent( lpOverlapped->hEvent); - CloseHandle(lpOverlapped->InternalHigh); +} + + + +/*********************************************************************** + * COMM_WaitCommEvent (INTERNAL) + * + * This function must have an lpOverlapped. + */ +static BOOL COMM_WaitCommEvent( + HANDLE hFile, /* [in] handle of comm port to wait for */ + LPDWORD lpdwEvents, /* [out] event(s) that were detected */ + LPOVERLAPPED lpOverlapped) /* [in/out] for Asynchronous waiting */ +{ + int fd,ret; + async_private *ovp; + + if(!lpOverlapped) + { + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + if(NtResetEvent(lpOverlapped->hEvent,NULL)) + return FALSE; + + lpOverlapped->Internal = STATUS_PENDING; + lpOverlapped->InternalHigh = 0; + lpOverlapped->Offset = 0; + lpOverlapped->OffsetHigh = 0; + + /* start an ASYNCHRONOUS WaitCommEvent */ + SERVER_START_REQ( create_async ) + { + req->file_handle = hFile; + req->count = 0; + req->type = ASYNC_TYPE_WAIT; + + ret=SERVER_CALL_ERR(); + } + SERVER_END_REQ; + + if (ret) + return FALSE; + + fd = FILE_GetUnixHandle( hFile, GENERIC_WRITE ); + if(fd<0) + return FALSE; + + ovp = (async_private *) HeapAlloc(GetProcessHeap(), 0, sizeof (async_private)); + if(!ovp) + { + close(fd); + return FALSE; + } + ovp->lpOverlapped = lpOverlapped; + ovp->timeout = 0; + ovp->tv.tv_sec = 0; + ovp->tv.tv_usec = 0; + ovp->event = POLLIN; + ovp->func = COMM_WaitCommEventService; + ovp->buffer = (char *)lpdwEvents; + ovp->fd = fd; + + ovp->next = NtCurrentTeb()->pending_list; + ovp->prev = NULL; + if(ovp->next) + ovp->next->prev=ovp; + NtCurrentTeb()->pending_list = ovp; + + SetLastError(ERROR_IO_PENDING); + + return FALSE; } /*********************************************************************** @@ -2852,79 +2936,31 @@ BOOL WINAPI WaitCommEvent( LPOVERLAPPED lpOverlapped) /* [in/out] for Asynchronous waiting */ { OVERLAPPED ov; - LPOVERLAPPED lpov; int ret; TRACE("(%x %p %p )\n",hFile, lpdwEvents,lpOverlapped); + if(lpOverlapped) + return COMM_WaitCommEvent(hFile, lpdwEvents, lpOverlapped); + /* if there is no overlapped structure, create our own */ - if(!lpOverlapped) - { - ov.hEvent = CreateEventA(NULL,FALSE,FALSE,NULL); - lpov = &ov; - } - else - lpov = lpOverlapped; + ov.hEvent = CreateEventA(NULL,FALSE,FALSE,NULL); - /* check that the overlapped structure has a valid event flag */ - if ( (lpov->hEvent==0) || (lpov->hEvent == INVALID_HANDLE_VALUE) ) + COMM_WaitCommEvent(hFile, lpdwEvents, &ov); + + if(GetLastError()!=STATUS_PENDING) { - ERR("Couldn't create Event flag for Overlapped structure\n"); - SetLastError(ERROR_INVALID_PARAMETER); + CloseHandle(ov.hEvent); return FALSE; } - ResetEvent(lpov->hEvent); + /* wait for the overlapped to complete */ + ret = GetOverlappedResult(hFile, &ov, NULL, TRUE); + CloseHandle(ov.hEvent); - lpov->Internal = STATUS_PENDING; - lpov->InternalHigh = 0; - lpov->Offset = 0; - lpov->OffsetHigh = 0; - - /* start an ASYNCHRONOUS WaitCommEvent */ - SERVER_START_REQ( create_async ) - { - req->file_handle = hFile; - req->overlapped = lpov; - req->buffer = lpdwEvents; - req->count = 0; - req->func = COMM_WaitCommEventService; - req->type = ASYNC_TYPE_WAIT; - - ret=SERVER_CALL_ERR(); - - lpov->InternalHigh = req->ov_handle; - } - SERVER_END_REQ; - - if(ret) - { - if(!lpOverlapped) - CloseHandle(lpov->hEvent); - TRACE("server call failed.\n"); - return FALSE; - } - - /* activate the overlapped operation */ - lpov->Internal = STATUS_PENDING; - - /* wait ourselves if the caller didn't give us an overlapped struct */ - if(!lpOverlapped) - { - GetOverlappedResult(hFile, lpov, NULL, TRUE); - CloseHandle(lpov->hEvent); - lpov->hEvent=0; - } - else - { - /* caller wants overlapped I/O using GetOverlapped result */ - SetLastError(ERROR_IO_PENDING); - return FALSE; - } - - return TRUE; + return ret; } - + /*********************************************************************** * GetCommProperties (KERNEL32.286) * diff --git a/files/file.c b/files/file.c index 1259ccff142..04e3bb7d48a 100644 --- a/files/file.c +++ b/files/file.c @@ -27,6 +27,7 @@ #include #endif #include +#include #include #include #include @@ -1196,60 +1197,43 @@ BOOL WINAPI GetOverlappedResult( *lpTransferred = lpOverlapped->InternalHigh; SetLastError(lpOverlapped->Internal); - + return (r==WAIT_OBJECT_0); } -/*********************************************************************** - * FILE_AsyncResult (INTERNAL) - */ -static int FILE_AsyncResult(HANDLE hAsync, int result) -{ - int r; - - SERVER_START_REQ( async_result ) - { - req->ov_handle = hAsync; - req->result = result; - r = SERVER_CALL_ERR(); - } - SERVER_END_REQ; - return !r; -} /*********************************************************************** * FILE_AsyncReadService (INTERNAL) + * + * This function is called while the client is waiting on the + * server, so we can't make any server calls here. */ -static void FILE_AsyncReadService(void **args) +static void FILE_AsyncReadService(async_private *ovp, int events) { - LPOVERLAPPED lpOverlapped = (LPOVERLAPPED)args[0]; - LPDWORD buffer = (LPDWORD)args[1]; - DWORD events = (DWORD)args[2]; - int fd, result, r; + LPOVERLAPPED lpOverlapped = ovp->lpOverlapped; + int result, r; - TRACE("%p %p %08lx\n", lpOverlapped, buffer, events ); + TRACE("%p %p %08x\n", lpOverlapped, ovp->buffer, events ); + + /* if POLLNVAL, then our fd was closed or we have the wrong fd */ + if(events&POLLNVAL) + { + ERR("fd %d invalid for %p\n",ovp->fd,ovp); + r = STATUS_UNSUCCESSFUL; + goto async_end; + } /* if there are no events, it must be a timeout */ if(events==0) { TRACE("read timed out\n"); - /* r = STATUS_TIMEOUT; */ - r = STATUS_SUCCESS; - goto async_end; - } - - fd = FILE_GetUnixHandle(lpOverlapped->Offset, GENERIC_READ); - if(fd<0) - { - TRACE("FILE_GetUnixHandle(%ld) failed \n",lpOverlapped->Offset); - r = STATUS_UNSUCCESSFUL; + r = STATUS_TIMEOUT; goto async_end; } /* check to see if the data is ready (non-blocking) */ - result = read(fd, &buffer[lpOverlapped->InternalHigh], + result = read(ovp->fd, &ovp->buffer[lpOverlapped->InternalHigh], lpOverlapped->OffsetHigh - lpOverlapped->InternalHigh); - close(fd); if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR))) { @@ -1276,50 +1260,91 @@ static void FILE_AsyncReadService(void **args) async_end: lpOverlapped->Internal = r; - if ( (r!=STATUS_PENDING) - || (!FILE_AsyncResult( lpOverlapped->InternalHigh, r))) - { - /* close the handle to the async operation */ - if(lpOverlapped->Offset) - CloseHandle(lpOverlapped->Offset); - lpOverlapped->Offset = 0; +} - NtSetEvent( lpOverlapped->hEvent, NULL ); - TRACE("set event flag\n"); +/* flogged from wineserver */ +/* add a timeout in milliseconds to an absolute time */ +static void add_timeout( struct timeval *when, int timeout ) +{ + if (timeout) + { + long sec = timeout / 1000; + if ((when->tv_usec += (timeout - 1000*sec) * 1000) >= 1000000) + { + when->tv_usec -= 1000000; + when->tv_sec++; + } + when->tv_sec += sec; } } /*********************************************************************** * FILE_StartAsyncRead (INTERNAL) + * + * Don't need thread safety, because the list of asyncs + * will only be modified in this thread. */ static BOOL FILE_StartAsyncRead( HANDLE hFile, LPOVERLAPPED overlapped, LPVOID buffer, DWORD count) { - int r; + async_private *ovp; + int fd, timeout, ret; - SERVER_START_REQ( create_async ) + /* + * Although the overlapped transfer will be done in this thread + * we still need to register the operation with the server, in + * case it is cancelled and to get a file handle and the timeout info. + */ + SERVER_START_REQ(create_async) { - req->file_handle = hFile; - req->overlapped = overlapped; - req->buffer = buffer; req->count = count; - req->func = FILE_AsyncReadService; req->type = ASYNC_TYPE_READ; - - r=SERVER_CALL_ERR(); - - overlapped->Offset = req->ov_handle; + req->file_handle = hFile; + ret = SERVER_CALL(); + timeout = req->timeout; } SERVER_END_REQ; - - if(!r) + if (ret) { - TRACE("ov=%ld IO is pending!!!\n",overlapped->Offset); - SetLastError(ERROR_IO_PENDING); + TRACE("server call failed\n"); + return FALSE; } - return !r; + fd = FILE_GetUnixHandle( hFile, GENERIC_WRITE ); + if(fd<0) + { + TRACE("Couldn't get FD\n"); + return FALSE; + } + + ovp = (async_private *) HeapAlloc(GetProcessHeap(), 0, sizeof (async_private)); + if(!ovp) + { + TRACE("HeapAlloc Failed\n"); + close(fd); + return FALSE; + } + ovp->lpOverlapped = overlapped; + ovp->timeout = timeout; + gettimeofday(&ovp->tv,NULL); + add_timeout(&ovp->tv,timeout); + ovp->event = POLLIN; + ovp->func = FILE_AsyncReadService; + ovp->buffer = buffer; + ovp->fd = fd; + + /* hook this overlap into the pending async operation list */ + ovp->next = NtCurrentTeb()->pending_list; + ovp->prev = NULL; + if(ovp->next) + ovp->next->prev = ovp; + NtCurrentTeb()->pending_list = ovp; + + SetLastError(ERROR_IO_PENDING); + + return TRUE; } + /*********************************************************************** * ReadFile (KERNEL32.577) */ @@ -1341,20 +1366,18 @@ BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead, if ( (overlapped->hEvent == 0) || (overlapped->hEvent == INVALID_HANDLE_VALUE) ) { + SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } overlapped->Offset = 0; overlapped->OffsetHigh = bytesToRead; - overlapped->Internal = 0; + overlapped->Internal = STATUS_PENDING; overlapped->InternalHigh = 0; NtResetEvent( overlapped->hEvent, NULL ); - if(FILE_StartAsyncRead(hFile, overlapped, buffer, bytesToRead)) - { - overlapped->Internal = STATUS_PENDING; - } + FILE_StartAsyncRead(hFile, overlapped, buffer, bytesToRead); /* always fail on return, either ERROR_IO_PENDING or other error */ return FALSE; @@ -1377,17 +1400,27 @@ BOOL WINAPI ReadFile( HANDLE hFile, LPVOID buffer, DWORD bytesToRead, return TRUE; } + /*********************************************************************** * FILE_AsyncWriteService (INTERNAL) + * + * This function is called while the client is waiting on the + * server, so we can't make any server calls here. */ -static void FILE_AsyncWriteService(void **args) +static void FILE_AsyncWriteService(struct async_private *ovp, int events) { - LPOVERLAPPED lpOverlapped = (LPOVERLAPPED)args[0]; - LPDWORD buffer = (LPDWORD)args[1]; - DWORD events = (DWORD)args[2]; - int fd, result, r; + LPOVERLAPPED lpOverlapped = ovp->lpOverlapped; + int result, r; - TRACE("(%p %p %lx)\n",lpOverlapped,buffer,events); + TRACE("(%p %p %08x)\n",lpOverlapped,ovp->buffer,events); + + /* if POLLNVAL, then our fd was closed or we have the wrong fd */ + if(events&POLLNVAL) + { + ERR("fd %d invalid for %p\n",ovp->fd,ovp); + r = STATUS_UNSUCCESSFUL; + goto async_end; + } /* if there are no events, it must be a timeout */ if(events==0) @@ -1397,18 +1430,9 @@ static void FILE_AsyncWriteService(void **args) goto async_end; } - fd = FILE_GetUnixHandle(lpOverlapped->Offset, GENERIC_WRITE); - if(fd<0) - { - ERR("FILE_GetUnixHandle(%ld) failed \n",lpOverlapped->Offset); - r = STATUS_UNSUCCESSFUL; - goto async_end; - } - /* write some data (non-blocking) */ - result = write(fd, &buffer[lpOverlapped->InternalHigh], + result = write(ovp->fd, &ovp->buffer[lpOverlapped->InternalHigh], lpOverlapped->OffsetHigh-lpOverlapped->InternalHigh); - close(fd); if ( (result<0) && ((errno == EAGAIN) || (errno == EINTR))) { @@ -1425,6 +1449,8 @@ static void FILE_AsyncWriteService(void **args) lpOverlapped->InternalHigh += result; + TRACE("wrote %d more bytes %ld/%ld so far\n",result,lpOverlapped->InternalHigh,lpOverlapped->OffsetHigh); + if(lpOverlapped->InternalHigh < lpOverlapped->OffsetHigh) r = STATUS_PENDING; else @@ -1432,15 +1458,6 @@ static void FILE_AsyncWriteService(void **args) async_end: lpOverlapped->Internal = r; - if ( (r!=STATUS_PENDING) - || (!FILE_AsyncResult( lpOverlapped->Offset, r))) - { - /* close the handle to the async operation */ - CloseHandle(lpOverlapped->Offset); - lpOverlapped->Offset = 0; - - NtSetEvent( lpOverlapped->hEvent, NULL ); - } } /*********************************************************************** @@ -1448,31 +1465,50 @@ async_end: */ static BOOL FILE_StartAsyncWrite(HANDLE hFile, LPOVERLAPPED overlapped, LPCVOID buffer,DWORD count) { - int r; + /* don't need thread safety, because the list will only be modified in this thread */ + async_private *ovp = (async_private*) HeapAlloc(GetProcessHeap(), 0, sizeof (async_private)); + int timeout,ret; - SERVER_START_REQ( create_async ) + SERVER_START_REQ(create_async) { - req->file_handle = hFile; - req->buffer = (LPVOID)buffer; - req->overlapped = overlapped; - req->count = 0; - req->func = FILE_AsyncWriteService; + req->count = count; req->type = ASYNC_TYPE_WRITE; - - r = SERVER_CALL_ERR(); - - overlapped->Offset = req->ov_handle; + req->file_handle = hFile; + ret = SERVER_CALL(); + timeout = req->timeout; } SERVER_END_REQ; + if (ret) + return FALSE; - if(!r) + /* need to register the overlapped with the server, get a file handle and the timeout info */ + ovp->lpOverlapped = overlapped; + ovp->timeout = timeout; + gettimeofday(&ovp->tv,NULL); + add_timeout(&ovp->tv,timeout); + ovp->event = POLLOUT; + ovp->func = FILE_AsyncWriteService; + ovp->buffer = (LPVOID) buffer; + ovp->fd = FILE_GetUnixHandle( hFile, GENERIC_WRITE ); + if(ovp->fd <0) { - SetLastError(ERROR_IO_PENDING); + HeapFree(GetProcessHeap(), 0, ovp); + return FALSE; } - return !r; + /* hook this overlap into the pending async operation list */ + ovp->next = NtCurrentTeb()->pending_list; + ovp->prev = NULL; + if(ovp->next) + ovp->next->prev = ovp; + NtCurrentTeb()->pending_list = ovp; + + SetLastError(ERROR_IO_PENDING); + + return TRUE; } + /*********************************************************************** * WriteFile (KERNEL32.738) */ @@ -1492,19 +1528,19 @@ BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite, { if ( (overlapped->hEvent == 0) || (overlapped->hEvent == INVALID_HANDLE_VALUE) ) + { + SetLastError(ERROR_INVALID_PARAMETER); return FALSE; + } overlapped->Offset = 0; overlapped->OffsetHigh = bytesToWrite; - overlapped->Internal = 0; + overlapped->Internal = STATUS_PENDING; overlapped->InternalHigh = 0; NtResetEvent( overlapped->hEvent, NULL ); - if (FILE_StartAsyncWrite(hFile, overlapped, buffer, bytesToWrite)) - { - overlapped->Internal = STATUS_PENDING; - } + FILE_StartAsyncWrite(hFile, overlapped, buffer, bytesToWrite); /* always fail on return, either ERROR_IO_PENDING or other error */ return FALSE; @@ -1530,7 +1566,7 @@ BOOL WINAPI WriteFile( HANDLE hFile, LPCVOID buffer, DWORD bytesToWrite, return TRUE; } - + /*********************************************************************** * WIN16_hread */ diff --git a/include/file.h b/include/file.h index 811455eb347..2093d0e45ed 100644 --- a/include/file.h +++ b/include/file.h @@ -8,6 +8,7 @@ #define __WINE_FILE_H #include /* time_t */ +#include #include "winbase.h" #include "wine/windef16.h" /* HFILE16 */ @@ -30,6 +31,22 @@ typedef struct int flags; } DOS_DEVICE; +/* overlapped private structure */ +struct async_private; +typedef void (*async_handler)(struct async_private *ovp, int revents); +typedef struct async_private +{ + LPOVERLAPPED lpOverlapped; + int fd; + int timeout; + struct timeval tv; + int event; + char *buffer; + async_handler func; + struct async_private *next; + struct async_private *prev; +} async_private; + /* locale-independent case conversion */ inline static char FILE_tolower( char c ) { diff --git a/include/server.h b/include/server.h index 87904b10b64..2f48af91c5c 100644 --- a/include/server.h +++ b/include/server.h @@ -1356,28 +1356,15 @@ struct set_serial_info_request struct create_async_request { REQUEST_HEADER; /* request header */ - IN handle_t file_handle; /* handle to comm port */ - IN void* overlapped; - IN void* buffer; + IN handle_t file_handle; /* handle to comm port, socket or file */ IN int count; - IN void* func; IN int type; - OUT handle_t ov_handle; + OUT int timeout; }; #define ASYNC_TYPE_READ 0x01 #define ASYNC_TYPE_WRITE 0x02 #define ASYNC_TYPE_WAIT 0x03 -/* - * Used by service thread to tell the server that the current - * operation has completed - */ -struct async_result_request -{ - REQUEST_HEADER; /* request header */ - IN handle_t ov_handle; - IN int result; /* NT status code */ -}; /* Everything below this line is generated automatically by tools/make_requests */ /* ### make_requests begin ### */ @@ -1492,7 +1479,6 @@ enum request REQ_get_serial_info, REQ_set_serial_info, REQ_create_async, - REQ_async_result, REQ_NB_REQUESTS }; @@ -1608,10 +1594,9 @@ union generic_request struct get_serial_info_request get_serial_info; struct set_serial_info_request set_serial_info; struct create_async_request create_async; - struct async_result_request async_result; }; -#define SERVER_PROTOCOL_VERSION 43 +#define SERVER_PROTOCOL_VERSION 44 /* ### make_requests end ### */ /* Everything above this line is generated automatically by tools/make_requests */ diff --git a/include/thread.h b/include/thread.h index f26239ca956..264c2fb1304 100644 --- a/include/thread.h +++ b/include/thread.h @@ -100,10 +100,11 @@ typedef struct _TEB int wait_fd[2]; /* --3 214 fd for sleeping server requests */ void *debug_info; /* --3 21c Info for debugstr functions */ void *pthread_data; /* --3 220 Data for pthread emulation */ + struct async_private *pending_list; /* --3 224 list of pending async operations */ /* here is plenty space for wine specific fields (don't forget to change pad6!!) */ /* the following are nt specific fields */ - DWORD pad6[629]; /* --n 224 */ + DWORD pad6[628]; /* --n 228 */ UNICODE_STRING StaticUnicodeString; /* -2- bf8 used by advapi32 */ USHORT StaticUnicodeBuffer[261]; /* -2- c00 used by advapi32 */ DWORD pad7; /* --n e0c */ diff --git a/scheduler/synchro.c b/scheduler/synchro.c index 45159eec839..0add600e89d 100644 --- a/scheduler/synchro.c +++ b/scheduler/synchro.c @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -35,6 +36,125 @@ inline static void get_timeout( struct timeval *when, int timeout ) } } +#define MAX_NUMBER_OF_FDS 20 + +static inline int time_before( struct timeval *t1, struct timeval *t2 ) +{ + return ((t1->tv_sec < t2->tv_sec) || + ((t1->tv_sec == t2->tv_sec) && (t1->tv_usec < t2->tv_usec))); +} + +static void finish_async(async_private *ovp) +{ + /* remove it from the active list */ + if(ovp->prev) + ovp->prev->next = ovp->next; + else + NtCurrentTeb()->pending_list = ovp->next; + + if(ovp->next) + ovp->next->prev = ovp->prev; + + ovp->next=NULL; + ovp->prev=NULL; + + close(ovp->fd); + NtSetEvent(ovp->lpOverlapped->hEvent,NULL); + HeapFree(GetProcessHeap(), 0, ovp); +} + +/*********************************************************************** + * check_async_list + * + * Create a list of fds for poll to check while waiting on the server + * FIXME: this loop is too large, cut into smaller functions + * perhaps we could share/steal some of the code in server/select.c? + */ +static void check_async_list(void) +{ + /* FIXME: should really malloc these two arrays */ + struct pollfd fds[MAX_NUMBER_OF_FDS]; + async_private *user[MAX_NUMBER_OF_FDS], *tmp; + int i, n, r, timeout; + async_private *ovp, *timeout_user; + struct timeval now; + + while(1) + { + /* the first fd belongs to the server connection */ + fds[0].events=POLLIN; + fds[0].revents=0; + fds[0].fd = NtCurrentTeb()->wait_fd[0]; + + ovp = NtCurrentTeb()->pending_list; + timeout = -1; + timeout_user = NULL; + gettimeofday(&now,NULL); + for(n=1; ovp && (nnext; + + if(ovp->lpOverlapped->Internal!=STATUS_PENDING) + { + ovp->lpOverlapped->Internal=STATUS_UNSUCCESSFUL; + finish_async(ovp); + continue; + } + + if(ovp->timeout && time_before(&ovp->tv,&now)) + { + ovp->lpOverlapped->Internal=STATUS_TIMEOUT; + finish_async(ovp); + continue; + } + + fds[n].fd=ovp->fd; + fds[n].events=ovp->event; + fds[n].revents=0; + user[n] = ovp; + + if(ovp->timeout && ( (!timeout_user) || time_before(&ovp->tv,&timeout_user->tv))) + { + timeout = (ovp->tv.tv_sec - now.tv_sec) * 1000 + + (ovp->tv.tv_usec - now.tv_usec) / 1000; + timeout_user = ovp; + } + + n++; + } + + /* if there aren't any active asyncs return */ + if(n==1) + return; + + r = poll(fds, n, timeout); + + /* if there were any errors, return immediately */ + if( (r<0) || (fds[0].revents==POLLNVAL) ) + return; + + if( r==0 ) + { + timeout_user->lpOverlapped->Internal = STATUS_TIMEOUT; + finish_async(timeout_user); + continue; + } + + /* search for async operations that are ready */ + for( i=1; ifunc(user[i],fds[i].revents); + + if(user[i]->lpOverlapped->Internal!=STATUS_PENDING) + finish_async(user[i]); + } + + if(fds[0].revents == POLLIN) + return; + } +} + /*********************************************************************** * wait_reply @@ -47,7 +167,9 @@ static int wait_reply( void *cookie ) struct wake_up_reply reply; for (;;) { - int ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) ); + int ret; + if (NtCurrentTeb()->pending_list) check_async_list(); + ret = read( NtCurrentTeb()->wait_fd[0], &reply, sizeof(reply) ); if (ret == sizeof(reply)) { if (!reply.cookie) break; /* thread got killed */ @@ -105,7 +227,6 @@ static void call_apcs( BOOL alertable ) case APC_NONE: return; /* no more APCs */ case APC_ASYNC: - proc( &args[0] ); break; case APC_USER: proc( args[0] ); diff --git a/server/async.c b/server/async.c index 2d37b836084..bcb5714a61c 100644 --- a/server/async.c +++ b/server/async.c @@ -4,280 +4,31 @@ * Copyright (C) 1998 Alexandre Julliard * Copyright (C) 2000 Mike McCormack * - * TODO: - * Fix up WaitCommEvent operations. Currently only EV_RXCHAR is supported. - * This may require modifications to the linux kernel to enable select - * to wait on Modem Status Register deltas. (delta DCD, CTS, DSR or RING) - * */ #include "config.h" #include -#include -#include #include #include -#include -#ifdef HAVE_SYS_ERRNO_H -#include -#endif -#include -#include -#include -#include #include -#include -#include -#include - -#include "winerror.h" -#include "winbase.h" #include "handle.h" #include "thread.h" #include "request.h" -struct async -{ - struct object obj; - void *client_overlapped; - int type; - int result; - int count; - int eventmask; - struct async *next; - struct timeval tv; - struct timeout_user *timeout; - struct wait_queue_entry wait; - void *buffer; - void *func; - struct thread *thread; - struct object *file; -}; - -static void async_dump( struct object *obj, int verbose ); -static void async_destroy( struct object *obj ); -static int async_get_poll_events( struct object *obj ); -static int async_get_fd( struct object *obj ); -static int async_get_info( struct object *obj, struct get_file_info_request *req ); -static void async_poll_event( struct object *obj, int event ); -static void overlapped_timeout (void *private); - -static const struct object_ops async_ops = -{ - sizeof(struct async), /* size */ - async_dump, /* dump */ - default_poll_add_queue, /* add_queue */ - default_poll_remove_queue, /* remove_queue */ - default_poll_signaled, /* signaled */ - no_satisfied, /* satisfied */ - async_get_poll_events, /* get_poll_events */ - async_poll_event, /* poll_event */ - async_get_fd, /* get_fd */ - no_flush, /* flush */ - async_get_info, /* get_file_info */ - async_destroy /* destroy */ -}; - -static void async_dump( struct object *obj, int verbose ) -{ - struct async *ov = (struct async *)obj; - - assert( obj->ops == &async_ops ); - - fprintf( stderr, "async: overlapped %p %s\n", - ov->client_overlapped, ov->timeout?"with timeout":""); -} - -/* same as file_destroy, but don't delete comm ports */ -static void async_destroy( struct object *obj ) -{ - struct async *ov = (struct async *)obj; - assert( obj->ops == &async_ops ); - - if(ov->timeout) - { - remove_timeout_user(ov->timeout); - ov->timeout = NULL; - } -} - -struct async *get_async_obj( struct process *process, handle_t handle, unsigned int access ) -{ - return (struct async *)get_handle_obj( process, handle, access, &async_ops ); -} - -static int async_get_poll_events( struct object *obj ) -{ - struct async *ov = (struct async *)obj; - assert( obj->ops == &async_ops ); - - /* FIXME: this should be a function pointer */ - return serial_async_get_poll_events(ov); -} - -static int async_get_fd( struct object *obj ) -{ - struct async *async = (struct async *)obj; - assert( obj->ops == &async_ops ); - return async->obj.fd; -} - -static int async_get_info( struct object *obj, struct get_file_info_request *req ) { - assert( obj->ops == &async_ops ); - req->type = FILE_TYPE_CHAR; - req->attr = 0; - req->access_time = 0; - req->write_time = 0; - req->size_high = 0; - req->size_low = 0; - req->links = 0; - req->index_high = 0; - req->index_low = 0; - req->serial = 0; - return 1; -} - -/* data access functions */ -int async_type(struct async *ov) -{ - return ov->type; -} - -int async_count(struct async *ov) -{ - return ov->count; -} - -int async_get_eventmask(struct async *ov) -{ - return ov->eventmask; -} - -int async_set_eventmask(struct async *ov, int eventmask) -{ - return ov->eventmask = eventmask; -} DECL_HANDLER(create_async) { struct object *obj; - struct async *ov = NULL; - int fd; - req->ov_handle = 0; if (!(obj = get_handle_obj( current->process, req->file_handle, 0, NULL)) ) return; - fd = dup(obj->fd); - if(fd<0) - { - release_object(obj); - set_error(STATUS_UNSUCCESSFUL); - return; - } - - if(0>fcntl(fd, F_SETFL, O_NONBLOCK)) - { - release_object(obj); - set_error(STATUS_UNSUCCESSFUL); - return; - } - - ov = alloc_object (&async_ops, fd); - if(!ov) - { - release_object(obj); - set_error(STATUS_UNSUCCESSFUL); - return; - } - - ov->client_overlapped = req->overlapped; - ov->next = NULL; - ov->timeout = NULL; - ov->type = req->type; - ov->thread = current; - ov->func = req->func; - ov->file = obj; - ov->buffer = req->buffer; - ov->count = req->count; - ov->tv.tv_sec = 0; - ov->tv.tv_usec = 0; + /* FIXME: check if this object is allowed to do overlapped I/O */ /* FIXME: this should be a function pointer */ - serial_async_setup(obj,ov); + req->timeout = get_serial_async_timeout(obj,req->type,req->count); - if( ov->tv.tv_sec || ov->tv.tv_usec ) - { - ov->timeout = add_timeout_user(&ov->tv, overlapped_timeout, ov); - } - - ov->obj.ops->add_queue(&ov->obj,&ov->wait); - - req->ov_handle = alloc_handle( current->process, ov, GENERIC_READ|GENERIC_WRITE, 0 ); - - release_object(ov); release_object(obj); } - -/* handler for async poll() events */ -static void async_poll_event( struct object *obj, int event ) -{ - struct async *ov = (struct async *) obj; - - /* queue an APC in the client thread to do our dirty work */ - ov->obj.ops->remove_queue(&ov->obj,&ov->wait); - if(ov->timeout) - { - remove_timeout_user(ov->timeout); - ov->timeout = NULL; - } - - /* FIXME: this should be a function pointer */ - event = serial_async_poll_event(obj,event); - - thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 1, 3, - ov->client_overlapped, ov->buffer, event); -} - -/* handler for async i/o timeouts */ -static void overlapped_timeout (void *private) -{ - struct async *ov = (struct async *) private; - - ov->obj.ops->remove_queue(&ov->obj,&ov->wait); - ov->timeout = NULL; - - thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 1, 3, - ov->client_overlapped,ov->buffer, 0); -} - -void async_add_timeout(struct async *ov, int timeout) -{ - if(timeout) - { - gettimeofday(&ov->tv,0); - add_timeout(&ov->tv,timeout); - } -} - -DECL_HANDLER(async_result) -{ - struct async *ov; - - if ((ov = get_async_obj( current->process, req->ov_handle, 0 ))) - { - ov->result = req->result; - if(ov->result == STATUS_PENDING) - { - ov->obj.ops->add_queue(&ov->obj,&ov->wait); - if( (ov->tv.tv_sec || ov->tv.tv_usec) && !ov->timeout) - { - ov->timeout = add_timeout_user(&ov->tv, overlapped_timeout, ov); - } - } - release_object( ov ); - } -} - diff --git a/server/object.h b/server/object.h index 089ab5bcca2..2b3a02a921a 100644 --- a/server/object.h +++ b/server/object.h @@ -153,19 +153,9 @@ extern int create_anonymous_file(void); extern struct file *create_temp_file( int access ); extern void file_set_error(void); -/* async functions */ - -void async_add_timeout(struct async *ov, int timeout); -int async_count(struct async *ov); -int async_type(struct async *ov); -int async_get_eventmask(struct async *ov); -int async_set_eventmask(struct async *ov, int eventmask); - /* serial functions */ -int serial_async_setup(struct object *obj, struct async *ov); -int serial_async_get_poll_events( struct async *ov ); -int serial_async_poll_event(struct object *obj, int event); +int get_serial_async_timeout(struct object *obj, int type, int count); /* console functions */ diff --git a/server/request.h b/server/request.h index a24d04b8548..43ac0ae08e5 100644 --- a/server/request.h +++ b/server/request.h @@ -176,7 +176,6 @@ DECL_HANDLER(create_serial); DECL_HANDLER(get_serial_info); DECL_HANDLER(set_serial_info); DECL_HANDLER(create_async); -DECL_HANDLER(async_result); #ifdef WANT_REQUEST_HANDLERS @@ -291,7 +290,6 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_get_serial_info, (req_handler)req_set_serial_info, (req_handler)req_create_async, - (req_handler)req_async_result, }; #endif /* WANT_REQUEST_HANDLERS */ diff --git a/server/serial.c b/server/serial.c index 953be977c3b..738bcbc8bdd 100644 --- a/server/serial.c +++ b/server/serial.c @@ -173,76 +173,25 @@ static int serial_get_info( struct object *obj, struct get_file_info_request *re return 1; } -/* these functions are for interaction with asynchronous i/o objects */ -int serial_async_setup(struct object *obj, struct async *ov) +/* these function calculates the timeout for an async operation + on a serial port */ +int get_serial_async_timeout(struct object *obj, int type, int count) { struct serial *serial = (struct serial *)obj; - int timeout; if(obj->ops != &serial_ops) return 0; - switch(async_type(ov)) + switch(type) { case ASYNC_TYPE_READ: - timeout = serial->readconst + serial->readmult*async_count(ov); - async_add_timeout(ov, timeout); - async_set_eventmask(ov,EV_RXCHAR); - break; + return serial->readconst + serial->readmult*count; case ASYNC_TYPE_WRITE: - timeout = serial->writeconst + serial->writemult*async_count(ov); - async_add_timeout(ov, timeout); - async_set_eventmask(ov,EV_TXEMPTY); - break; - case ASYNC_TYPE_WAIT: - async_set_eventmask(ov,serial->eventmask); - break; + return serial->writeconst + serial->writemult*count; } - - return 1; + return 0; } -int serial_async_get_poll_events( struct async *ov ) -{ - int events=0,mask; - - switch(async_type(ov)) - { - case ASYNC_TYPE_READ: - events |= POLLIN; - break; - case ASYNC_TYPE_WRITE: - events |= POLLOUT; - break; - case ASYNC_TYPE_WAIT: - /* - * FIXME: here is the spot to implement other WaitCommEvent flags - */ - mask = async_get_eventmask(ov); - if(mask&EV_RXCHAR) - events |= POLLIN; - /* if(mask&EV_TXEMPTY) - events |= POLLOUT; */ - break; - } - return events; -} - -/* receive a select event, and output a windows event */ -int serial_async_poll_event(struct object *obj, int event) -{ - int r=0; - - /* - * FIXME: here is the spot to implement other WaitCommEvent flags - */ - if(event & POLLIN) - r |= EV_RXCHAR; - if(event & POLLOUT) - r |= EV_TXEMPTY; - - return r; -} /* create a serial */ DECL_HANDLER(create_serial) diff --git a/server/trace.c b/server/trace.c index 367c2580e1e..f1648ccc1fc 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1477,22 +1477,13 @@ static void dump_set_serial_info_request( const struct set_serial_info_request * static void dump_create_async_request( const struct create_async_request *req ) { fprintf( stderr, " file_handle=%d,", req->file_handle ); - fprintf( stderr, " overlapped=%p,", req->overlapped ); - fprintf( stderr, " buffer=%p,", req->buffer ); fprintf( stderr, " count=%d,", req->count ); - fprintf( stderr, " func=%p,", req->func ); fprintf( stderr, " type=%d", req->type ); } static void dump_create_async_reply( const struct create_async_request *req ) { - fprintf( stderr, " ov_handle=%d", req->ov_handle ); -} - -static void dump_async_result_request( const struct async_result_request *req ) -{ - fprintf( stderr, " ov_handle=%d,", req->ov_handle ); - fprintf( stderr, " result=%d", req->result ); + fprintf( stderr, " timeout=%d", req->timeout ); } static const dump_func req_dumpers[REQ_NB_REQUESTS] = { @@ -1604,7 +1595,6 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_serial_info_request, (dump_func)dump_set_serial_info_request, (dump_func)dump_create_async_request, - (dump_func)dump_async_result_request, }; static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { @@ -1716,7 +1706,6 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_get_serial_info_reply, (dump_func)0, (dump_func)dump_create_async_reply, - (dump_func)0, }; static const char * const req_names[REQ_NB_REQUESTS] = { @@ -1828,7 +1817,6 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "get_serial_info", "set_serial_info", "create_async", - "async_result", }; /* ### make_requests end ### */