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 | 172 +++++++++++++++++----------- files/file.c | 252 +++++++++++++++++++++++------------------ include/file.h | 17 +++ include/server.h | 21 +--- include/thread.h | 3 +- scheduler/synchro.c | 125 ++++++++++++++++++++- server/async.c | 317 ++++++---------------------------------------------- server/object.h | 12 +- server/request.h | 2 - server/serial.c | 65 ++--------- server/trace.c | 14 +-- 11 files changed, 436 insertions(+), 564 deletions(-) rewrite server/async.c (92%) 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,117 +2815,152 @@ 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); } + + /*********************************************************************** - * WaitCommEvent (KERNEL32.719) - * - * Wait until something interesting happens on a COMM port. - * Interesting things (events) are set by calling SetCommMask before - * this function is called. - * - * RETURNS: - * TRUE if successful - * FALSE if failure - * - * The set of detected events will be written to *lpdwEventMask - * ERROR_IO_PENDING will be returned the overlapped structure was passed + * COMM_WaitCommEvent (INTERNAL) * - * BUGS: - * Only supports EV_RXCHAR and EV_TXEMPTY + * This function must have an lpOverlapped. */ -BOOL WINAPI WaitCommEvent( +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 */ { - OVERLAPPED ov; - LPOVERLAPPED lpov; - int ret; + int fd,ret; + async_private *ovp; - TRACE("(%x %p %p )\n",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; - - /* check that the overlapped structure has a valid event flag */ - if ( (lpov->hEvent==0) || (lpov->hEvent == INVALID_HANDLE_VALUE) ) - { - ERR("Couldn't create Event flag for Overlapped structure\n"); SetLastError(ERROR_INVALID_PARAMETER); return FALSE; } - ResetEvent(lpov->hEvent); + if(NtResetEvent(lpOverlapped->hEvent,NULL)) + return FALSE; - lpov->Internal = STATUS_PENDING; - lpov->InternalHigh = 0; - lpov->Offset = 0; - lpov->OffsetHigh = 0; + 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->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 (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) { - if(!lpOverlapped) - CloseHandle(lpov->hEvent); - TRACE("server call failed.\n"); + 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; +} - /* activate the overlapped operation */ - lpov->Internal = STATUS_PENDING; +/*********************************************************************** + * WaitCommEvent (KERNEL32.719) + * + * Wait until something interesting happens on a COMM port. + * Interesting things (events) are set by calling SetCommMask before + * this function is called. + * + * RETURNS: + * TRUE if successful + * FALSE if failure + * + * The set of detected events will be written to *lpdwEventMask + * ERROR_IO_PENDING will be returned the overlapped structure was passed + * + * BUGS: + * Only supports EV_RXCHAR and EV_TXEMPTY + */ +BOOL WINAPI 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 */ +{ + OVERLAPPED ov; + int ret; - /* 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 + 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 */ + ov.hEvent = CreateEventA(NULL,FALSE,FALSE,NULL); + + COMM_WaitCommEvent(hFile, lpdwEvents, &ov); + + if(GetLastError()!=STATUS_PENDING) { - /* caller wants overlapped I/O using GetOverlapped result */ - SetLastError(ERROR_IO_PENDING); + CloseHandle(ov.hEvent); return FALSE; } - return TRUE; -} + /* wait for the overlapped to complete */ + ret = GetOverlappedResult(hFile, &ov, NULL, TRUE); + CloseHandle(ov.hEvent); + 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 there are no events, it must be a timeout */ - if(events==0) + /* if POLLNVAL, then our fd was closed or we have the wrong fd */ + if(events&POLLNVAL) { - TRACE("read timed out\n"); - /* r = STATUS_TIMEOUT; */ - r = STATUS_SUCCESS; + ERR("fd %d invalid for %p\n",ovp->fd,ovp); + r = STATUS_UNSUCCESSFUL; goto async_end; } - fd = FILE_GetUnixHandle(lpOverlapped->Offset, GENERIC_READ); - if(fd<0) + /* if there are no events, it must be a timeout */ + if(events==0) { - TRACE("FILE_GetUnixHandle(%ld) failed \n",lpOverlapped->Offset); - r = STATUS_UNSUCCESSFUL; + TRACE("read timed out\n"); + 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 (ret) + { + TRACE("server call failed\n"); + return FALSE; + } + + fd = FILE_GetUnixHandle( hFile, GENERIC_WRITE ); + if(fd<0) + { + TRACE("Couldn't get FD\n"); + return FALSE; + } - if(!r) + ovp = (async_private *) HeapAlloc(GetProcessHeap(), 0, sizeof (async_private)); + if(!ovp) { - TRACE("ov=%ld IO is pending!!!\n",overlapped->Offset); - SetLastError(ERROR_IO_PENDING); + 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; - 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; } + /*********************************************************************** * 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,38 +1400,39 @@ 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 there are no events, it must be a timeout */ - if(events==0) + /* if POLLNVAL, then our fd was closed or we have the wrong fd */ + if(events&POLLNVAL) { - TRACE("write timed out\n"); - r = STATUS_TIMEOUT; + ERR("fd %d invalid for %p\n",ovp->fd,ovp); + r = STATUS_UNSUCCESSFUL; goto async_end; } - fd = FILE_GetUnixHandle(lpOverlapped->Offset, GENERIC_WRITE); - if(fd<0) + /* if there are no events, it must be a timeout */ + if(events==0) { - ERR("FILE_GetUnixHandle(%ld) failed \n",lpOverlapped->Offset); - r = STATUS_UNSUCCESSFUL; + TRACE("write timed out\n"); + r = STATUS_TIMEOUT; 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) - { - SetLastError(ERROR_IO_PENDING); + /* 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) + { + 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 dissimilarity index 92% index 2d37b836084..bcb5714a61c 100644 --- a/server/async.c +++ b/server/async.c @@ -1,283 +1,34 @@ -/* - * Server-side support for async i/o operations - * - * 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: this should be a function pointer */ - serial_async_setup(obj,ov); - - 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 ); - } -} - +/* + * Server-side support for async i/o operations + * + * Copyright (C) 1998 Alexandre Julliard + * Copyright (C) 2000 Mike McCormack + * + */ + +#include "config.h" + +#include +#include +#include +#include + +#include "handle.h" +#include "thread.h" +#include "request.h" + + +DECL_HANDLER(create_async) +{ + struct object *obj; + + if (!(obj = get_handle_obj( current->process, req->file_handle, 0, NULL)) ) + return; + + /* FIXME: check if this object is allowed to do overlapped I/O */ + + /* FIXME: this should be a function pointer */ + req->timeout = get_serial_async_timeout(obj,req->type,req->count); + + release_object(obj); +} 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 ### */ -- 2.11.4.GIT