105 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			105 lines
		
	
	
		
			3.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*	asyn_special(), asyn_result()			Author: Kees J. Bot
 | |
|  *								8 Jul 1997
 | |
|  */
 | |
| #include "asyn.h"
 | |
| #include <signal.h>
 | |
| 
 | |
| /* Saved signal mask between asyn_special() and asyn_result(). */
 | |
| static sigset_t mask;
 | |
| 
 | |
| int asyn_special(asynchio_t *asyn, int fd, int op)
 | |
| /* Wait for an operation.  This is an odd one out compared to asyn_read()
 | |
|  * and asyn_write().  It does not do an operation itself, but together with
 | |
|  * asyn_result() it is a set of brackets around a system call xxx that has
 | |
|  * no asyn_xxx() for itself.  It can be used to build an asyn_accept() or
 | |
|  * asyn_connect() for instance.  (Minix-vmd has asyn_ioctl() instead,
 | |
|  * which is used for any other event like TCP/IP listen/connect.  BSD has
 | |
|  * a myriad of calls that can't be given an asyn_xxx() counterpart each.)
 | |
|  * Asyn_special() returns -1 for "forget it", 0 for "try it", and 1 for
 | |
|  * "very first call, maybe you should try it once, maybe not".  Errno is
 | |
|  * set to EAGAIN if the result is -1 or 1.  After trying the system call
 | |
|  * make sure errno equals EAGAIN if the call is still in progress and call
 | |
|  * asyn_result with the result of the system call.  Asyn_result() must be
 | |
|  * called if asyn_special() returns 0 or 1.
 | |
|  *
 | |
|  * Example use:
 | |
|  *
 | |
|  * int asyn_accept(asynchio_t *asyn, int s, struct sockaddr *addr, int *addrlen)
 | |
|  * {
 | |
|  *     int r;
 | |
|  *     if ((r= asyn_special(asyn, fd, SEL_READ)) < 0) return -1;
 | |
|  *     r= r == 0 ? accept(fd, addr, addrlen) : -1;
 | |
|  *     return asyn_result(asyn, fd, SEL_READ, r);
 | |
|  * }
 | |
|  *
 | |
|  * int asyn_connect(asynchio_t *asyn, int s, struct sockaddr *name, int namelen)
 | |
|  * {
 | |
|  *     int r;
 | |
|  *     if ((r= asyn_special(asyn, fd, SEL_WRITE)) < 0) return -1;
 | |
|  *     if (r == 1 && (r= connect(fd, name, namelen)) < 0) {
 | |
|  *         if (errno == EINPROGRESS) errno= EAGAIN;
 | |
|  *     }
 | |
|  *     return asyn_result(asyn, fd, SEL_WRITE, r);
 | |
|  * }
 | |
|  */
 | |
| {
 | |
| 	asynfd_t *afd;
 | |
| 	int seen;
 | |
| 
 | |
| 	asyn->asyn_more++;
 | |
| 
 | |
| 	if ((unsigned) fd >= FD_SETSIZE) { errno= EBADF; return -1; }
 | |
| 	afd= &asyn->asyn_afd[fd];
 | |
| 
 | |
| 	/* If this is the first async call on this filedescriptor then
 | |
| 	 * remember its file flags.
 | |
| 	 */
 | |
| 	if (!(seen= afd->afd_seen)) {
 | |
| 		if ((afd->afd_flags= fcntl(fd, F_GETFL)) < 0) return -1;
 | |
| 		afd->afd_seen= 1;
 | |
| 	}
 | |
| 
 | |
| 	/* Try to read if I/O is pending. */
 | |
| 	if (!seen || afd->afd_state[op] == PENDING) {
 | |
| 		sigemptyset(&mask);
 | |
| 		if (sigprocmask(SIG_SETMASK, &mask, &mask) < 0) return -1;
 | |
| 		(void) fcntl(fd, F_SETFL, afd->afd_flags | O_NONBLOCK);
 | |
| 
 | |
| 		/* Let the caller try the system call. */
 | |
| 		errno= EAGAIN;
 | |
| 		return seen ? 0 : 1;
 | |
| 	}
 | |
| 
 | |
| 	/* Record this read as "waiting". */
 | |
| 	afd->afd_state[op]= WAITING;
 | |
| 	FD_SET(fd, &asyn->asyn_fdset[op]);
 | |
| 	errno= EAGAIN;
 | |
| 	asyn->asyn_more--;
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| int asyn_result(asynchio_t *asyn, int fd, int op, int result)
 | |
| /* The caller has tried the system call with the given result.  Finish up. */
 | |
| {
 | |
| 	int err;
 | |
| 	asynfd_t *afd= &asyn->asyn_afd[fd];
 | |
| 
 | |
| 	err= errno;
 | |
| 
 | |
| 	(void) fcntl(fd, F_SETFL, afd->afd_flags);
 | |
| 	(void) sigprocmask(SIG_SETMASK, &mask, nil);
 | |
| 
 | |
| 	errno= err;
 | |
| 	if (result != -1 || errno != EAGAIN) {
 | |
| 		afd->afd_state[op]= IDLE;
 | |
| 		return result;
 | |
| 	}
 | |
| 
 | |
| 	/* Record this operation as "waiting". */
 | |
| 	afd->afd_state[op]= WAITING;
 | |
| 	FD_SET(fd, &asyn->asyn_fdset[op]);
 | |
| 	errno= EAGAIN;
 | |
| 	asyn->asyn_more--;
 | |
| 	return -1;
 | |
| }
 | 
