EPICS Home

Experimental Physics and Industrial Control System


 
2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024  Index 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: [Merge] ~info-martin-konrad/epics-base:fix-log-issues into epics-base:3.15
From: Martin Konrad via Core-talk <[email protected]>
To: [email protected]
Date: Wed, 13 Nov 2019 19:15:38 -0000
Martin Konrad has proposed merging ~info-martin-konrad/epics-base:fix-log-issues into epics-base:3.15.

Requested reviews:
  EPICS Core Developers (epics-core)

For more details, see:
https://code.launchpad.net/~info-martin-konrad/epics-base/+git/epics-base/+merge/375509

Fix lp:1841608 + some other logging issues (cherry-picked from 7.0 branch).
-- 
Your team EPICS Core Developers is requested to review the proposed merge of ~info-martin-konrad/epics-base:fix-log-issues into epics-base:3.15.
diff --git a/src/ioc/misc/dbCore.dbd b/src/ioc/misc/dbCore.dbd
index 9d5ae94..898bb59 100644
--- a/src/ioc/misc/dbCore.dbd
+++ b/src/ioc/misc/dbCore.dbd
@@ -25,3 +25,6 @@ variable(callbackParallelThreadsDefault,int)
 
 # Real-time operation
 variable(dbThreadRealtimeLock,int)
+
+# show logClient network activity
+variable(logClientDebug,int)
diff --git a/src/libCom/log/iocLog.c b/src/libCom/log/iocLog.c
index e62da20..ba78041 100644
--- a/src/libCom/log/iocLog.c
+++ b/src/libCom/log/iocLog.c
@@ -18,8 +18,10 @@
 
 #define epicsExportSharedSymbols
 #include "envDefs.h"
+#include "errlog.h"
 #include "logClient.h"
 #include "iocLog.h"
+#include "epicsExit.h"
 
 int iocLogDisable = 0;
 
@@ -75,6 +77,24 @@ void epicsShareAPI epicsShareAPI iocLogFlush (void)
 }
 
 /*
+ * logClientSendMessage ()
+ */
+static void logClientSendMessage ( logClientId id, const char * message )
+{
+    if ( !iocLogDisable ) {
+        logClientSend (id, message);
+    }
+}
+
+/*
+ * iocLogClientDestroy()
+ */
+static void iocLogClientDestroy (logClientId id)
+{
+    errlogRemoveListeners (logClientSendMessage, id);
+}
+
+/*
  *  iocLogClientInit()
  */
 static logClientId iocLogClientInit (void)
@@ -89,6 +109,10 @@ static logClientId iocLogClientInit (void)
         return NULL;
     }
     id = logClientCreate (addr, port);
+    if (id != NULL) {
+        errlogAddListener (logClientSendMessage, id);
+        epicsAtExit (iocLogClientDestroy, id);
+    }
     return id;
 }
 
@@ -135,3 +159,4 @@ logClientId epicsShareAPI logClientInit (void)
 {
     return iocLogClientInit ();
 }
+
diff --git a/src/libCom/log/logClient.c b/src/libCom/log/logClient.c
index 99ee671..9a09ef7 100644
--- a/src/libCom/log/logClient.c
+++ b/src/libCom/log/logClient.c
@@ -21,11 +21,11 @@
 #include <string.h>
 #include <stdio.h>
 
+#define EPICS_PRIVATE_API
 #define epicsExportSharedSymbols
 #include "dbDefs.h"
 #include "epicsEvent.h"
 #include "iocLog.h"
-#include "errlog.h"
 #include "epicsMutex.h"
 #include "epicsThread.h"
 #include "epicsTime.h"
@@ -33,9 +33,13 @@
 #include "epicsAssert.h"
 #include "epicsExit.h"
 #include "epicsSignal.h"
+#include "epicsExport.h"
 
 #include "logClient.h"
 
