Browse code

io_wait: fix kqueue and too many errors in changelist

kevent() tries to return errors in the changelist back in the
supplied eventlist array. However if this is not large enough, the
whole kevent() syscall will fail.
Now if kevent() fails with EBADF the call will be retried with a
smaller set of changes, until the entire original changelist is
applied.
Fixes also kq_ev_change() flush mode: on error it will try to
apply the changes one-by-one.

(this affects only systems that have kqueue: *bsd and darwin)

Andrei Pelinescu-Onciul authored on 17/06/2010 16:43:14
Showing 1 changed files
... ...
@@ -247,6 +247,7 @@ static inline int kq_ev_change(io_wait_h* h, int fd, int filter, int flag,
247 247
 								void* data)
248 248
 {
249 249
 	int n;
250
+	int r;
250 251
 	struct timespec tspec;
251 252
 
252 253
 	if (h->kq_nchanges>=h->kq_changes_size){
... ...
@@ -257,11 +258,36 @@ static inline int kq_ev_change(io_wait_h* h, int fd, int filter, int flag,
257 258
 		tspec.tv_nsec=0;
258 259
 again:
259 260
 		n=kevent(h->kq_fd, h->kq_changes, h->kq_nchanges, 0, 0, &tspec);
260
-		if (n==-1){
261
-			if (errno==EINTR) goto again;
262
-			LOG(L_ERR, "ERROR: io_watch_add: kevent flush changes "
261
+		if (unlikely(n == -1)){
262
+			if (likely(errno == EBADF)) {
263
+				/* one of the file descriptors is bad, probably already
264
+				   closed => try to apply changes one-by-one */
265
+				for (r = 0; r < h->kq_nchanges; r++) {
266
+retry2:
267
+					n = kevent(h->kq_fd, &h->kq_changes[r], 1, 0, 0, &tspec);
268
+					if (n==-1) {
269
+						if (errno == EBADF)
270
+							continue; /* skip over it */
271
+						if (errno == EINTR)
272
+							goto retry2;
273
+						LOG(L_ERR, "ERROR: io_watch_add: kevent flush changes"
274
+									" failed: %s [%d]\n",
275
+										strerror(errno), errno);
276
+						/* shift the array */
277
+						memmove(&h->kq_changes[0], &h->kq_changes[r+1],
278
+									sizeof(h->kq_changes[0])*
279
+										(h->kq_nchanges-r-1));
280
+						h->kq_nchanges-=(r+1);
281
+						return -1;
282
+					}
283
+				}
284
+			} else if (errno == EINTR) goto again;
285
+			else {
286
+				LOG(L_ERR, "ERROR: io_watch_add: kevent flush changes"
263 287
 						" failed: %s [%d]\n", strerror(errno), errno);
264
-			return -1;
288
+				h->kq_nchanges=0; /* reset changes array */
289
+				return -1;
290
+			}
265 291
 		}
266 292
 		h->kq_nchanges=0; /* changes array is empty */
267 293
 	}
... ...
@@ -1076,22 +1102,43 @@ inline static int io_wait_loop_kqueue(io_wait_h* h, int t, int repeat)
1076 1102
 	int n, r;
1077 1103
 	struct timespec tspec;
1078 1104
 	struct fd_map* fm;
1105
+	int orig_changes;
1106
+	int apply_changes;
1079 1107
 	int revents;
1080 1108
 	
1081 1109
 	tspec.tv_sec=t;
1082 1110
 	tspec.tv_nsec=0;
1111
+	orig_changes=h->kq_nchanges;
1112
+	apply_changes=orig_changes;
1113
+	do {
1083 1114
 again:
1084
-		n=kevent(h->kq_fd, h->kq_changes, h->kq_nchanges,  h->kq_array,
1115
+		n=kevent(h->kq_fd, h->kq_changes, apply_changes,  h->kq_array,
1085 1116
 					h->fd_no, &tspec);
1086 1117
 		if (unlikely(n==-1)){
1087 1118
 			if (errno==EINTR) goto again; /* signal, ignore it */
1088
-			else{
1119
+			else if (errno==EBADF) {
1120
+				/* some of the FDs in kq_changes are bad (already closed)
1121
+				   and there is not enough space in kq_array to return all
1122
+				   of them back */
1123
+				apply_changes = h->fd_no;
1124
+				goto again;
1125
+			}else{
1089 1126
 				LOG(L_ERR, "ERROR: io_wait_loop_kqueue: kevent:"
1090 1127
 						" %s [%d]\n", strerror(errno), errno);
1091 1128
 				goto error;
1092 1129
 			}
1093 1130
 		}
1094
-		h->kq_nchanges=0; /* reset changes array */
1131
+		/* remove applied changes */
1132
+		h->kq_nchanges -= apply_changes;
1133
+		if (unlikely(apply_changes < orig_changes)) {
1134
+			orig_changes -= apply_changes;
1135
+			memmove(&h->kq_changes[0], &h->kq_changes[apply_changes],
1136
+									sizeof(h->kq_changes[0])*h->kq_nchanges);
1137
+			apply_changes = orig_changes<h->fd_no ? orig_changes : h->fd_no;
1138
+		} else {
1139
+			orig_changes = 0;
1140
+			apply_changes = 0;
1141
+		}
1095 1142
 		for (r=0; r<n; r++){
1096 1143
 #ifdef EXTRA_DEBUG
1097 1144
 			DBG("DBG: kqueue: event %d/%d: fd=%d, udata=%lx, flags=0x%x\n",
... ...
@@ -1148,6 +1195,7 @@ again:
1148 1195
 				}
1149 1196
 			}
1150 1197
 		}
1198
+	} while(unlikely(orig_changes));
1151 1199
 error:
1152 1200
 	return n;
1153 1201
 }