vpi: fix and simplify windows simulation (ends of msg were ignored)
authorFlorent Kermarrec <florent@enjoy-digital.fr>
Tue, 12 May 2015 23:20:57 +0000 (01:20 +0200)
committerFlorent Kermarrec <florent@enjoy-digital.fr>
Wed, 13 May 2015 01:03:34 +0000 (03:03 +0200)
migen/sim/ipc.py
vpi/ipc.c

index 1fd6c865e13e4a394ea3c7dae2aa35fb47e423db..4e0fa5299af11d7057287d5b4d48e942afc89fd6 100644 (file)
@@ -6,6 +6,8 @@ import os
 import sys
 import struct
 
+if sys.platform == "win32":
+    header_len = 2
 
 #
 # Message classes
@@ -108,7 +110,7 @@ def _pack(message):
         else:
             raise TypeError
     if sys.platform == "win32":
-        size = _pack_int16(len(r) + 2)  # size specifier adds to size
+        size = _pack_int16(len(r) + header_len)
         r = size + r
     return bytes(r)
 
@@ -174,6 +176,8 @@ class Initiator:
         self.socket.bind(self.sockaddr)
         self.socket.listen(1)
 
+        self.ipc_rxbuffer = bytearray()
+
     def _cleanup_file(self):
         try:
             os.remove(self.sockaddr)
@@ -189,30 +193,22 @@ class Initiator:
     def recv(self):
         maxlen = 2048
         if sys.platform == "win32":
-            packet = self.conn.recv(maxlen)
-            if len(packet) < 1:
-                return None
-            if len(packet) >= maxlen:
-                raise PacketTooLarge
-            expected_size = struct.unpack("<H", packet[:2])[0]
-
-            # If full packet wasn't received, keep waiting!
-            if len(packet) < expected_size:
-                packet_frag = self.conn.recv(maxlen)
-                packet += packet_frag
-                if len(packet_frag) < 1:
-                    return None
-                if len(packet) >= maxlen:
-                    raise PacketTooLarge
-
-            # Discard the length from the packet - it's not needed anymore.
-            packet = packet[2:]
+            def recv_packet():
+                while len(self.ipc_rxbuffer) < header_len:
+                    self.ipc_rxbuffer += self.conn.recv(maxlen)
+                packet_len = struct.unpack("<H", self.ipc_rxbuffer[:header_len])[0]
+                while len(self.ipc_rxbuffer) < packet_len:
+                    self.ipc_rxbuffer += self.conn.recv(maxlen)
+                packet = self.ipc_rxbuffer[header_len:packet_len]
+                self.ipc_rxbuffer = self.ipc_rxbuffer[packet_len:]
+                return packet
+            packet = recv_packet()
         else:
             packet = self.conn.recv(maxlen)
-            if len(packet) < 1:
-                return None
-            if len(packet) >= maxlen:
-                raise PacketTooLarge
+        if len(packet) < 1:
+            return None
+        if len(packet) >= maxlen:
+            raise PacketTooLarge
         return _unpack(packet)
 
     def close(self):
@@ -221,14 +217,9 @@ class Initiator:
             self.conn.close()
         if hasattr(self, "socket"):
             if sys.platform == "win32":
-                # self.socket.shutdown(socket.SHUT_RDWR)
-                # Fails with WinError 10057:
-                # A request to send or receive data was disallowed because the
-                # socket is not connected and (when sending on a datagram
-                # socket using a sendto call) no address was supplied
-                # This error will cascade into WinError 10038, where
-                # Simulator.__exit__ didn't finish cleaning up and left an
-                # invalid socket.
+                # don't shutdown our socket since closing connection
+                # seems to already have done it. (trigger an error
+                # otherwise)
                 self.socket.close()
             else:
                 self.socket.shutdown(socket.SHUT_RDWR)
index 2b7dd0082bfb6f890a0512a00d8b430d2921dfb2..21b1efe0a2a8e313a2284833b4e34842bea22140 100644 (file)
--- a/vpi/ipc.c
+++ b/vpi/ipc.c
@@ -3,6 +3,10 @@
  * License: GPLv3 with additional permissions (see README).
  */
 
+#ifdef _WIN32
+#define WINVER 0x501
+#endif
+
 #include <assert.h>
 #include <sys/types.h>
 #include <unistd.h>
@@ -13,7 +17,6 @@
 #ifdef _WIN32
 #include <winsock2.h>
 #include <ws2tcpip.h>
-#define WIN_SOCKET_PORT "50007"
 #else
 #include <sys/socket.h>
 #include <sys/un.h>
@@ -30,6 +33,16 @@ struct ipc_softc {
        void *user;
 };
 
