/**
 * @interface_method_impl{TXSTRANSPORT,pfnTerm}
 */
static DECLCALLBACK(void) txsTcpTerm(void)
{
    /* Signal thread */
    if (RTCritSectIsInitialized(&g_TcpCritSect))
    {
        RTCritSectEnter(&g_TcpCritSect);
        g_fTcpStopConnecting = true;
        RTCritSectLeave(&g_TcpCritSect);
    }

    if (g_hThreadTcpConnect != NIL_RTTHREAD)
    {
        RTThreadUserSignal(g_hThreadTcpConnect);
        RTTcpClientCancelConnect(&g_pTcpConnectCancelCookie);
    }

    /* Shut down the server (will wake up thread). */
    if (g_pTcpServer)
    {
        Log(("txsTcpTerm: Destroying server...\n"));
        int rc = RTTcpServerDestroy(g_pTcpServer);
        if (RT_FAILURE(rc))
            RTMsgInfo("RTTcpServerDestroy failed in txsTcpTerm: %Rrc", rc);
        g_pTcpServer        = NULL;
    }

    /* Shut down client */
    if (g_hTcpClient != NIL_RTSOCKET)
    {
        if (g_fTcpClientFromServer)
        {
            Log(("txsTcpTerm: Disconnecting client...\n"));
            int rc = RTTcpServerDisconnectClient2(g_hTcpClient);
            if (RT_FAILURE(rc))
                RTMsgInfo("RTTcpServerDisconnectClient2(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc);
        }
        else
        {
            int rc = RTTcpClientClose(g_hTcpClient);
            if (RT_FAILURE(rc))
                RTMsgInfo("RTTcpClientClose(%RTsock) failed in txsTcpTerm: %Rrc", g_hTcpClient, rc);
        }
        g_hTcpClient        = NIL_RTSOCKET;
    }

    /* Clean up stashing. */
    RTMemFree(g_pbTcpStashed);
    g_pbTcpStashed          = NULL;
    g_cbTcpStashed          = 0;
    g_cbTcpStashedAlloced   = 0;

    /* Wait for the thread (they should've had some time to quit by now). */
    txsTcpConnectWaitOnThreads(15000);

    /* Finally, clean up the critical section. */
    if (RTCritSectIsInitialized(&g_TcpCritSect))
        RTCritSectDelete(&g_TcpCritSect);

    Log(("txsTcpTerm: done\n"));
}
/**
 * Receive thread loop.
 *
 * @returns VINF_SUCCESS
 * @param   hThreadSelf Thread handle to this thread.
 * @param   pvUser      User argument.
 */
static DECLCALLBACK(int) drvTCPListenLoop(RTTHREAD hThreadSelf, void *pvUser)
{
    RT_NOREF(hThreadSelf);
    PDRVTCP pThis = (PDRVTCP)pvUser;

    while (RT_LIKELY(!pThis->fShutdown))
    {
        RTSOCKET hTcpSockNew = NIL_RTSOCKET;
        int rc = RTTcpServerListen2(pThis->hTcpServ, &hTcpSockNew);
        if (RT_SUCCESS(rc))
        {
            if (pThis->hTcpSock != NIL_RTSOCKET)
            {
                LogRel(("DrvTCP%d: only single connection supported\n", pThis->pDrvIns->iInstance));
                RTTcpServerDisconnectClient2(hTcpSockNew);
            }
            else
            {
                pThis->hTcpSock = hTcpSockNew;
                /* Inform the poller about the new socket. */
                drvTcpPollerKick(pThis, DRVTCP_WAKEUP_REASON_NEW_CONNECTION);
            }
        }
    }

    return VINF_SUCCESS;
}
/**
 * Disconnects the current client.
 */
static void txsTcpDisconnectClient(void)
{
    int rc;
    if (g_fTcpClientFromServer)
        rc = RTTcpServerDisconnectClient2(g_hTcpClient);
    else
        rc = RTTcpClientClose(g_hTcpClient);
    AssertRCSuccess(rc);
    g_hTcpClient = NIL_RTSOCKET;
}
/**
 * Server mode connection thread.
 *
 * @returns iprt status code.
 * @param   hSelf           Thread handle. Ignored.
 * @param   pvUser          Ignored.
 */
static DECLCALLBACK(int) txsTcpServerConnectThread(RTTHREAD hSelf, void *pvUser)
{
    RTSOCKET hTcpClient;
    int rc = RTTcpServerListen2(g_pTcpServer, &hTcpClient);
    Log(("txsTcpConnectServerThread: RTTcpServerListen2 -> %Rrc\n", rc));
    if (RT_SUCCESS(rc))
    {
        hTcpClient = txsTcpSetClient(hTcpClient, true /*fFromServer*/);
        RTTcpServerDisconnectClient2(hTcpClient);
    }

    return rc;
}
/** @interface_method_impl{PDMISTREAM,pfnRead} */
static DECLCALLBACK(int) drvTcpRead(PPDMISTREAM pInterface, void *pvBuf, size_t *pcbRead)
{
    int rc = VINF_SUCCESS;
    PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);
    LogFlow(("%s: pvBuf=%p *pcbRead=%#x (%s)\n", __FUNCTION__, pvBuf, *pcbRead, pThis->pszLocation));

    Assert(pvBuf);

    if (pThis->hTcpSock != NIL_RTSOCKET)
    {
        size_t cbRead;
        size_t cbBuf = *pcbRead;
        rc = RTSocketReadNB(pThis->hTcpSock, pvBuf, cbBuf, &cbRead);
        if (RT_SUCCESS(rc))
        {
            if (!cbRead && rc != VINF_TRY_AGAIN)
            {
                rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
                AssertRC(rc);

                if (pThis->fIsServer)
                    RTTcpServerDisconnectClient2(pThis->hTcpSock);
                else
                    RTSocketClose(pThis->hTcpSock);
                pThis->hTcpSock = NIL_RTSOCKET;
                pThis->fTcpSockInPollSet = false;
                rc = VINF_SUCCESS;
            }
            *pcbRead = cbRead;
        }
    }
    else
    {
        RTThreadSleep(100);
        *pcbRead = 0;
    }

    LogFlow(("%s: *pcbRead=%zu returns %Rrc\n", __FUNCTION__, *pcbRead, rc));
    return rc;
}
/** @interface_method_impl{PDMISTREAM,pfnPoll} */
static DECLCALLBACK(int) drvTcpPoll(PPDMISTREAM pInterface, uint32_t fEvts, uint32_t *pfEvts, RTMSINTERVAL cMillies)
{
    int rc = VINF_SUCCESS;
    PDRVTCP pThis = RT_FROM_MEMBER(pInterface, DRVTCP, IStream);

    if (pThis->hTcpSock != NIL_RTSOCKET)
    {
        if (!pThis->fTcpSockInPollSet)
        {
            rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
                                    fEvts, DRVTCP_POLLSET_ID_SOCKET);
            if (RT_SUCCESS(rc))
            {
                pThis->fTcpSockInPollSet = true;
                pThis->fXmitBufFull = false;
            }
        }
        else
        {
            /* Always include error event. */
            fEvts |= RTPOLL_EVT_ERROR;
            rc = RTPollSetEventsChange(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET, fEvts);
            AssertRC(rc);
        }
    }

    if (RT_SUCCESS(rc))
    {
        while (RT_SUCCESS(rc))
        {
            uint32_t fEvtsRecv = 0;
            uint32_t idHnd = 0;

            /*
             * Just check for data available to be read if the send buffer wasn't full till now and
             * the caller wants to check whether writing is possible with the event set.
             *
             * On Windows the write event is only posted after a send operation returned
             * WSAEWOULDBLOCK. So without this we would block in the poll call below waiting
             * for an event which would never happen if the buffer has space left.
             */
            if (   (fEvts & RTPOLL_EVT_WRITE)
                && !pThis->fXmitBufFull
                && pThis->fTcpSockInPollSet)
                cMillies = 0;

            rc = RTPoll(pThis->hPollSet, cMillies, &fEvtsRecv, &idHnd);
            if (RT_SUCCESS(rc))
            {
                if (idHnd == DRVTCP_POLLSET_ID_WAKEUP)
                {
                    /* We got woken up, drain the pipe and return. */
                    uint8_t bReason;
                    size_t cbRead = 0;
                    rc = RTPipeRead(pThis->hPipeWakeR, &bReason, 1, &cbRead);
                    AssertRC(rc);

                    if (bReason == DRVTCP_WAKEUP_REASON_EXTERNAL)
                        rc = VERR_INTERRUPTED;
                    else if (bReason == DRVTCP_WAKEUP_REASON_NEW_CONNECTION)
                    {
                        Assert(!pThis->fTcpSockInPollSet);
                        rc = RTPollSetAddSocket(pThis->hPollSet, pThis->hTcpSock,
                                                fEvts, DRVTCP_POLLSET_ID_SOCKET);
                        if (RT_SUCCESS(rc))
                            pThis->fTcpSockInPollSet = true;
                    }
                    else
                        AssertMsgFailed(("Unknown wakeup reason in pipe %u\n", bReason));
                }
                else
                {
                    Assert(idHnd == DRVTCP_POLLSET_ID_SOCKET);

                    /* On error we close the socket here. */
                    if (fEvtsRecv & RTPOLL_EVT_ERROR)
                    {
                        rc = RTPollSetRemove(pThis->hPollSet, DRVTCP_POLLSET_ID_SOCKET);
                        AssertRC(rc);

                        if (pThis->fIsServer)
                            RTTcpServerDisconnectClient2(pThis->hTcpSock);
                        else
                            RTSocketClose(pThis->hTcpSock);
                        pThis->hTcpSock = NIL_RTSOCKET;
                        pThis->fTcpSockInPollSet = false;
                        /* Continue with polling. */
                    }
                    else
                    {
                        if (fEvtsRecv & RTPOLL_EVT_WRITE)
                            pThis->fXmitBufFull = false;
                        else if (!pThis->fXmitBufFull)
                            fEvtsRecv |= RTPOLL_EVT_WRITE;
                        *pfEvts = fEvtsRecv;
                        break;
                    }
                }
            }
            else if (   rc == VERR_TIMEOUT
                     && !pThis->fXmitBufFull)
            {
                *pfEvts = RTPOLL_EVT_WRITE;
                rc = VINF_SUCCESS;
                break;
            }
        }
    }

    return rc;
}