+23 June 2008: Wouter
+ - fixup minitpkg to cleanup on windows with its file locking troubles.
+ - minitpkg shows skipped tests in report.
+ - skip ipv6 tests on ipv4 only hosts (requires only ipv6 localhost not
+ ipv6 connectivity).
+ - winsock event handler keeps track of sticky TCP events, that have
+ not been fully handled yet. when interest in the event(s) resumes,
+ they are sent again. When WOULDBLOCK is returned events are cleared.
+ - skip tests that need signals when testing on mingw.
+
18 June 2008: Wouter
- open testbound replay files in binary mode, because fseek/ftell
do not work in ascii-mode on windows. The b does nothing on unix.
NEED_NC='fwd_compress_c00c.tpkg'
NEED_CURL='06-ianaports.tpkg'
NEED_WHOAMI='07-confroot.tpkg'
+NEED_IPV6='fwd_ancil.tpkg fwd_tcp_tc6.tpkg'
+NEED_JOBCONTROL='tcp_sigpipe.tpkg'
+
+# test if job control - and also signals - are available (not on mingw).
+if wait %% 2>&1 | grep "job control not enabled" >/dev/null 2>&1; then
+ JOBCONTROL=no
+else
+ JOBCONTROL=yes
+fi
+
+# test for ipv6, uses dig 9.4.2 peculiarity (test @ before -v).
+if dig @::1 -v >/dev/null 2>&1; then
+ HAVE_IPV6=yes
+else
+ HAVE_IPV6=no
+fi
cd testdata;
sh ../testcode/mini_tpkg.sh clean
SKIP=1;
fi
fi
+ if echo $NEED_IPV6 | grep $test >/dev/null; then
+ if test "$HAVE_IPV6" = no; then
+ SKIP=1;
+ fi
+ fi
+ if echo $NEED_JOBCONTROL | grep $test >/dev/null; then
+ if test "$JOBCONTROL" = no; then
+ SKIP=1;
+ fi
+ fi
if test $SKIP -eq 0; then
echo $test
sh ../testcode/mini_tpkg.sh -a ../.. exe $test
fi
if test "$1" = "report" || test "$2" = "report"; then
echo "Minitpkg Report"
- for result in result.*; do
- name=`echo $result | sed -e 's/result\.//'`
+ for result in *.tpkg; do
+ name=`basename $result .tpkg`
if test -f ".done-$name"; then
if test "$1" != "-q"; then
echo "** PASSED ** : $name"
fi
else
- echo "!! FAILED !! : $name"
+ if test -f "result.$name"; then
+ echo "!! FAILED !! : $name"
+ else
+ echo ">> SKIPPED<< : $name"
+ fi
fi
done
exit 0
mv $result ..
cd ..
rm -rf $dir
+# compat for windows where deletion may not succeed initially (files locked
+# by processes that still have to exit).
+if test $? -eq 1; then
+ echo "minitpkg waiting for processes to terminate"
+ sleep 2 # some time to exit, and try again
+ rm -rf $dir
+fi
log_err("accept failed: %s", strerror(errno));
#else /* USE_WINSOCK */
if(WSAGetLastError() == WSAEINPROGRESS ||
- WSAGetLastError() == WSAEWOULDBLOCK ||
WSAGetLastError() == WSAECONNRESET)
return;
+ if(WSAGetLastError() == WSAEWOULDBLOCK) {
+ winsock_tcp_wouldblock(&c->ev->ev, EV_READ);
+ return ;
+ }
log_err("accept failed: %s", wsa_strerror(WSAGetLastError()));
#endif
log_addr(0, "remote address is", &c_hdl->repinfo.addr,
#endif
log_err("read (in tcp s): %s", strerror(errno));
#else /* USE_WINSOCK */
- if(WSAGetLastError() == WSAEINPROGRESS ||
- WSAGetLastError() == WSAECONNRESET ||
- WSAGetLastError() == WSAEWOULDBLOCK)
+ if(WSAGetLastError() == WSAECONNRESET)
+ return 0;
+ if(WSAGetLastError() == WSAEINPROGRESS)
+ return 1;
+ if(WSAGetLastError() == WSAEWOULDBLOCK) {
+ winsock_tcp_wouldblock(&c->ev->ev, EV_READ);
return 1;
+ }
log_err("read (in tcp s): %s",
wsa_strerror(WSAGetLastError()));
#endif
return 1;
log_err("read (in tcp r): %s", strerror(errno));
#else /* USE_WINSOCK */
- if(WSAGetLastError() == WSAEINPROGRESS ||
- WSAGetLastError() == WSAECONNRESET ||
- WSAGetLastError() == WSAEWOULDBLOCK)
+ if(WSAGetLastError() == WSAECONNRESET)
+ return 0;
+ if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
+ if(WSAGetLastError() == WSAEWOULDBLOCK) {
+ winsock_tcp_wouldblock(&c->ev->ev, EV_READ);
+ return 1;
+ }
log_err("read (in tcp r): %s",
wsa_strerror(WSAGetLastError()));
#endif
return 1;
log_err("tcp writev: %s", strerror(errno));
#else
- if(WSAGetLastError() == WSAEINPROGRESS ||
- WSAGetLastError() == WSAEWOULDBLOCK)
+ if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
+ if(WSAGetLastError() == WSAEWOULDBLOCK) {
+ winsock_tcp_wouldblock(&c->ev->ev, EV_WRITE);
+ return 1;
+ }
log_err("tcp send s: %s",
wsa_strerror(WSAGetLastError()));
#endif
return 1;
log_err("tcp send r: %s", strerror(errno));
#else
- if(WSAGetLastError() == WSAEINPROGRESS ||
- WSAGetLastError() == WSAEWOULDBLOCK)
+ if(WSAGetLastError() == WSAEINPROGRESS)
return 1;
+ if(WSAGetLastError() == WSAEWOULDBLOCK) {
+ winsock_tcp_wouldblock(&c->ev->ev, EV_WRITE);
+ return 1;
+ }
log_err("tcp send r: %s",
wsa_strerror(WSAGetLastError()));
#endif
event_base_free(base);
return NULL;
}
+ base->tcp_stickies = 0;
+ base->tcp_reinvigorated = 0;
return base;
}
struct event* eventlist[WSK_MAX_ITEMS];
WSANETWORKEVENTS netev;
int i, numwait = 0, startidx = 0, was_timeout = 0;
+ int newstickies = 0;
+ struct timeval nultm;
#ifndef S_SPLINT_S
if(wait->tv_sec==(time_t)-1)
wait = NULL;
if(wait)
timeout = wait->tv_sec*1000 + wait->tv_usec/1000;
+ if(base->tcp_stickies) {
+ wait = &nultm;
+ nultm.tv_sec = 0;
+ nultm.tv_usec = 0;
+ timeout = 0; /* no waiting, we have sticky events */
+ }
#endif
/* prepare event array */
}
log_assert(numwait <= WSA_MAXIMUM_WAIT_EVENTS);
-
/* do the wait */
if(numwait == 0) {
/* WSAWaitFor.. doesn't like 0 event objects */
return -1;
/* callbacks */
- if(was_timeout)
- return 0;
+ if(base->tcp_stickies)
+ startidx = 0; /* process all events, some are sticky */
for(i=startidx; i<numwait; i++) {
short bits = 0;
}
if((netev.lNetworkEvents & FD_READ)) {
if(netev.iErrorCode[FD_READ_BIT] != 0)
- log_err("FD_READ_BIT error: %s",
+ verbose(VERB_ALGO, "FD_READ_BIT error: %s",
wsa_strerror(netev.iErrorCode[FD_READ_BIT]));
bits |= EV_READ;
}
if((netev.lNetworkEvents & FD_WRITE)) {
if(netev.iErrorCode[FD_WRITE_BIT] != 0)
- log_err("FD_WRITE_BIT error: %s",
+ verbose(VERB_ALGO, "FD_WRITE_BIT error: %s",
wsa_strerror(netev.iErrorCode[FD_WRITE_BIT]));
bits |= EV_WRITE;
}
if((netev.lNetworkEvents & FD_CONNECT)) {
if(netev.iErrorCode[FD_CONNECT_BIT] != 0)
- log_err("FD_CONNECT_BIT error: %s",
+ verbose(VERB_ALGO, "FD_CONNECT_BIT error: %s",
wsa_strerror(netev.iErrorCode[FD_CONNECT_BIT]));
bits |= EV_READ;
bits |= EV_WRITE;
}
if((netev.lNetworkEvents & FD_ACCEPT)) {
if(netev.iErrorCode[FD_ACCEPT_BIT] != 0)
- log_err("FD_ACCEPT_BIT error: %s",
+ verbose(VERB_ALGO, "FD_ACCEPT_BIT error: %s",
wsa_strerror(netev.iErrorCode[FD_ACCEPT_BIT]));
bits |= EV_READ;
}
if((netev.lNetworkEvents & FD_CLOSE)) {
if(netev.iErrorCode[FD_CLOSE_BIT] != 0)
- log_err("FD_CLOSE_BIT error: %s",
+ verbose(VERB_ALGO, "FD_CLOSE_BIT error: %s",
wsa_strerror(netev.iErrorCode[FD_CLOSE_BIT]));
bits |= EV_READ;
bits |= EV_WRITE;
}
- if(bits) {
+ if(eventlist[i]->is_tcp && eventlist[i]->stick_events) {
+ verbose(VERB_ALGO, "winsock %d pass sticky %s%s",
+ eventlist[i]->ev_fd,
+ (eventlist[i]->old_events&EV_READ)?"EV_READ":"",
+ (eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":"");
+ bits |= eventlist[i]->old_events;
+ }
+ if(eventlist[i]->is_tcp && bits) {
+ eventlist[i]->old_events = bits;
+ eventlist[i]->stick_events = 1;
+ if((eventlist[i]->ev_events & bits)) {
+ newstickies = 1;
+ }
+ verbose(VERB_ALGO, "winsock %d store sticky %s%s",
+ eventlist[i]->ev_fd,
+ (eventlist[i]->old_events&EV_READ)?"EV_READ":"",
+ (eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":"");
+ }
+ if((bits & eventlist[i]->ev_events)) {
verbose(VERB_ALGO, "winsock event callback %p fd=%d "
"%s%s%s%s%s ; %s%s%s",
eventlist[i], eventlist[i]->ev_fd,
fptr_ok(fptr_whitelist_event(
eventlist[i]->ev_callback));
(*eventlist[i]->ev_callback)(eventlist[i]->ev_fd,
- bits, eventlist[i]->ev_arg);
+ bits & eventlist[i]->ev_events,
+ eventlist[i]->ev_arg);
}
+ if(eventlist[i]->is_tcp && bits)
+ verbose(VERB_ALGO, "winsock %d got sticky %s%s",
+ eventlist[i]->ev_fd,
+ (eventlist[i]->old_events&EV_READ)?"EV_READ":"",
+ (eventlist[i]->old_events&EV_WRITE)?"EV_WRITE":"");
}
+ if(base->tcp_reinvigorated) {
+ base->tcp_reinvigorated = 0;
+ newstickies = 1;
+ }
+ base->tcp_stickies = newstickies;
return 0;
}
int event_base_set(struct event_base *base, struct event *ev)
{
ev->ev_base = base;
+ ev->old_events = 0;
+ ev->stick_events = 0;
ev->added = 0;
return 0;
}
return -1;
ev->idx = ev->ev_base->max++;
ev->ev_base->items[ev->idx] = ev;
+ ev->is_tcp = 0;
if((ev->ev_events&(EV_READ|EV_WRITE)) && ev->ev_fd != -1) {
BOOL b=0;
wsa_strerror(WSAGetLastError()));
if(t == SOCK_STREAM) {
/* TCP socket */
+ ev->is_tcp = 1;
events |= FD_CLOSE;
if( (ev->ev_events&EV_WRITE) )
events |= FD_CONNECT;
log_err("WSAEventSelect failed: %s",
wsa_strerror(WSAGetLastError()));
}
+ if(ev->is_tcp && ev->stick_events &&
+ (ev->ev_events & ev->old_events)) {
+ /* go to processing the sticky event right away */
+ ev->ev_base->tcp_reinvigorated = 1;
+ }
}
if(tv && (ev->ev_events&EV_TIMEOUT)) {
return 0;
}
+void winsock_tcp_wouldblock(struct event* ev, int eventbits)
+{
+ verbose(VERB_ALGO, "winsock: tcp wouldblock %s",
+ eventbits==EV_READ?"EV_READ":"EV_WRITE");
+ ev->old_events &= (~eventbits);
+ if(ev->old_events == 0)
+ ev->stick_events = 0;
+ /* in case this is the last sticky event, we could
+ * possibly run an empty handler loop to reset the base
+ * tcp_stickies variable
+ */
+}
+
#endif /* USE_WINSOCK */
* the socket has blocked, and the event handler can mark it as such.
* Thus, this file transforms the edge notify from windows to a level notify
* that is compatible with UNIX.
- * However, the WSAEventSelect page says that it does do level notify, as long
+ * The WSAEventSelect page says that it does do level notify, as long
* as you call a recv/write/accept at least once when it is signalled.
+ * This last bit is not true, even though documented in server2008 api docs
+ * from microsoft, it does not happen at all. Instead you have to test for
+ * WSAEWOULDBLOCK on a tcp stream, and only then retest the socket.
+ * And before that remember the previous result as still valid.
*
* To stay 'fair', instead of emptying a socket completely, the event handler
* can test the other (marked as blocking) sockets for new events.
*
* on winsock, you must use recv() and send() for TCP reads and writes,
* not read() and write(), those work only on files.
+ *
+ * Also fseek and fseeko do not work if a FILE is not fopen-ed in binary mode.
*/
#ifndef UTIL_WINSOCK_EVENT_H
uint32_t* time_secs;
/** where to store time in microseconds */
struct timeval* time_tv;
+ /**
+ * TCP streams have sticky events to them, these are not
+ * reported by the windows event system anymore, we have to
+ * keep reporting those events as present until wouldblock() is
+ * signalled by the handler back to use.
+ */
+ int tcp_stickies;
+ /**
+ * should next cycle process reinvigorated stickies,
+ * these are stickies that have been stored, but due to a new
+ * event_add a sudden interest in the event has incepted.
+ */
+ int tcp_reinvigorated;
+
};
/**
int idx;
/** the event handle to wait for new events to become ready */
WSAEVENT hEvent;
+ /** true if this filedes is a TCP socket and needs special attention */
+ int is_tcp;
+ /** remembered EV_ values */
+ short old_events;
+ /** should remembered EV_ values be used for TCP streams.
+ * Reset after WOULDBLOCK is signaled using the function. */
+ int stick_events;
};
/** create event base */
/** compare events in tree, based on timevalue, ptr for uniqueness */
int mini_ev_cmp(const void* a, const void* b);
+/**
+ * Routine for windows only, where the handling layer can signal that
+ * a TCP stream encountered WSAEWOULDBLOCK for a stream and thus needs
+ * retesting the event.
+ * Pass if EV_READ or EV_WRITE gave wouldblock.
+ */
+void winsock_tcp_wouldblock(struct event* ev, int eventbit);
+
#endif /* USE_WINSOCK */
#endif /* UTIL_WINSOCK_EVENT_H */