]> git.rm.cloudns.org Git - xonotic/darkplaces.git/commitdiff
Make LHNET support TCP and SOME kind of use from other threads.
authorRudolf Polzer <divverent@xonotic.org>
Mon, 12 Aug 2013 07:37:27 +0000 (09:37 +0200)
committerRudolf Polzer <divverent@xonotic.org>
Mon, 12 Aug 2013 07:41:00 +0000 (09:41 +0200)
Most LHNET functions are still unsafe from other threads, but it's good
enough now for blocking I/O from other threads.

lhnet.c
lhnet.h

diff --git a/lhnet.c b/lhnet.c
index cd77a2fa26148ed3a2754ac22b28469e0f73317f..fe2d6733e4571f07fd4f9a27fb05d621acf91f37 100644 (file)
--- a/lhnet.c
+++ b/lhnet.c
@@ -725,7 +725,7 @@ lhnetpacket_t;
 static int lhnet_active;
 static lhnetsocket_t lhnet_socketlist;
 static lhnetpacket_t lhnet_packetlist;
-static int lhnet_default_dscp = 0;
+static volatile int lhnet_default_dscp = 0;
 #ifdef WIN32
 static int lhnet_didWSAStartup = 0;
 static WSADATA lhnet_winsockdata;
@@ -869,19 +869,101 @@ void LHNET_SleepUntilPacket_Microseconds(int microseconds)
 #endif
 }
 
-lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
+static int LHNETSOCKET_TryBind(lhnetsocket_t *lhnetsocket, lhnetaddress_t *address)
 {
-       lhnetsocket_t *lhnetsocket, *s;
+       SOCKLEN_T namelen;
+       int bindresult;
+       if (!address)
+               return 0;
+       lhnetsocket->address = *address;
+       lhnetaddressnative_t *localaddress = (lhnetaddressnative_t *)&lhnetsocket->address;
+#ifdef SUPPORTIPV6
+       if (address->addresstype == LHNETADDRESSTYPE_INET6)
+       {
+               namelen = sizeof(localaddress->addr.in6);
+               bindresult = bind(lhnetsocket->inetsocket, &localaddress->addr.sock, namelen);
+               if (bindresult != -1)
+                       getsockname(lhnetsocket->inetsocket, &localaddress->addr.sock, &namelen);
+       }
+       else
+#endif
+       {
+               namelen = sizeof(localaddress->addr.in);
+               bindresult = bind(lhnetsocket->inetsocket, &localaddress->addr.sock, namelen);
+               if (bindresult != -1)
+                       getsockname(lhnetsocket->inetsocket, &localaddress->addr.sock, &namelen);
+       }
+       return bindresult;
+}
+
+static int LHNETSOCKET_TryConnect(lhnetsocket_t *lhnetsocket, lhnetaddress_t *address)
+{
+       SOCKLEN_T namelen;
+       int connectresult;
        if (!address)
+               return 0;
+       lhnetaddressnative_t *peeraddress = (lhnetaddressnative_t *)&lhnetsocket->address;
+#ifdef SUPPORTIPV6
+       if (address->addresstype == LHNETADDRESSTYPE_INET6)
+       {
+               namelen = sizeof(peeraddress->addr.in6);
+               connectresult = connect(lhnetsocket->inetsocket, &peeraddress->addr.sock, namelen);
+               if (connectresult != -1)
+                       getsockname(lhnetsocket->inetsocket, &peeraddress->addr.sock, &namelen);
+       }
+       else
+#endif
+       {
+               namelen = sizeof(peeraddress->addr.in);
+               connectresult = connect(lhnetsocket->inetsocket, &peeraddress->addr.sock, namelen);
+               if (connectresult != -1)
+                       getsockname(lhnetsocket->inetsocket, &peeraddress->addr.sock, &namelen);
+       }
+       return connectresult;
+}
+
+lhnetsocket_t *LHNET_OpenSocket(lhnetaddress_t *address, lhnetaddress_t *peeraddress, int use_tcp, int use_blocking, int register_for_select)
+{
+       lhnetsocket_t *lhnetsocket, *s;
+       if (!address && !peeraddress)
+               return NULL;
+
+       int addresstype = address ? address->addresstype : peeraddress->addresstype;
+       if (peeraddress && addresstype != peeraddress->addresstype)
+       {
+               Con_Printf("Cannot connect different address types.\n");
+               return NULL;
+       }
+       if (addresstype == LHNETADDRESSTYPE_LOOP && peeraddress)
+       {
+               Con_Printf("LHNETADDRESSTYPE_LOOP does not support peeraddress.\n");
+               return NULL;
+       }
+       if (addresstype == LHNETADDRESSTYPE_LOOP && use_tcp)
+       {
+               Con_Printf("LHNETADDRESSTYPE_LOOP does not support TCP.\n");
                return NULL;
+       }
+       if (addresstype == LHNETADDRESSTYPE_LOOP && use_blocking)
+       {
+               Con_Printf("LHNETADDRESSTYPE_LOOP does not support blocking mode.\n");
+               return NULL;
+       }
+       if (addresstype == LHNETADDRESSTYPE_LOOP && !register_for_select)
+       {
+               Con_Printf("LHNETADDRESSTYPE_LOOP must always be registered for select.\n");
+               return NULL;
+       }
+
        lhnetsocket = (lhnetsocket_t *)Z_Malloc(sizeof(*lhnetsocket));
        if (lhnetsocket)
        {
                memset(lhnetsocket, 0, sizeof(*lhnetsocket));
-               lhnetsocket->address = *address;
-               switch(lhnetsocket->address.addresstype)
+               switch(addresstype)
                {
                case LHNETADDRESSTYPE_LOOP:
+                       // NOTE: here, the address is always set
+                       lhnetsocket->address = *address;
                        if (lhnetsocket->address.port == 0)
                        {
                                // allocate a port dynamically
@@ -919,12 +1001,14 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
 #endif
 #ifdef WIN32
                        if (lhnet_didWSAStartup)
-                       {
 #endif
+                       {
+                               int socktype = use_tcp ? SOCK_STREAM : SOCK_DGRAM;
+                               int proto = use_tcp ? IPPROTO_TCP : IPPROTO_UDP;
 #ifdef SUPPORTIPV6
-                               if ((lhnetsocket->inetsocket = socket(address->addresstype == LHNETADDRESSTYPE_INET6 ? PF_INET6 : PF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1)
+                               if ((lhnetsocket->inetsocket = socket(addresstype == LHNETADDRESSTYPE_INET6 ? PF_INET6 : PF_INET, socktype, proto)) != -1)
 #else
-                               if ((lhnetsocket->inetsocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) != -1)
+                               if ((lhnetsocket->inetsocket = socket(AF_INET, socktype, proto)) != -1)
 #endif
                                {
 #ifdef WIN32
@@ -938,7 +1022,7 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
 #else
                                        char _true = 1;
 #endif
-                                       if (ioctlsocket(lhnetsocket->inetsocket, FIONBIO, &_true) != -1)
+                                       if (ioctlsocket(lhnetsocket->inetsocket, FIONBIO, use_blocking ? &_true : &_false) != -1)
 #endif
                                        {
 #ifdef IPV6_V6ONLY
@@ -946,7 +1030,7 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
                                                // most OSes will create a dual-protocol socket that also listens on IPv4. In this case
                                                // if an IPv4 socket is already bound to the port we want, our bind() call will fail.
                                                int ipv6_only = 1;
-                                               if (address->addresstype != LHNETADDRESSTYPE_INET6
+                                               if (addresstype != LHNETADDRESSTYPE_INET6
                                                        || setsockopt (lhnetsocket->inetsocket, IPPROTO_IPV6, IPV6_V6ONLY,
                                                                                   (const char *)&ipv6_only, sizeof(ipv6_only)) == 0
 #ifdef WIN32
@@ -957,8 +1041,6 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
                                                        )
 #endif
                                                {
-                                                       lhnetaddressnative_t *localaddress = (lhnetaddressnative_t *)&lhnetsocket->address;
-                                                       SOCKLEN_T namelen;
                                                        int bindresult;
 
 #if defined(SOL_RFC1149) && defined(RFC1149_1149ONLY)
@@ -973,47 +1055,39 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
                                                                        Con_Printf("LHNET_OpenSocket_Connectionless: warning: setsockopt(RFC1149_ENABLED) returned error: %s\n", LHNETPRIVATE_StrError());
                                                        }
 #endif
-
-#ifdef SUPPORTIPV6
-                                                       if (address->addresstype == LHNETADDRESSTYPE_INET6)
-                                                       {
-                                                               namelen = sizeof(localaddress->addr.in6);
-                                                               bindresult = bind(lhnetsocket->inetsocket, &localaddress->addr.sock, namelen);
-                                                               if (bindresult != -1)
-                                                                       getsockname(lhnetsocket->inetsocket, &localaddress->addr.sock, &namelen);
-                                                       }
-                                                       else
-#endif
-                                                       {
-                                                               namelen = sizeof(localaddress->addr.in);
-                                                               bindresult = bind(lhnetsocket->inetsocket, &localaddress->addr.sock, namelen);
-                                                               if (bindresult != -1)
-                                                                       getsockname(lhnetsocket->inetsocket, &localaddress->addr.sock, &namelen);
-                                                       }
-                                                       if (bindresult != -1)
+                                                       if (LHNETSOCKET_TryBind(lhnetsocket, address) != -1)
                                                        {
-                                                               int i = 1;
-                                                               // enable broadcast on this socket
-                                                               setsockopt(lhnetsocket->inetsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i));
-#ifdef IP_TOS
+                                                               if (LHNETSOCKET_TryConnect(lhnetsocket, peeraddress) != -1)
                                                                {
-                                                                       // enable DSCP for ToS support
-                                                                       int tos = lhnet_default_dscp << 2;
-                                                                       setsockopt(lhnetsocket->inetsocket, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(tos));
-                                                               }
-#endif
-                                                               lhnetsocket->next = &lhnet_socketlist;
-                                                               lhnetsocket->prev = lhnetsocket->next->prev;
-                                                               lhnetsocket->next->prev = lhnetsocket;
-                                                               lhnetsocket->prev->next = lhnetsocket;
+                                                                       int i = 1;
+                                                                       // enable broadcast on this socket
+                                                                       setsockopt(lhnetsocket->inetsocket, SOL_SOCKET, SO_BROADCAST, (char *)&i, sizeof(i));
+#ifdef IP_TOS
+                                                                       {
+                                                                               // enable DSCP for ToS support
+                                                                               int tos = lhnet_default_dscp << 2;
+                                                                               setsockopt(lhnetsocket->inetsocket, IPPROTO_IP, IP_TOS, (char *) &tos, sizeof(tos));
+                                                                       }
+#endif
+                                                                       lhnetsocket->next = &lhnet_socketlist;
+                                                                       lhnetsocket->prev = lhnetsocket->next->prev;
+                                                                       lhnetsocket->next->prev = lhnetsocket;
+                                                                       lhnetsocket->prev->next = lhnetsocket;
 #ifdef WIN32
-                                                               if (ioctlsocket(lhnetsocket->inetsocket, SIO_UDP_CONNRESET, &_false) == -1)
-                                                                       Con_DPrintf("LHNET_OpenSocket_Connectionless: ioctlsocket SIO_UDP_CONNRESET returned error: %s\n", LHNETPRIVATE_StrError());
+                                                                       if (ioctlsocket(lhnetsocket->inetsocket, SIO_UDP_CONNRESET, &_false) == -1)
+                                                                               Con_DPrintf("LHNET_OpenSocket_Connectionless: ioctlsocket SIO_UDP_CONNRESET returned error: %s\n", LHNETPRIVATE_StrError());
 #endif
-                                                               return lhnetsocket;
+                                                                       return lhnetsocket;
+                                                               }
+                                                               else
+                                                               {
+                                                                       Con_Printf("LHNET_OpenSocket_Connectionless: connect returned error: %s\n", LHNETPRIVATE_StrError());
+                                                               }
                                                        }
                                                        else
+                                                       {
                                                                Con_Printf("LHNET_OpenSocket_Connectionless: bind returned error: %s\n", LHNETPRIVATE_StrError());
+                                                       }
                                                }
 #ifdef IPV6_V6ONLY
                                                else
@@ -1026,8 +1100,8 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
                                }
                                else
                                        Con_Printf("LHNET_OpenSocket_Connectionless: socket returned error: %s\n", LHNETPRIVATE_StrError());
-#ifdef WIN32
                        }
+#ifdef WIN32
                        else
                                Con_Print("LHNET_OpenSocket_Connectionless: can't open a socket (WSAStartup failed during LHNET_Init)\n");
 #endif
@@ -1040,6 +1114,11 @@ lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
        return NULL;
 }
 
+lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address)
+{
+       return LHNET_OpenSocket(address, NULL, 0, 0, 1);
+}
+
 void LHNET_CloseSocket(lhnetsocket_t *lhnetsocket)
 {
        if (lhnetsocket)
@@ -1073,7 +1152,7 @@ int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength,
 {
        lhnetaddressnative_t *address = (lhnetaddressnative_t *)vaddress;
        int value = 0;
-       if (!lhnetsocket || !address || !content || maxcontentlength < 1)
+       if (!lhnetsocket || !content || maxcontentlength < 1)
                return -1;
        if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_LOOP)
        {
@@ -1102,8 +1181,11 @@ int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength,
                                if (p->length <= maxcontentlength)
                                {
                                        lhnetaddressnative_t *localaddress = (lhnetaddressnative_t *)&lhnetsocket->address;
-                                       *address = *localaddress;
-                                       address->port = p->sourceport;
+                                       if (address)
+                                       {
+                                               *address = *localaddress;
+                                               address->port = p->sourceport;
+                                       }
                                        memcpy(content, p->data, p->length);
                                        value = p->length;
                                }
@@ -1120,12 +1202,15 @@ int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength,
        {
                SOCKLEN_T inetaddresslength;
                address->addresstype = LHNETADDRESSTYPE_NONE;
-               inetaddresslength = sizeof(address->addr.in);
-               value = recvfrom(lhnetsocket->inetsocket, (char *)content, maxcontentlength, LHNET_RECVFROM_FLAGS, &address->addr.sock, &inetaddresslength);
+               inetaddresslength = sizeof(address->addr.in); // is OK if address is NULL
+               value = recvfrom(lhnetsocket->inetsocket, (char *)content, maxcontentlength, LHNET_RECVFROM_FLAGS, address ? &address->addr.sock : NULL, address ? &inetaddresslength : NULL);
                if (value > 0)
                {
-                       address->addresstype = LHNETADDRESSTYPE_INET4;
-                       address->port = ntohs(address->addr.in.sin_port);
+                       if (address)
+                       {
+                               address->addresstype = LHNETADDRESSTYPE_INET4;
+                               address->port = ntohs(address->addr.in.sin_port);
+                       }
                        return value;
                }
                else if (value < 0)
@@ -1147,12 +1232,15 @@ int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength,
        {
                SOCKLEN_T inetaddresslength;
                address->addresstype = LHNETADDRESSTYPE_NONE;
-               inetaddresslength = sizeof(address->addr.in6);
-               value = recvfrom(lhnetsocket->inetsocket, (char *)content, maxcontentlength, LHNET_RECVFROM_FLAGS, &address->addr.sock, &inetaddresslength);
+               inetaddresslength = sizeof(address->addr.in6); // is OK if address is NULL
+               value = recvfrom(lhnetsocket->inetsocket, (char *)content, maxcontentlength, LHNET_RECVFROM_FLAGS, address ? &address->addr.sock : NULL, address ? &inetaddresslength : NULL);
                if (value > 0)
                {
-                       address->addresstype = LHNETADDRESSTYPE_INET6;
-                       address->port = ntohs(address->addr.in6.sin6_port);
+                       if (address)
+                       {
+                               address->addresstype = LHNETADDRESSTYPE_INET6;
+                               address->port = ntohs(address->addr.in6.sin6_port);
+                       }
                        return value;
                }
                else if (value == -1)
@@ -1177,12 +1265,17 @@ int LHNET_Write(lhnetsocket_t *lhnetsocket, const void *content, int contentleng
 {
        lhnetaddressnative_t *address = (lhnetaddressnative_t *)vaddress;
        int value = -1;
-       if (!lhnetsocket || !address || !content || contentlength < 1)
+       if (!lhnetsocket || !content || contentlength < 1)
                return -1;
        if (lhnetsocket->address.addresstype != address->addresstype)
                return -1;
        if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_LOOP)
        {
+               if (!address)
+               {
+                       Con_DPrintf("LHNET_Write: destination address required on LHNETADDRESSTYPE_LOOP\n", LHNETPRIVATE_StrError());
+                       return -1;
+               }
                lhnetpacket_t *p;
                p = (lhnetpacket_t *)Z_Malloc(sizeof(*p) + contentlength);
                p->data = (void *)(p + 1);
@@ -1202,7 +1295,7 @@ int LHNET_Write(lhnetsocket_t *lhnetsocket, const void *content, int contentleng
        }
        else if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_INET4)
        {
-               value = sendto(lhnetsocket->inetsocket, (char *)content, contentlength, LHNET_SENDTO_FLAGS, (struct sockaddr *)&address->addr.in, sizeof(struct sockaddr_in));
+               value = sendto(lhnetsocket->inetsocket, (char *)content, contentlength, LHNET_SENDTO_FLAGS, address ? (struct sockaddr *)&address->addr.in : NULL, sizeof(struct sockaddr_in));
                if (value == -1)
                {
                        if (SOCKETERRNO == EWOULDBLOCK)
@@ -1213,7 +1306,7 @@ int LHNET_Write(lhnetsocket_t *lhnetsocket, const void *content, int contentleng
 #ifdef SUPPORTIPV6
        else if (lhnetsocket->address.addresstype == LHNETADDRESSTYPE_INET6)
        {
-               value = sendto(lhnetsocket->inetsocket, (char *)content, contentlength, 0, (struct sockaddr *)&address->addr.in6, sizeof(struct sockaddr_in6));
+               value = sendto(lhnetsocket->inetsocket, (char *)content, contentlength, 0, address ? (struct sockaddr *)&address->addr.in6 : NULL, sizeof(struct sockaddr_in6));
                if (value == -1)
                {
                        if (SOCKETERRNO == EWOULDBLOCK)
diff --git a/lhnet.h b/lhnet.h
index e66e5a5d8f9ca8068d037a00ae943a51edf573e1..817c0e65bbd41ce143124ffdfd561ced003a456c 100644 (file)
--- a/lhnet.h
+++ b/lhnet.h
@@ -22,7 +22,7 @@ typedef struct lhnetaddress_s
 lhnetaddress_t;
 
 int LHNETADDRESS_FromPort(lhnetaddress_t *address, lhnetaddresstype_t addresstype, int port);
-int LHNETADDRESS_FromString(lhnetaddress_t *address, const char *string, int defaultport);
+int LHNETADDRESS_FromString(lhnetaddress_t *address, const char *string, int defaultport); // NOT thread-safe! (namecache, namecacheposition, libc)
 int LHNETADDRESS_ToString(const lhnetaddress_t *address, char *string, int stringbuffersize, int includeport);
 int LHNETADDRESS_GetAddressType(const lhnetaddress_t *address);
 const char *LHNETADDRESS_GetInterfaceName(const lhnetaddress_t *address, char *ifname, size_t ifnamelength);
@@ -38,12 +38,13 @@ typedef struct lhnetsocket_s
 }
 lhnetsocket_t;
 
-void LHNET_Init(void);
+void LHNET_Init(void); // must be called before any other threads got spawned
 void LHNET_Shutdown(void);
-int LHNET_DefaultDSCP(int dscp); // < 0: query; >= 0: set (returns previous value)
-void LHNET_SleepUntilPacket_Microseconds(int microseconds);
-lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address);
-void LHNET_CloseSocket(lhnetsocket_t *lhnetsocket);
+int LHNET_DefaultDSCP(int dscp); // < 0: query; >= 0: set (returns previous value); NOT thread-safe but usually does not matter (lhnet_default_dscp)
+void LHNET_SleepUntilPacket_Microseconds(int microseconds); // must only be used from the main thread
+lhnetsocket_t *LHNET_OpenSocket(lhnetaddress_t *address, lhnetaddress_t *peeraddress, int use_tcp, int use_blocking, int register_for_select); // thread-safe ONLY if register_for_select is false (socketlist)
+lhnetsocket_t *LHNET_OpenSocket_Connectionless(lhnetaddress_t *address); // NOT thread-safe! (socketlist)
+void LHNET_CloseSocket(lhnetsocket_t *lhnetsocket); // thread-safe ONLY when the socket was created with register_for_select being false (socketlist)
 lhnetaddress_t *LHNET_AddressFromSocket(lhnetsocket_t *sock);
 int LHNET_Read(lhnetsocket_t *lhnetsocket, void *content, int maxcontentlength, lhnetaddress_t *address);
 int LHNET_Write(lhnetsocket_t *lhnetsocket, const void *content, int contentlength, const lhnetaddress_t *address);