[comp.os.misc] Asynchronous I/O

raveling@vaxb.isi.edu (Paul Raveling) (01/19/89)

In article <2766@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
>
>Perhaps a standard set of real-time calls can be decided on. A set more
>suited to a wider variety of operating systems. Something like:
>
>	token = start_read(fd, buffer, nbytes, timeout);
>	...
>	nread = check_io(token);
>	...
>	nread = wait_io(token);

	Here's a C rendition of what we did in several systems
	starting in 1972 that directly addresses this; I'd still
	recommend something like:


	#define	ANYEVENT	   0
	#define	READ_EVENT_ID	   xxx	/* integer values to uniquely	  */
	#define	TIMEOUT_EVENT_ID   xxx	/*	identify events		  */
	struct event_data {...} evdata;	/*  Generic event data structure  */
	...
	...
	ARead (file, &buffer, nbytes, READ_EVENT_ID);
	SetTimer (timeout, TIMEOUT_EVENT_ID);
	...
	...
	evdata = WaitFor (ANYEVENT);
	/*
	    Or if you prefer,	WaitFor (READ_EVENT_ID)
	    or			WaitFor (READ_EVENT_ID "|" TIMEOUT_EVENT_ID)
	    or			CheckFor (whatever)  [don't block]
	*/

	switch ( evdata.event_id )
		{
		case READ_EVENT_ID:
			completion_status = evdata.status;
			byte_count 	  = evdata.byte_count;
			...
		case TIMEOUT_EVENT_ID:
			...
		}
	


	Communicating event data was the basis for scheduling processes
	in these systems; this proved to be an extremely general mechanism
	that made it easy to program a LARGE variety of functions using
	cooperating processes.  It was also quite fast:  In communicating
	small amounts of info, such as i/o request or i/o completion
	parameters, and doing the consequent context switch, the 1975
	system (EPOS) was about 10-12 times faster than UNIX V6.


---------------------
Paul Raveling
Raveling@vaxb.isi.edu

peter@ficc.uu.net (Peter da Silva) (01/24/89)

In article <7304@venera.isi.edu>, raveling@vaxb.isi.edu (Paul Raveling) writes:
> In article <2766@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
> >Perhaps a standard set of real-time calls can be decided on. A set more
> >suited to a wider variety of operating systems. Something like:

> >	token = start_read(fd, buffer, nbytes, timeout);
...
> >	nread = check_io(token);
...
> >	nread = wait_io(token);

> 	Here's a C rendition of what we did in several systems
> 	starting in 1972 that directly addresses this; I'd still
> 	recommend something like:

> 	ARead (file, &buffer, nbytes, READ_EVENT_ID);
> 	SetTimer (timeout, TIMEOUT_EVENT_ID);

I like it too. Silly me. Of course you want to do it this way.

One thing, though... passing structures around isn't so cool. And some way
of allocating an event flag dynamically (so you can use this stuff in libs)
is useful. In AmigaDOS you call AllocSignal(MASK) to do this, but it's more
UNIXy to use a file descriptor, or habe the call return the event flag...

	revent = aread(fd, buffer, nbytes);
	tevent = asleep(timeout);

	mask = await(revent|tevent);
	if(!(mask & revent))
		akill(revent);
	akill(tevent);
	nread = astatus(revent);

And, what's more, you have the kernel of a better error handling setup
than perror...
-- 
Peter da Silva, Xenix Support, Ferranti International Controls Corporation.
Work: uunet.uu.net!ficc!peter, peter@ficc.uu.net, +1 713 274 5180.   `-_-'
Home: bigtex!texbell!sugar!peter, peter@sugar.uu.net.                 'U`
Opinions may not represent the policies of FICC or the Xenix Support group.

raveling@vaxb.isi.edu (Paul Raveling) (02/01/89)

In article <2851@ficc.uu.net> peter@ficc.uu.net (Peter da Silva) writes:
>
>One thing, though... passing structures around isn't so cool.

	I agree within this context (oops, I already edited that part of
	the message out), though I have actually done this on occasion.
	What I'd REALLY like is a language feature for multiple
	return values... it might look something like:

	(completion_status, bytes_actually_read, updated_buffer_pointer) =
		Read (file, buffer, nbytes);
			** or **
		WaitFor (READ_EVENT_ID /* for prior ARead */);

	In fact, that example is what we did in EPOS for block input,
	where we roughly mimiced TENEX's SIN (string input) function.
	The catch was that we did it in assembly language.

	It turns out that sensible machine designers provided a great
	feature to help with this sort of thing -- a register set.
	On a decent machine there are at least as many registers as
	there are parameters for functions such as this, and returned
	info is an even easier fit.  Using the registers is fast,
	no memory allocation is needed for parameters in registers,
	and the hardware supplies stack operations to save and restore
	them on opposite sides of context switches.

	With multiple return values it's easier to avoid overloaded
	return values.  With a separate status code and count of
	bytes read, there's no need for special cases such as byte
	counts or "character" values of -1 or 0.  In EPOS virtually
	ALL system calls issued a status (return) code that the caller
	could feed to a function something like perror; 0 was the
	universal code for "no error".


	It's possible to do this sort of thing in a fairly C-like
	language.  BLISS-11 even did a good job of handling parameters
	in registers.  Seems like it would be both fun and valuable
	to fuse some of these notions for languages and system interfaces
	into a coherent scheme.


---------------------
Paul Raveling
Raveling@vaxb.isi.edu