+#define MAX_LEN 2048
+
+#ifdef _WIN32
+#define WIN32_HEADER_LEN 2
+#define WIN32_SOCKET_PORT "50007"
+
+unsigned char ipc_rxbuffer[2*MAX_LEN];
+int ipc_rxlen;
+#endif
+
 struct ipc_softc *ipc_connect(const char *sockaddr,
        go_handler h_go, write_handler h_write, read_handler h_read, void *user)
 {
@@ -37,6 +50,7 @@ struct ipc_softc *ipc_connect(const char *sockaddr,
 #ifdef _WIN32
        struct addrinfo hints, *my_addrinfo;
        WSADATA wsaData;
+       ipc_rxlen = 0;
 #else
        struct sockaddr_un addr;
 #endif
@@ -61,7 +75,7 @@ struct ipc_softc *ipc_connect(const char *sockaddr,
        hints.ai_socktype = SOCK_STREAM;
        hints.ai_protocol = IPPROTO_TCP;
 
-       if(getaddrinfo(sockaddr, WIN_SOCKET_PORT, NULL, &my_addrinfo) != 0) {
+       if(getaddrinfo(sockaddr, WIN32_SOCKET_PORT, NULL, &my_addrinfo) != 0) {
                free(sc);
                return NULL;
        }
@@ -113,7 +127,37 @@ enum {
        MESSAGE_READ_REPLY
 };
 
-#define MAX_LEN 2048
+static int ipc_receive_packet(struct ipc_softc *sc, unsigned char *buffer) {
+#ifdef _WIN32
+       int len;
+       int packet_len;
+       /* ensure we have packet header */
+       while (ipc_rxlen < WIN32_HEADER_LEN) {
+               len = recv(sc->socket, (char *)&ipc_rxbuffer[ipc_rxlen], MAX_LEN, 0);
+               if (len)
+                       ipc_rxlen += len;
+       }
+
+       /* compute packet length and ensure we have the payload */
+       packet_len = ((ipc_rxbuffer[1] << 8) | (ipc_rxbuffer[0]));
+       while (ipc_rxlen < packet_len) {
+               len = recv(sc->socket, (char *) &ipc_rxbuffer[ipc_rxlen], MAX_LEN, 0);
+               if (len)
+                       ipc_rxlen += len;
+       }
+
+       /* copy packet to buffer */
+       memcpy(buffer, ipc_rxbuffer + WIN32_HEADER_LEN, packet_len - WIN32_HEADER_LEN);
+
+       /* prepare ipc_rxbuffer for next packet */
+       ipc_rxlen = ipc_rxlen - packet_len;
+       memcpy(ipc_rxbuffer, ipc_rxbuffer + packet_len, ipc_rxlen);
+
+       return packet_len - WIN32_HEADER_LEN;
+#else
+       return recv(sc->socket, buffer, MAX_LEN, 0);
+#endif
+}
 
 /*
  * 0 -> error
@@ -125,55 +169,13 @@ int ipc_receive(struct ipc_softc *sc)
        unsigned char buffer[MAX_LEN];
        ssize_t l = 0;
        int i;
-#ifdef _WIN32
-       int expected_num;
-       int received_num = 0;
-#endif
 
-#ifdef _WIN32
-       /* Initial recv. Includes a length identifier so that we wait
-        * until a full message is received. The length of the message
-        * includes the length identifier. */
-       while(received_num < 2) {
-               /* Ensure we wait to get the packet length,
-                * which requires two bytes, before continuing. */
-               received_num = recv(sc->socket, (char *)&buffer[l], \
-                       (MAX_LEN - received_num), 0);
-               l += received_num;
-
-               if(received_num == 0)
-                       return 2;
-               if((received_num < 0) || (received_num >= MAX_LEN) || \
-                       (l >= MAX_LEN))
-                       return 0;
-       }
-
-       expected_num = ((buffer[1] << 8) | (buffer[0]));
-       while(l < expected_num) {
-               /* received_num will never exceed MAX_LEN, unless
-                * recv is broken. */
-               received_num = recv(sc->socket, (char *)&buffer[l], \
-                       (MAX_LEN - received_num), 0);
-               l += received_num;
-
-               if(received_num == 0)
-                       return 2;
-               if((received_num < 0) || (received_num >= MAX_LEN) || \
-                       (l >= MAX_LEN))
-                       return 0;
-       } /* l is assumed to have the message length
-          * in the message unpacking code. */
-       i = 2; /* Skip the length identifier. */
-#else
-       /* On Unix, SOCK_SEQPACKET will take care of ensuring message
-        * boundaries are satisfied. */
-       l = recv(sc->socket, buffer, MAX_LEN, 0);
+       l = ipc_receive_packet(sc, (unsigned char *)&buffer);
        if(l == 0)
                return 2;
        if((l < 0) || (l >= MAX_LEN))
                return 0;
-       i = 0; /* No length identifier, so we care about the entire buffer */
-#endif
+       i = 0;
 
        switch(buffer[i++]) {
                case MESSAGE_GO: