Skip site navigation (1)Skip section navigation (2)
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>