Date: Tue, 27 May 1997 10:06:49 -0700 (MST) From: Terry Lambert <terry@lambert.org> To: cmsedore@mailbox.syr.edu (Christopher Sedore) Cc: FreeBSD-Hackers@FreeBSD.ORG Subject: Re: async socket stuff Message-ID: <199705271706.KAA15497@phaeton.artisoft.com> In-Reply-To: <Pine.SOL.3.95.970527111554.10830E-100000@rodan.syr.edu> from "Christopher Sedore" at May 27, 97 11:24:22 am
next in thread | previous in thread | raw e-mail | index | archive | help
> I've been thinking about implementing some async socket handling code and > am wondering if someone else is doing this or has done it. I've been > thinking about two different approaches: > > 1. Creating a new ioctl set and a few syscalls to allow you to > associate a socket with something like an NT I/O completion port. This > would allow you to associate a socket descriptor with a queue and each > time the socket's status changed (via sorwakeup or sowwakeup) I'd post an > entry into the queue. The idea is that rather than using select() and > then searching through a list of descriptors, you could just read them off > one (or many) at a time and do I/O on the appropriate descriptors. This > seems to me to be more efficient than select(). > > 2. Implementing general async I/O for sockets. Then, rather than (or > perhaps in addition to) the above functionality, you could use the queue > to hold results of async operations. > > I've also thought of adding a call like NT's TransmitFile() (single call > file transfer). > > Comments? Suggestions? Criticisms? The need for async system calls is a general problem for all system calls which potentially block. It is also an issue for multithreading, which the POSIX threading implemenation uses non-blocking I/O to overcome. Unfortunately, this is not scalable to calls not involving fd's. I suggest an alternate approach which will keep this issue from coming up again, and may in fact be easier to implement. The system call entry into the kernel is defined in the file: /usr/src/libc/i386/SYS.h I suggest the use of an alternate call gate/trap gate (depending on whether or not you're doing it for ELF), and the following additional call: SYS_async ARG0 = WAIT CANCEL Finally, you will need to modify /usr/include/sys/sysent.h: struct sysent { /* system call table */ int sy_narg; /* number of arguments */ + int sy_flags; /* flags for this call*/ sy_call_t *sy_call; /* implementing function */ }; + +#define S_F_CANBLOCK 0x00000001 /* call may block*/ +#define S_F_WILLBLOCK 0x00000002 /* call will block*/ +#define S_F_MUSTBLOCK 0x00000004 /* call must block (ie: async(2))*/ +#define S_F_CANABORT 0x00010000 /* signal may abort call*/ You will also need to define an async operation context record; this record is allocated by the user and passed to all calls through the async call gate. When calling through the gate, it's basically: int async_open( aio_result_t **st, const char *path, int flags, mode_t mode) instead of: int open( const char *path, int flags, mode_t mode) SYS_async with the "WAIT" argument is wrapped as follows: #define async_wait( timeout) async( WAIT, timeout) aio_result_t * async_wait( const struct timeval *timeout) SYS_async with the "CANCEL" argument is wrapped as follows: #define async_cancel( st) async( WAIT, st) int async_cancel( aio_result_t *st) In addition, the "read" and "write" system calls are extended to take fourth and fifth arguments, an offset_t. This is because for async completion, there is no guarantee that the file pointer reference is going to maintain a predictable offset. Thus: ssize_t async_read( aio_result_t *st, int d, void *buf, size_t nbytes, off_t offset, int whence) ssize_t async_write( aio_result_t *st, int d, const void *buf, size_t nbytes, off_t offset, int whence) And aio_result_t: typedef struct aio_result_t { union { int ret_i; quad_t ret_q; } aio_ret; int aio_errno; }; Finally, the return of all async_* calls through the call gate is defined as "error indicator" for the call (ie: -1 for read or write) with the newly defined return value "EASYNC" to indicate that the operation has been asynchronously queued for completion. This lets programs that make the async calls detect immediate completion for operations that do not block (like async_getpid(), etc.) and operations which are capable of blocking, but didn't happen to (S_F_CANBLOCK flag; examples are a read or write, where the requested/written pages are in cache and the requested destination/source address in the user address space is in core, etc.). Such a call gate would be applicable to all system calls, including sockets (I assume you want async completion on socket calls to interleave DNS operations 8-)). Regards, Terry Lambert terry@lambert.org --- Any opinions in this posting are my own and not those of my present or previous employers.
Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?199705271706.KAA15497>