| /* | |
| * 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 <assert.h> | |
| #include <fcntl.h> | |
| #include <stdio.h> | |
| #include <string.h> | |
| #include <stdlib.h> | |
| #include <errno.h> | |
| #ifdef HAVE_SYS_ERRNO_H | |
| #include <sys/errno.h> | |
| #endif | |
| #include <sys/stat.h> | |
| #include <sys/time.h> | |
| #include <sys/types.h> | |
| #include <time.h> | |
| #include <unistd.h> | |
| #include <utime.h> | |
| #include <termios.h> | |
| #include <sys/ioctl.h> | |
| #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 ); | |
| } | |
| } | |