+int logClientDebug = 0;
+epicsExportAddress (int, logClientDebug);
+
 typedef struct {
     char                msgBuf[0x4000];
     struct sockaddr_in  addr;
@@ -44,8 +48,10 @@ typedef struct {
     SOCKET              sock;
     epicsThreadId       restartThreadId;
     epicsEventId        stateChangeNotify;
+    epicsEventId        shutdownNotify;
     unsigned            connectCount;
     unsigned            nextMsgIndex;
+    unsigned            backlog;
     unsigned            connected;
     unsigned            shutdown;
     unsigned            shutdownConfirm;
@@ -53,7 +59,6 @@ typedef struct {
 } logClient;
 
 static const double      LOG_RESTART_DELAY = 5.0; /* sec */
-static const double      LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT = 5.0; /* sec */
 static const double      LOG_SERVER_SHUTDOWN_TIMEOUT = 30.0; /* sec */
 
 /*
@@ -66,10 +71,10 @@ static char* logClientPrefix = NULL;
  */
 static void logClientClose ( logClient *pClient )
 {
-#   ifdef DEBUG
+    if (logClientDebug) {
         fprintf (stderr, "log client: lingering for connection close...");
         fflush (stderr);
-#   endif
+    }
 
     /*
      * mutex on
@@ -84,8 +89,6 @@ static void logClientClose ( logClient *pClient )
         pClient->sock = INVALID_SOCKET;
     }
 
-    pClient->nextMsgIndex = 0u;
-    memset ( pClient->msgBuf, '\0', sizeof ( pClient->msgBuf ) );
     pClient->connected = 0u;
 
     /*
@@ -93,9 +96,8 @@ static void logClientClose ( logClient *pClient )
      */
     epicsMutexUnlock (pClient->mutex);
 
-#   ifdef DEBUG
+    if (logClientDebug)
         fprintf (stderr, "done\n");
-#   endif
 }
 
 /*
@@ -113,6 +115,7 @@ static void logClientDestroy (logClientId id)
     epicsMutexMustLock ( pClient->mutex );
     pClient->shutdown = 1u;
     epicsMutexUnlock ( pClient->mutex );
+    epicsEventSignal ( pClient->shutdownNotify );
 
     /* unblock log client thread blocking in send() or connect() */
     interruptInfo =
@@ -154,13 +157,11 @@ static void logClientDestroy (logClientId id)
         return;
     }
 
-    errlogRemoveListeners ( logClientSendMessage, (void *) pClient );
-
     logClientClose ( pClient );
 
     epicsMutexDestroy ( pClient->mutex );
-   
     epicsEventDestroy ( pClient->stateChangeNotify );
+    epicsEventDestroy ( pClient->shutdownNotify );
 
     free ( pClient );
 }
@@ -176,61 +177,26 @@ static void sendMessageChunk(logClient * pClient, const char * message) {
         unsigned msgBufBytesLeft = 
             sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex;
 
-        if ( strSize > msgBufBytesLeft ) {
-            int status;
-
-            if ( ! pClient->connected ) {
-                break;
-            }
-
-            if ( msgBufBytesLeft > 0u ) {
-                memcpy ( & pClient->msgBuf[pClient->nextMsgIndex],
-                    message, msgBufBytesLeft );
-                pClient->nextMsgIndex += msgBufBytesLeft;
-                strSize -= msgBufBytesLeft;
-                message += msgBufBytesLeft;
-            }
-
-            status = send ( pClient->sock, pClient->msgBuf, 
-                pClient->nextMsgIndex, 0 );
-            if ( status > 0 ) {
-                unsigned nSent = (unsigned) status;
-                if ( nSent < pClient->nextMsgIndex ) {
-                    unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent;
-                    memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], 
-                        newNextMsgIndex );
-                    pClient->nextMsgIndex = newNextMsgIndex;
-                }
-                else {
-                    pClient->nextMsgIndex = 0u;
-                }
-            }
-            else {
-                if ( ! pClient->shutdown ) {
-                    char sockErrBuf[64];
-                    if ( status ) {
-                        epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
-                    }
-                    else {
-                        strcpy ( sockErrBuf, "server initiated disconnect" );
-                    }
-                    fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", 
-                        pClient->name, sockErrBuf );
-                }
-                logClientClose ( pClient );
-                break;
-            }
+        if ( msgBufBytesLeft < strSize && pClient->nextMsgIndex != 0u && pClient->connected)
+        {
+            /* buffer is full, thus flush it */
+            logClientFlush ( pClient );
+            msgBufBytesLeft = sizeof ( pClient->msgBuf ) - pClient->nextMsgIndex;
         }
