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