]> git.ipfire.org Git - thirdparty/unbound.git/commitdiff
Porting continues. TCP works fine now.
authorWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 23 Jun 2008 13:52:03 +0000 (13:52 +0000)
committerWouter Wijngaards <wouter@nlnetlabs.nl>
Mon, 23 Jun 2008 13:52:03 +0000 (13:52 +0000)
git-svn-id: file:///svn/unbound/trunk@1128 be551aaa-1e26-0410-a405-d3ace91eadb9

doc/Changelog
testcode/do-tests.sh
testcode/mini_tpkg.sh
testdata/stream_tcp.tpkg
util/netevent.c
util/winsock_event.c
util/winsock_event.h

index 6b236714f0d2ff42504006a7db94f41013a952ea..154fd37ab52143a32a043db689ac6ff6f2bd101b 100644 (file)
@@ -1,3 +1,13 @@
+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.
index a50299e9e4ad41385e930425be6407f69bd2afe4..4c13c9fb82e6269ef42265d0c13b59881a759b99 100755 (executable)
@@ -7,6 +7,22 @@ NEED_XXD='fwd_compress_c00c.tpkg'
 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
@@ -48,6 +64,16 @@ for test in `ls *.tpkg`; do
                        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
index 6e89a3986f05a1905cbdecd897e769abfb356fd0..1693c488423690343d17a044b082b9498741c253 100755 (executable)
@@ -18,14 +18,18 @@ if test "$1" = "fake"; then
 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
@@ -113,3 +117,10 @@ fi
 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
index 17412150574d1d6c6ab14a649ac7de57fd2aa878..c0c147754067b4bac420d471ae325ca2d046bb1f 100644 (file)
Binary files a/testdata/stream_tcp.tpkg and b/testdata/stream_tcp.tpkg differ
index 9cf71b5f94758c2e903d48c2bb92ccc254d561d8..3e5e1c99102db3f484a4747e1ac9220aec7171e9 100644 (file)
@@ -574,9 +574,12 @@ comm_point_tcp_accept_callback(int fd, short event, void* arg)
                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,
@@ -671,10 +674,14 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
 #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
@@ -712,10 +719,14 @@ comm_point_tcp_handle_read(int fd, struct comm_point* c, int short_ok)
                        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
@@ -793,9 +804,12 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
                                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
@@ -822,9 +836,12 @@ comm_point_tcp_handle_write(int fd, struct comm_point* c)
                        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
index c1ccb5aad4b0e689b20846fbef55a01a49c874f6..343367562776c99c1d6c9b7b8e55f27b6c4e4e4c 100644 (file)
@@ -126,6 +126,8 @@ void *event_init(uint32_t* time_secs, struct timeval* time_tv)
                 event_base_free(base);
                 return NULL;
         }
+       base->tcp_stickies = 0;
+       base->tcp_reinvigorated = 0;
         return base;
 }
 
@@ -184,12 +186,20 @@ static int handle_select(struct event_base* base, struct timeval* wait)
        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 */
@@ -201,7 +211,6 @@ static int handle_select(struct event_base* base, struct timeval* wait)
        }
        log_assert(numwait <= WSA_MAXIMUM_WAIT_EVENTS);
 
-
        /* do the wait */
        if(numwait == 0) {
                /* WSAWaitFor.. doesn't like 0 event objects */
@@ -232,8 +241,8 @@ static int handle_select(struct event_base* base, struct timeval* wait)
                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;
@@ -248,37 +257,55 @@ static int handle_select(struct event_base* base, struct timeval* wait)
                }
                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,
@@ -296,9 +323,20 @@ static int handle_select(struct event_base* base, struct timeval* wait)
                         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;
 }
 
@@ -358,6 +396,8 @@ void event_set(struct event *ev, int fd, short bits,
 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;
 }
@@ -377,6 +417,7 @@ int event_add(struct event *ev, struct timeval *tv)
                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;
@@ -393,6 +434,7 @@ int event_add(struct event *ev, struct timeval *tv)
                                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;
@@ -414,6 +456,11 @@ int event_add(struct event *ev, struct timeval *tv)
                        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)) {
@@ -502,4 +549,17 @@ int signal_del(struct event *ev)
         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 */
index 6dec399dafa8320697772283e9d1f28d87cf79e2..2851b0eeb99e204a527fe9b6aae3d36e6e0d862e 100644 (file)
  * 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.
@@ -69,6 +73,8 @@
  *
  * 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
@@ -122,6 +128,20 @@ struct event_base
        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;
+
 };
 
 /**
@@ -152,6 +172,13 @@ struct event {
        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 */
@@ -192,5 +219,13 @@ int signal_del(struct event *);
 /** 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 */