From 1eac1911c7fcf3c0719ed2cffdce66d90cf51c1a Mon Sep 17 00:00:00 2001 From: Mike McCormack Date: Mon, 13 Nov 2000 19:27:21 +0000 Subject: [PATCH] - created server object for handling async i/o - implemented WaitCommEvent with the EV_RXCHAR flag - implemented GetOverlappedResult --- dlls/kernel/comm.c | 104 ++++++++++++++++- include/server.h | 34 +++++- scheduler/synchro.c | 3 + server/Makefile.in | 1 + server/async.c | 270 ++++++++++++++++++++++++++++++++++++++++++++ server/object.h | 15 +++ server/request.h | 4 + server/serial.c | 71 ++++++++++++ server/trace.c | 27 +++++ 9 files changed, 524 insertions(+), 5 deletions(-) create mode 100644 server/async.c diff --git a/dlls/kernel/comm.c b/dlls/kernel/comm.c index c6da61cd8bd..b100e1d9fb8 100644 --- a/dlls/kernel/comm.c +++ b/dlls/kernel/comm.c @@ -2580,13 +2580,111 @@ BOOL WINAPI GetCommModemStatus(HANDLE hFile,LPDWORD lpModemStat ) return FALSE; #endif } + +VOID COMM_WaitCommEventService(void **args) +{ + LPOVERLAPPED lpOverlapped = (LPOVERLAPPED)args[0]; + LPDWORD buffer = (LPDWORD)args[1]; + DWORD events = (DWORD)args[2]; + + TRACE("overlapped %p wait complete %p <- %lx\n",lpOverlapped,buffer,events); + if(buffer) + *buffer = events; + + SetEvent( lpOverlapped->hEvent); +} + /*********************************************************************** * 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,LPDWORD eventmask ,LPOVERLAPPED overlapped) +BOOL WINAPI WaitCommEvent( + HANDLE hFile, /* [I] handle of comm port to wait for */ + LPDWORD lpdwEvents, /* [O] event(s) that were detected */ + LPOVERLAPPED lpOverlapped /* [I/O] for Asynchronous waiting */ +) { + OVERLAPPED ov; + LPOVERLAPPED lpov; + int ret; + + 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) ) { - FIXME("(%d %p %p )\n",hFile, eventmask,overlapped); - return TRUE; + ERR("Couldn't create Event flag for Overlapped structure\n"); + SetLastError(ERROR_INVALID_PARAMETER); + return FALSE; + } + + lpov->Internal = 0; + lpov->InternalHigh = hFile; + lpov->Offset = 0; + lpov->OffsetHigh = 0; + + /* start an ASYNCHRONOUS WaitCommEvent */ + SERVER_START_REQ + { + struct create_async_request *req = server_alloc_req( sizeof(*req), 0 ); + + 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( REQ_CREATE_ASYNC ); + + lpov->Internal = req->ov_handle; + } + SERVER_END_REQ; + + if(ret) + { + if(!lpOverlapped) + CloseHandle(lpov->hEvent); + TRACE("server call failed.\n"); + return FALSE; + } + + /* 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; } /*********************************************************************** diff --git a/include/server.h b/include/server.h index c6c7d5778bf..c069f4371c3 100644 --- a/include/server.h +++ b/include/server.h @@ -356,7 +356,7 @@ struct get_apc_request OUT int type; /* function type */ OUT VARARG(args,ptrs); /* function arguments */ }; -enum apc_type { APC_NONE, APC_USER, APC_TIMER }; +enum apc_type { APC_NONE, APC_USER, APC_TIMER, APC_ASYNC }; /* Close a handle for the current process */ @@ -1332,6 +1332,32 @@ struct set_serial_info_request #define SERIALINFO_SET_MASK 0x02 #define SERIALINFO_SET_ERROR 0x04 +struct create_async_request +{ + REQUEST_HEADER; /* request header */ + IN int file_handle; /* handle to comm port */ + IN void* overlapped; + IN void* buffer; + IN int count; + IN void* func; + IN int type; + OUT int ov_handle; +}; +#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 int ov_handle; + IN int result; /* NT status code */ +}; + /* Everything below this line is generated automatically by tools/make_requests */ /* ### make_requests begin ### */ @@ -1445,6 +1471,8 @@ enum request REQ_CREATE_SERIAL, REQ_GET_SERIAL_INFO, REQ_SET_SERIAL_INFO, + REQ_CREATE_ASYNC, + REQ_ASYNC_RESULT, REQ_NB_REQUESTS }; @@ -1560,9 +1588,11 @@ union generic_request struct create_serial_request create_serial; 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 26 +#define SERVER_PROTOCOL_VERSION 27 /* ### make_requests end ### */ /* Everything above this line is generated automatically by tools/make_requests */ diff --git a/scheduler/synchro.c b/scheduler/synchro.c index 52f112b75b3..f5d447001e2 100644 --- a/scheduler/synchro.c +++ b/scheduler/synchro.c @@ -46,6 +46,9 @@ static void call_apcs(void) { case APC_NONE: return; /* no more APCs */ + case APC_ASYNC: + proc( &args[0] ); + break; case APC_USER: proc( args[0] ); break; diff --git a/server/Makefile.in b/server/Makefile.in index a7779f39219..6f1c36a9aeb 100644 --- a/server/Makefile.in +++ b/server/Makefile.in @@ -6,6 +6,7 @@ VPATH = @srcdir@ MODULE = none C_SRCS = \ + async.c \ atom.c \ change.c \ console.c \ diff --git a/server/async.c b/server/async.c new file mode 100644 index 00000000000..379d130f95b --- /dev/null +++ b/server/async.c @@ -0,0 +1,270 @@ +/* + * 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 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_read_fd( struct object *obj ); +static int async_get_write_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 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_read_fd, /* get_read_fd */ + async_get_write_fd, /* get_write_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, int 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_read_fd( struct object *obj ) +{ + struct async *async = (struct async *)obj; + assert( obj->ops == &async_ops ); + return dup( async->obj.fd ); +} + +static int async_get_write_fd( struct object *obj ) +{ + struct async *async = (struct async *)obj; + assert( obj->ops == &async_ops ); + return dup( 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 = -1; + 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; + + /* FIXME: this should be a function pointer */ + serial_async_setup(obj,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(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); + + /* FIXME: this should be a function pointer */ + event = serial_async_poll_event(obj,event); + + thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 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); + + thread_queue_apc(ov->thread, NULL, ov->func, APC_ASYNC, 3, + ov->client_overlapped,ov->buffer, 0); +} + +void async_add_timeout(struct async *ov, int timeout) +{ + struct timeval tv; + + gettimeofday(&tv,0); + add_timeout(&tv,timeout); + ov->timeout = add_timeout_user(&tv, overlapped_timeout, ov); +} + +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); + } + release_object( ov ); + } +} + diff --git a/server/object.h b/server/object.h index ab52ed6a461..746b7f6641b 100644 --- a/server/object.h +++ b/server/object.h @@ -25,6 +25,7 @@ struct thread; struct process; struct file; struct wait_queue_entry; +struct async; /* operations valid on all objects */ struct object_ops @@ -156,6 +157,20 @@ 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); + /* console functions */ extern int alloc_console( struct process *process ); diff --git a/server/request.h b/server/request.h index ac80b7bbcc7..14dc2e3bafe 100644 --- a/server/request.h +++ b/server/request.h @@ -202,6 +202,8 @@ DECL_HANDLER(wait_input_idle); 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 @@ -316,6 +318,8 @@ static const req_handler req_handlers[REQ_NB_REQUESTS] = (req_handler)req_create_serial, (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 d102233670a..c549becd41b 100644 --- a/server/serial.c +++ b/server/serial.c @@ -182,6 +182,77 @@ 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) +{ + struct serial *serial = (struct serial *)obj; + int timeout; + + if(obj->ops != &serial_ops) + return 0; + + switch(async_type(ov)) + { + case ASYNC_TYPE_READ: + timeout = serial->readconst + serial->readmult*async_count(ov); + async_add_timeout(ov, timeout); + async_set_eventmask(ov,EV_RXCHAR); + break; + 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 1; +} + +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 4ac36ead89e..c5ff2eae84d 100644 --- a/server/trace.c +++ b/server/trace.c @@ -1455,6 +1455,27 @@ static void dump_set_serial_info_request( const struct set_serial_info_request * fprintf( stderr, " commerror=%08x", req->commerror ); } +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 ); +} + static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_new_process_request, (dump_func)dump_wait_process_request, @@ -1564,6 +1585,8 @@ static const dump_func req_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_create_serial_request, (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] = { @@ -1675,6 +1698,8 @@ static const dump_func reply_dumpers[REQ_NB_REQUESTS] = { (dump_func)dump_create_serial_reply, (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] = { @@ -1786,6 +1811,8 @@ static const char * const req_names[REQ_NB_REQUESTS] = { "create_serial", "get_serial_info", "set_serial_info", + "create_async", + "async_result", }; /* ### make_requests end ### */