-        else {
-            memcpy ( & pClient->msgBuf[pClient->nextMsgIndex],
-                message, strSize );
-            pClient->nextMsgIndex += strSize;
+        if ( msgBufBytesLeft == 0u ) {
+            fprintf ( stderr, "log client: messages to \"%s\" are lost\n",
+                pClient->name );
             break;
         }
+        if ( msgBufBytesLeft > strSize) msgBufBytesLeft = strSize;
+        memcpy ( & pClient->msgBuf[pClient->nextMsgIndex],
+            message, msgBufBytesLeft );
+        pClient->nextMsgIndex += msgBufBytesLeft;
+        strSize -= msgBufBytesLeft;
+        message += msgBufBytesLeft;
     }
 }
 
-
 /* 
  * logClientSend ()
  */
@@ -255,43 +221,54 @@ void epicsShareAPI logClientSend ( logClientId id, const char * message )
 
 void epicsShareAPI logClientFlush ( logClientId id )
 {
+    unsigned nSent;
+    int status = 0;
+
     logClient * pClient = ( logClient * ) id;
 
-    if ( ! pClient ) {
+    if ( ! pClient || ! pClient->connected ) {
         return;
     }
 
     epicsMutexMustLock ( pClient->mutex );
 
-    while ( pClient->nextMsgIndex && pClient->connected ) {
-        int status = send ( pClient->sock, pClient->msgBuf, 
-            pClient->nextMsgIndex, 0 );
-        if ( status > 0 ) {
-            unsigned nSent = (unsigned) status;
-            if ( nSent < pClient->nextMsgIndex ) {
-                unsigned newNextMsgIndex = pClient->nextMsgIndex - nSent;
-                memmove ( pClient->msgBuf, & pClient->msgBuf[nSent], 
-                    newNextMsgIndex );
-                pClient->nextMsgIndex = newNextMsgIndex;
-            }
-            else {
-                pClient->nextMsgIndex = 0u;
-            }
+    nSent = pClient->backlog;
+    while ( nSent < pClient->nextMsgIndex && pClient->connected ) {
+        status = send ( pClient->sock, pClient->msgBuf + nSent,
+            pClient->nextMsgIndex - nSent, 0 );
+        if ( status < 0 ) break;
+        nSent += status;
+    }
+
+    if ( pClient->backlog > 0 && status >= 0 )
+    {
+        /* On Linux send 0 bytes can detect EPIPE */
+        /* NOOP on Windows, fails on vxWorks */
+        errno = 0;
+        status = send ( pClient->sock, NULL, 0, 0 );
+        if (!(errno == ECONNRESET || errno == EPIPE)) status = 0;
+    }
+
+    if ( status < 0 ) {
+        if ( ! pClient->shutdown ) {
+            char sockErrBuf[128];
+            epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
+            fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", 
+                pClient->name, sockErrBuf );
         }
-        else {
-            if ( ! pClient->shutdown ) {
-                char sockErrBuf[64];
-                if ( status ) {
-                    epicsSocketConvertErrnoToString ( sockErrBuf, sizeof ( sockErrBuf ) );
-                }
-                else {
-                    strcpy ( sockErrBuf, "server initiated disconnect" );
-                }
-                fprintf ( stderr, "log client: lost contact with log server at \"%s\" because \"%s\"\n", 
-                    pClient->name, sockErrBuf );
-            }
-            logClientClose ( pClient );
-            break;
+        pClient->backlog = 0;
+        logClientClose ( pClient );
+    }
+    else if ( nSent > 0 && pClient->nextMsgIndex > 0 ) {
+        int backlog = epicsSocketUnsentCount ( pClient->sock );
+        if (backlog >= 0) {
+            pClient->backlog = backlog;
+            nSent -= backlog;
+        }
+        pClient->nextMsgIndex -= nSent;
+        if ( nSent > 0 && pClient->nextMsgIndex > 0 ) {
+            memmove ( pClient->msgBuf, & pClient->msgBuf[nSent],
+                pClient->nextMsgIndex );
         }
     }
     epicsMutexUnlock ( pClient->mutex );
@@ -302,10 +279,10 @@ void epicsShareAPI logClientFlush ( logClientId id )
  */
 static void logClientMakeSock (logClient *pClient)
 {
-    
-#   ifdef DEBUG
+    if (logClientDebug) {
         fprintf (stderr, "log client: creating socket...");
-#   endif
+        fflush (stderr);
+    }
 
     epicsMutexMustLock (pClient->mutex);
    
@@ -314,7 +291,7 @@ static void logClientMakeSock (logClient *pClient)
      */
     pClient->sock = epicsSocketCreate ( AF_INET, SOCK_STREAM, 0 );
     if ( pClient->sock == INVALID_SOCKET ) {
-        char sockErrBuf[64];
+        char sockErrBuf[128];
         epicsSocketConvertErrnoToString ( 
             sockErrBuf, sizeof ( sockErrBuf ) );
         fprintf ( stderr, "log client: no socket error %s\n", 
@@ -323,10 +300,8 @@ static void logClientMakeSock (logClient *pClient)
     
     epicsMutexUnlock (pClient->mutex);
 
-#   ifdef DEBUG
+    if (logClientDebug)
         fprintf (stderr, "done\n");
-#   endif
-
 }
 
 /*
@@ -366,7 +341,7 @@ static void logClientConnect (logClient *pClient)
             }
             else {
                 if ( pClient->connFailStatus != errnoCpy && ! pClient->shutdown ) {
-                    char sockErrBuf[64];
+                    char sockErrBuf[128];
                     epicsSocketConvertErrnoToString (
                         sockErrBuf, sizeof ( sockErrBuf ) );
                     fprintf (stderr,
@@ -392,7 +367,7 @@ static void logClientConnect (logClient *pClient)
     optval = TRUE;
     status = setsockopt (pClient->sock, SOL_SOCKET, SO_KEEPALIVE, (char *)&optval, sizeof(optval));
     if (status<0) {
-        char sockErrBuf[64];
+        char sockErrBuf[128];
         epicsSocketConvertErrnoToString ( 
             sockErrBuf, sizeof ( sockErrBuf ) );
         fprintf (stderr, "log client: unable to enable keepalive option because \"%s\"\n", sockErrBuf);
@@ -404,11 +379,11 @@ static void logClientConnect (logClient *pClient)
      */
     status = shutdown (pClient->sock, SHUT_RD);
     if (status < 0) {
-        char sockErrBuf[64];
+        char sockErrBuf[128];
         epicsSocketConvertErrnoToString ( 
             sockErrBuf, sizeof ( sockErrBuf ) );
-        fprintf (stderr, "%s:%d shutdown(%d,SHUT_RD) error was \"%s\"\n", 
-            __FILE__, __LINE__, pClient->sock, sockErrBuf);
+        fprintf (stderr, "%s:%d shutdown(sock,SHUT_RD) error was \"%s\"\n", 
+            __FILE__, __LINE__, sockErrBuf);
         /* not fatal (although it shouldn't happen) */
     }
 
@@ -425,7 +400,7 @@ static void logClientConnect (logClient *pClient)
         lingerval.l_linger = 60*5; 
         status = setsockopt (pClient->sock, SOL_SOCKET, SO_LINGER, (char *) &lingerval, sizeof(lingerval));
         if (status<0) {
-            char sockErrBuf[64];
+            char sockErrBuf[128];
             epicsSocketConvertErrnoToString ( 
                 sockErrBuf, sizeof ( sockErrBuf ) );
             fprintf (stderr, "log client: unable to set linger options because \"%s\"\n", sockErrBuf);
@@ -457,14 +432,10 @@ static void logClientRestart ( logClientId id )
 
         epicsMutexUnlock ( pClient->mutex );
 
-        if ( isConn ) {
-            logClientFlush ( pClient );
-        }
-        else {
-            logClientConnect ( pClient );
-        }
-        
-        epicsThreadSleep ( LOG_RESTART_DELAY );
+        if ( ! isConn ) logClientConnect ( pClient );
+        logClientFlush ( pClient );
+
+        epicsEventWaitWithTimeout ( pClient->shutdownNotify, LOG_RESTART_DELAY);
 
         epicsMutexMustLock ( pClient->mutex );
     }
@@ -480,9 +451,7 @@ static void logClientRestart ( logClientId id )
 logClientId epicsShareAPI logClientCreate (
     struct in_addr server_addr, unsigned short server_port)
 {
-    epicsTimeStamp begin, current;
     logClient *pClient;
-    double diff;
 
     pClient = calloc (1, sizeof (*pClient));
     if (pClient==NULL) {
@@ -507,14 +476,22 @@ logClientId epicsShareAPI logClientCreate (
     pClient->shutdownConfirm = 0;
 
     epicsAtExit (logClientDestroy, (void*) pClient);
-    
+
     pClient->stateChangeNotify = epicsEventCreate (epicsEventEmpty);
     if ( ! pClient->stateChangeNotify ) {
         epicsMutexDestroy ( pClient->mutex );
         free ( pClient );
         return NULL;
     }
-   
+
+    pClient->shutdownNotify = epicsEventCreate (epicsEventEmpty);
+    if ( ! pClient->shutdownNotify ) {
+        epicsMutexDestroy ( pClient->mutex );
+        epicsEventDestroy ( pClient->stateChangeNotify );
+        free ( pClient );
+        return NULL;
+    }
+
     pClient->restartThreadId = epicsThreadCreate (
         "logRestart", epicsThreadPriorityLow, 
         epicsThreadGetStackSize(epicsThreadStackSmall),
@@ -522,35 +499,12 @@ logClientId epicsShareAPI logClientCreate (
     if ( pClient->restartThreadId == NULL ) {
         epicsMutexDestroy ( pClient->mutex );
         epicsEventDestroy ( pClient->stateChangeNotify );
+        epicsEventDestroy ( pClient->shutdownNotify );
         free (pClient);
         fprintf(stderr, "log client: unable to start log client connection watch dog thread\n");
         return NULL;
     }
 
-    /*
-     * attempt to synchronize with circuit connect
-     */
-    epicsTimeGetCurrent ( & begin );
-    epicsMutexMustLock ( pClient->mutex );
-    do {
-        epicsMutexUnlock ( pClient->mutex );
-        epicsEventWaitWithTimeout ( 
-            pClient->stateChangeNotify, 
-            LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT / 10.0 ); 
-        epicsTimeGetCurrent ( & current );
-        diff = epicsTimeDiffInSeconds ( & current, & begin );
-        epicsMutexMustLock ( pClient->mutex );
-    }
-    while ( ! pClient->connected && diff < LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT );
-    epicsMutexUnlock ( pClient->mutex );
-
-    if ( ! pClient->connected ) {
-        fprintf (stderr, "log client create: timed out synchronizing with circuit connect to \"%s\" after %.1f seconds\n",
-            pClient->name, LOG_SERVER_CREATE_CONNECT_SYNC_TIMEOUT );
-    }
-        
-    errlogAddListener ( logClientSendMessage, (void *) pClient );
-
     return (void *) pClient;
 }
 
@@ -568,24 +522,21 @@ void epicsShareAPI logClientShow (logClientId id, unsigned level)
         printf ("log client: disconnected from log server at \"%s\"\n", pClient->name);
     }
 
-    if (level>1) {
-        printf ("log client: sock=%s, connect cycles = %u\n",
-            pClient->sock==INVALID_SOCKET?"INVALID":"OK",
-            pClient->connectCount);
-    }
-
     if (logClientPrefix) {
         printf ("log client: prefix is \"%s\"\n", logClientPrefix);
     }
-}
 
-/*
- * logClientSendMessage (); deprecated
- */
-void logClientSendMessage ( logClientId id, const char * message )
-{
-    if ( !iocLogDisable ) {
-        logClientSend (id, message);
+    if (level>0) {
+        printf ("log client: sock %s, connect cycles = %u\n",
+            pClient->sock==INVALID_SOCKET?"INVALID":"OK",
+            pClient->connectCount);
+    }
+    if (level>1) {
+        printf ("log client: %u bytes in buffer\n", pClient->nextMsgIndex);
+        if (pClient->nextMsgIndex)
+            printf("-------------------------\n"
+                "%.*s-------------------------\n",
+                (int)(pClient->nextMsgIndex), pClient->msgBuf);
     }
 }
 
diff --git a/src/libCom/log/logClient.h b/src/libCom/log/logClient.h
index 1797bbb..3b3f63a 100644
--- a/src/libCom/log/logClient.h
+++ b/src/libCom/log/logClient.h
@@ -38,7 +38,6 @@ epicsShareFunc void epicsShareAPI iocLogPrefix(const char* prefix);
 /* deprecated interface; retained for backward compatibility */
 /* note: implementations are in iocLog.c, not logClient.c */
 epicsShareFunc logClientId epicsShareAPI logClientInit (void);
-epicsShareFunc void logClientSendMessage (logClientId id, const char *message);
 
 #ifdef __cplusplus
 }
diff --git a/src/libCom/osi/Makefile b/src/libCom/osi/Makefile
index e05aec3..00685d8 100644
--- a/src/libCom/osi/Makefile
+++ b/src/libCom/osi/Makefile
@@ -86,6 +86,7 @@ endif
 
 Com_SRCS += osdSock.c
 Com_SRCS += osdSockAddrReuse.cpp
+Com_SRCS += osdSockUnsentCount.c
 Com_SRCS += osiSock.c
 Com_SRCS += systemCallIntMech.cpp
 Com_SRCS += epicsSocketConvertErrnoToString.cpp
diff --git a/src/libCom/osi/os/Darwin/osdSockUnsentCount.c b/src/libCom/osi/os/Darwin/osdSockUnsentCount.c
new file mode 100644
index 0000000..20bd82b
--- /dev/null
+++ b/src/libCom/osi/os/Darwin/osdSockUnsentCount.c
@@ -0,0 +1,19 @@
+/*************************************************************************\
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#define EPICS_PRIVATE_API
+#include "osiSock.h"
+
+/*
+ * epicsSocketUnsentCount ()
+ * See https://www.unix.com/man-page/osx/2/setsockopt
+ */
+int epicsSocketUnsentCount(SOCKET sock) {
+    int unsent;
+    socklen_t len = sizeof(unsent);
+    if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent, &len) == 0)
+        return unsent;
+    return -1;
+}
diff --git a/src/libCom/osi/os/Linux/osdSockUnsentCount.c b/src/libCom/osi/os/Linux/osdSockUnsentCount.c
new file mode 100644
index 0000000..3c0a8f9
--- /dev/null
+++ b/src/libCom/osi/os/Linux/osdSockUnsentCount.c
@@ -0,0 +1,19 @@
+/*************************************************************************\
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#include <linux/sockios.h>
+#define EPICS_PRIVATE_API
+#include "osiSock.h"
+
+/*
+ * epicsSocketUnsentCount ()
+ * See https://linux.die.net/man/7/tcp
+ */
+int epicsSocketUnsentCount(SOCKET sock) {
+    int unsent;
+    if (ioctl(sock, SIOCOUTQ, &unsent) == 0)
+        return unsent;
+    return -1;
+}
diff --git a/src/libCom/osi/os/WIN32/osdSockUnsentCount.c b/src/libCom/osi/os/WIN32/osdSockUnsentCount.c
new file mode 100644
index 0000000..fe68ead
--- /dev/null
+++ b/src/libCom/osi/os/WIN32/osdSockUnsentCount.c
@@ -0,0 +1,26 @@
+/*************************************************************************\
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#define epicsExportSharedSymbols
+#define EPICS_PRIVATE_API
+#include "osiSock.h"
+#include <mstcpip.h>
+
+/*
+ * epicsSocketUnsentCount ()
+ * See https://docs.microsoft.com/en-us/windows/win32/api/mstcpip/ns-mstcpip-tcp_info_v0
+ */
+int epicsSocketUnsentCount(SOCKET sock) {
+#if defined (_WIN32) && WINVER >= _WIN32_WINNT_WIN10
+/* Windows 10 Version 1703 / Server 2016 */
+    DWORD infoVersion = 0, bytesReturned;
+    TCP_INFO_v0 tcpInfo;
+    int status;
+    if ((status = WSAIoctl(sock, SIO_TCP_INFO, &infoVersion, sizeof(infoVersion),
+        &tcpInfo, sizeof(tcpInfo), &bytesReturned, NULL, NULL)) == 0)
+        return tcpInfo.BytesInFlight;
+#endif
+    return -1;
+}
diff --git a/src/libCom/osi/os/default/osdSockUnsentCount.c b/src/libCom/osi/os/default/osdSockUnsentCount.c
new file mode 100644
index 0000000..ef01e9b
--- /dev/null
+++ b/src/libCom/osi/os/default/osdSockUnsentCount.c
@@ -0,0 +1,15 @@
+/*************************************************************************\
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#define EPICS_PRIVATE_API
+#include "osiSock.h"
+
+/*
+ * epicsSocketUnsentCount ()
+ */
+int epicsSocketUnsentCount(SOCKET sock) {
+    /* not implemented */
+    return -1;
+}
diff --git a/src/libCom/osi/os/iOS/osdSockUnsentCount.c b/src/libCom/osi/os/iOS/osdSockUnsentCount.c
new file mode 100644
index 0000000..20bd82b
--- /dev/null
+++ b/src/libCom/osi/os/iOS/osdSockUnsentCount.c
@@ -0,0 +1,19 @@
+/*************************************************************************\
+* EPICS BASE is distributed subject to a Software License Agreement found
+* in file LICENSE that is included with this distribution.
+\*************************************************************************/
+
+#define EPICS_PRIVATE_API
+#include "osiSock.h"
+
+/*
+ * epicsSocketUnsentCount ()
+ * See https://www.unix.com/man-page/osx/2/setsockopt
+ */
+int epicsSocketUnsentCount(SOCKET sock) {
+    int unsent;
+    socklen_t len = sizeof(unsent);
+    if (getsockopt(sock, SOL_SOCKET, SO_NWRITE, &unsent, &len) == 0)
+        return unsent;
+    return -1;
+}
diff --git a/src/libCom/osi/osiSock.h b/src/libCom/osi/osiSock.h
index 061619e..6e3b053 100644
--- a/src/libCom/osi/osiSock.h
+++ b/src/libCom/osi/osiSock.h
@@ -52,6 +52,14 @@ enum epicsSocketSystemCallInterruptMechanismQueryInfo {
 epicsShareFunc enum epicsSocketSystemCallInterruptMechanismQueryInfo 
         epicsSocketSystemCallInterruptMechanismQuery ();
 
+#ifdef EPICS_PRIVATE_API
+/*
+ * Some systems (e.g Linux and Windows 10) allow to check the amount
+ * of unsent data in the output queue.
+ * Returns -1 if the information is not available.
+ */
+epicsShareFunc int epicsSocketUnsentCount(SOCKET sock);
+#endif
 
 /*
  * convert socket address to ASCII in this order

Navigate by Date:
Prev: [Bug 1841608] Re: logClient falsely sends error logs on all connections Martin Konrad via Core-talk
Next: [Merge] ~info-martin-konrad/epics-base:simplify-compiler-specific-macros into epics-base:3.15 Martin Konrad via Core-talk
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Build failed: epics-base base-integration-352 AppVeyor via Core-talk
Next: [Merge] ~info-martin-konrad/epics-base:simplify-compiler-specific-macros into epics-base:7.0 Martin Konrad via Core-talk
Index: 2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  2012  2013  2014  2015  2016  2017  2018  <20192020  2021  2022  2023  2024