/* * 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 ); } }