178 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			178 lines
		
	
	
		
			4.6 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| #define _SYSTEM		1
 | |
| 
 | |
| #include <minix/config.h>
 | |
| #include <assert.h> 
 | |
| #include <sys/types.h>
 | |
| #include <minix/const.h>
 | |
| #include <minix/type.h>
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <unistd.h>
 | |
| #include <minix/syslib.h>
 | |
| #include <minix/sysutil.h>
 | |
| #include <minix/sys_config.h>
 | |
| 
 | |
| #include <limits.h>
 | |
| #include <errno.h>
 | |
| 
 | |
| #define ASYN_NR	(2*_NR_PROCS)
 | |
| static asynmsg_t msgtable[ASYN_NR];
 | |
| static int first_slot = 0, next_slot = 0;
 | |
| static int initialized = 0;
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				asynsend3				     *
 | |
|  *===========================================================================*/
 | |
| int asynsend3(dst, mp, fl)
 | |
| endpoint_t dst;
 | |
| message *mp;
 | |
| int fl;
 | |
| {
 | |
|   int i, r, src_ind, dst_ind;
 | |
|   unsigned flags;
 | |
|   static int inside = 0;
 | |
|   int len, needack = 0;
 | |
| 
 | |
|   /* Debug printf() causes asynchronous sends? */
 | |
|   if (inside)	/* Panic will not work either then, so exit */
 | |
| 	exit(1);
 | |
| 
 | |
|   inside = 1;
 | |
| 
 | |
|   if(!initialized) {
 | |
|   	/* Initialize table by marking all entries empty */
 | |
| 	for (i = 0; i < ASYN_NR; i++) msgtable[i].flags = AMF_EMPTY;
 | |
| 
 | |
| 	initialized = 1;
 | |
|   }
 | |
| 
 | |
|   /* Update first_slot. That is, find the first not-completed slot by the
 | |
|    * kernel since the last time we sent this table (e.g., the receiving end of
 | |
|    * the message wasn't ready yet).
 | |
|    */
 | |
|   for (; first_slot < next_slot; first_slot++) {
 | |
| 	flags = msgtable[first_slot].flags;
 | |
| 	if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) {
 | |
| 		/* Marked in use by us (VALID) and processed by the kernel */
 | |
| 		if (msgtable[first_slot].result != OK) {
 | |
| #if NDEBUG
 | |
| 			printf("asynsend: found entry %d with error %d\n",
 | |
| 				first_slot, msgtable[first_slot].result);
 | |
| #endif
 | |
| 			needack = (flags & (AMF_NOTIFY|AMF_NOTIFY_ERR));
 | |
| 		}
 | |
| 		continue;
 | |
| 	}
 | |
| 
 | |
| 	if (flags != AMF_EMPTY) 
 | |
| 		/* Found first not-completed table entry */
 | |
| 		break;
 | |
|   }
 | |
| 
 | |
|   /* Reset to the beginning of the table when all messages are completed */
 | |
|   if (first_slot >= next_slot && !needack)
 | |
| 	next_slot = first_slot = 0;
 | |
| 
 | |
|   /* Can the table handle one more message? */
 | |
|   if (next_slot >= ASYN_NR) {
 | |
| 	/* We're full; tell the kernel to stop processing for now */
 | |
| 	if ((r = senda(NULL, 0)) != OK)
 | |
| 		panic("asynsend: senda failed: %d", r);
 | |
| 
 | |
| 	/* Move all unprocessed messages to the beginning */
 | |
| 	dst_ind = 0;
 | |
| 	for (src_ind = first_slot; src_ind < next_slot; src_ind++) {
 | |
| 		flags = msgtable[src_ind].flags;
 | |
| 
 | |
| 		/* Skip empty entries */
 | |
| 		if (flags == AMF_EMPTY) continue;
 | |
| 
 | |
| 		/* and completed entries only if result is OK or if error 
 | |
| 		 * doesn't need to be acknowledged */
 | |
| 		if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) {
 | |
| 			if (msgtable[src_ind].result == OK)
 | |
| 				continue;
 | |
| 			else {
 | |
| #if NDEBUG
 | |
| 				printf(
 | |
| 				 "asynsend: found entry %d with error %d\n",
 | |
| 					src_ind, msgtable[src_ind].result);
 | |
| #endif
 | |
| 				if (!(flags & (AMF_NOTIFY|AMF_NOTIFY_ERR)))
 | |
| 					/* Don't need to ack this error */
 | |
| 					 continue;
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 
 | |
| 		/* Copy/move in use entry */
 | |
| #if NDEBUG
 | |
| 		printf("asynsend: copying entry %d to %d\n", src_ind, dst_ind);
 | |
| #endif
 | |
| 		if (src_ind != dst_ind) msgtable[dst_ind] = msgtable[src_ind];
 | |
| 			dst_ind++;
 | |
| 	}
 | |
| 
 | |
| 	/* Mark unused entries empty */
 | |
| 	for (i = dst_ind; i < ASYN_NR; i++) msgtable[i].flags = AMF_EMPTY;
 | |
| 
 | |
| 	first_slot = 0;
 | |
| 	next_slot = dst_ind;
 | |
| 	if (next_slot >= ASYN_NR)	/* Cleanup failed */
 | |
| 		panic("asynsend: msgtable full");
 | |
|   }
 | |
| 
 | |
|   fl |= AMF_VALID;	/* Mark in use */
 | |
|   msgtable[next_slot].dst = dst;
 | |
|   msgtable[next_slot].msg = *mp;
 | |
|   msgtable[next_slot].flags = fl;		/* Has to be last. The kernel 
 | |
| 					 	 * scans this table while we
 | |
| 						 * are sleeping.
 | |
| 					 	 */
 | |
|   next_slot++;
 | |
| 
 | |
|   assert(next_slot >= first_slot);
 | |
|   len = next_slot - first_slot;
 | |
|   assert(first_slot + len <= ASYN_NR);
 | |
|   assert(len >= 0);
 | |
| 
 | |
|   inside = 0;
 | |
| 
 | |
|   /* Tell the kernel to rescan the table */
 | |
|   return senda(&msgtable[first_slot], len);
 | |
| }
 | |
| 
 | |
| /*===========================================================================*
 | |
|  *				asyn_geterror				     *
 | |
|  *===========================================================================*/
 | |
| int asyn_geterror(endpoint_t *dst, message *msg, int *err)
 | |
| {
 | |
|   int src_ind, flags, result;
 | |
| 
 | |
|   if (!initialized) return(0);
 | |
| 
 | |
|   for (src_ind = 0; src_ind < next_slot; src_ind++) {
 | |
| 	flags = msgtable[src_ind].flags;
 | |
| 	result = msgtable[src_ind].result;
 | |
| 
 | |
| 	/* Find a message that has been completed with an error */
 | |
| 	if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) {
 | |
| 		if (result != OK && (flags & (AMF_NOTIFY|AMF_NOTIFY_ERR))) {
 | |
| 			/* Found one */
 | |
| 			if (dst != NULL) *dst = msgtable[src_ind].dst;
 | |
| 			if (msg != NULL) *msg = msgtable[src_ind].msg;
 | |
| 			if (err != NULL) *err = result;
 | |
| 
 | |
| 			/* Acknowledge error so it can be cleaned up upon next
 | |
| 			 * asynsend */
 | |
| 			msgtable[src_ind].result = OK; 
 | |
| 
 | |
| 			return(1);
 | |
| 		}
 | |
| 	}
 | |
|   }
 | |
| 
 | |
|   return(0);
 | |
| }
 | |
| 
 | 
