xref: /minix/minix/lib/libsys/asynsend.c (revision 83133719)
1 
2 #include <minix/config.h>
3 #include <assert.h>
4 #include <sys/types.h>
5 #include <minix/const.h>
6 #include <minix/type.h>
7 
8 #include <stdlib.h>
9 #include <unistd.h>
10 #include <minix/syslib.h>
11 #include <minix/sysutil.h>
12 #include <minix/sys_config.h>
13 
14 #include <limits.h>
15 #include <errno.h>
16 
17 #define ASYN_NR	(2*_NR_PROCS)
18 static asynmsg_t msgtable[ASYN_NR];
19 static int first_slot = 0, next_slot = 0;
20 static int initialized = 0;
21 
22 #define DEBUG 0
23 
24 /*===========================================================================*
25  *				asynsend3				     *
26  *===========================================================================*/
27 int asynsend3(dst, mp, fl)
28 endpoint_t dst;
29 message *mp;
30 int fl;
31 {
32   int i, r, src_ind, dst_ind;
33   unsigned flags;
34   static int inside = 0;
35   int len, needack = 0;
36 
37   /* Debug printf() causes asynchronous sends? */
38   if (inside)	/* Panic will not work either then, so exit */
39 	exit(1);
40 
41   inside = 1;
42 
43   if(!initialized) {
44   	/* Initialize table by marking all entries empty */
45 	for (i = 0; i < ASYN_NR; i++) msgtable[i].flags = AMF_EMPTY;
46 
47 	initialized = 1;
48   }
49 
50   /* Update first_slot. That is, find the first not-completed slot by the
51    * kernel since the last time we sent this table (e.g., the receiving end of
52    * the message wasn't ready yet).
53    */
54   for (; first_slot < next_slot; first_slot++) {
55 	flags = msgtable[first_slot].flags;
56 	if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) {
57 		/* Marked in use by us (VALID) and processed by the kernel */
58 		if (msgtable[first_slot].result != OK) {
59 #if DEBUG
60 			printf("asynsend: found entry %d with error %d\n",
61 				first_slot, msgtable[first_slot].result);
62 #endif
63 			needack = (flags & (AMF_NOTIFY|AMF_NOTIFY_ERR));
64 		}
65 		continue;
66 	}
67 
68 	if (flags != AMF_EMPTY)
69 		/* Found first not-completed table entry */
70 		break;
71   }
72 
73   /* Reset to the beginning of the table when all messages are completed */
74   if (first_slot >= next_slot && !needack)
75 	next_slot = first_slot = 0;
76 
77   /* Can the table handle one more message? */
78   if (next_slot >= ASYN_NR) {
79 	/* We're full; tell the kernel to stop processing for now */
80 	if ((r = ipc_senda(NULL, 0)) != OK)
81 		panic("asynsend: ipc_senda failed: %d", r);
82 
83 	/* Move all unprocessed messages to the beginning */
84 	dst_ind = 0;
85 	for (src_ind = first_slot; src_ind < next_slot; src_ind++) {
86 		flags = msgtable[src_ind].flags;
87 
88 		/* Skip empty entries */
89 		if (flags == AMF_EMPTY) continue;
90 
91 		/* and completed entries only if result is OK or if error
92 		 * doesn't need to be acknowledged */
93 		if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) {
94 			if (msgtable[src_ind].result == OK)
95 				continue;
96 			else {
97 #if DEBUG
98 				printf(
99 				 "asynsend: found entry %d with error %d\n",
100 					src_ind, msgtable[src_ind].result);
101 #endif
102 				if (!(flags & (AMF_NOTIFY|AMF_NOTIFY_ERR)))
103 					/* Don't need to ack this error */
104 					 continue;
105 			}
106 		}
107 
108 
109 		/* Copy/move in use entry */
110 #if DEBUG
111 		printf("asynsend: copying entry %d to %d\n", src_ind, dst_ind);
112 #endif
113 		if (src_ind != dst_ind) msgtable[dst_ind] = msgtable[src_ind];
114 			dst_ind++;
115 	}
116 
117 	/* Mark unused entries empty */
118 	for (i = dst_ind; i < ASYN_NR; i++) msgtable[i].flags = AMF_EMPTY;
119 
120 	first_slot = 0;
121 	next_slot = dst_ind;
122 	if (next_slot >= ASYN_NR)	/* Cleanup failed */
123 		panic("asynsend: msgtable full");
124   }
125 
126   fl |= AMF_VALID;	/* Mark in use */
127   msgtable[next_slot].dst = dst;
128   msgtable[next_slot].msg = *mp;
129   msgtable[next_slot].flags = fl;		/* Has to be last. The kernel
130 					 	 * scans this table while we
131 						 * are sleeping.
132 					 	 */
133   next_slot++;
134 
135   assert(next_slot >= first_slot);
136   len = next_slot - first_slot;
137   assert(first_slot + len <= ASYN_NR);
138   assert(len >= 0);
139 
140   inside = 0;
141 
142   /* Tell the kernel to rescan the table */
143   return ipc_senda(&msgtable[first_slot], len);
144 }
145 
146 /*===========================================================================*
147  *				asyn_geterror				     *
148  *===========================================================================*/
149 int asyn_geterror(endpoint_t *dst, message *msg, int *err)
150 {
151   int src_ind, flags, result;
152 
153   if (!initialized) return(0);
154 
155   for (src_ind = 0; src_ind < next_slot; src_ind++) {
156 	flags = msgtable[src_ind].flags;
157 	result = msgtable[src_ind].result;
158 
159 	/* Find a message that has been completed with an error */
160 	if ((flags & (AMF_VALID|AMF_DONE)) == (AMF_VALID|AMF_DONE)) {
161 		if (result != OK && (flags & (AMF_NOTIFY|AMF_NOTIFY_ERR))) {
162 			/* Found one */
163 			if (dst != NULL) *dst = msgtable[src_ind].dst;
164 			if (msg != NULL) *msg = msgtable[src_ind].msg;
165 			if (err != NULL) *err = result;
166 
167 			/* Acknowledge error so it can be cleaned up upon next
168 			 * asynsend */
169 			msgtable[src_ind].result = OK;
170 
171 			return(1);
172 		}
173 	}
174   }
175 
176   return(0);
177 }
178 
179