/**
 * @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"));
}
/**
 * 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;
}
Console::teleporterSrcThreadWrapper(RTTHREAD hThread, void *pvUser)
{
    TeleporterStateSrc *pState = (TeleporterStateSrc *)pvUser;

    /*
     * Console::teleporterSrc does the work, we just grab onto the VM handle
     * and do the cleanups afterwards.
     */
    SafeVMPtr ptrVM(pState->mptrConsole);
    HRESULT hrc = ptrVM.rc();

    if (SUCCEEDED(hrc))
        hrc = pState->mptrConsole->teleporterSrc(pState);

    /* Close the connection ASAP on so that the other side can complete. */
    if (pState->mhSocket != NIL_RTSOCKET)
    {
        RTTcpClientClose(pState->mhSocket);
        pState->mhSocket = NIL_RTSOCKET;
    }

    /* Aaarg! setMachineState trashes error info on Windows, so we have to
       complete things here on failure instead of right before cleanup. */
    if (FAILED(hrc))
        pState->mptrProgress->notifyComplete(hrc);

    /* We can no longer be canceled (success), or it doesn't matter any longer (failure). */
    pState->mptrProgress->setCancelCallback(NULL, NULL);

    /*
     * Write lock the console before resetting mptrCancelableProgress and
     * fixing the state.
     */
    AutoWriteLock autoLock(pState->mptrConsole COMMA_LOCKVAL_SRC_POS);
    pState->mptrConsole->mptrCancelableProgress.setNull();

    VMSTATE const        enmVMState      = VMR3GetStateU(pState->mpUVM);
    MachineState_T const enmMachineState = pState->mptrConsole->mMachineState;
    if (SUCCEEDED(hrc))
    {
        /*
         * Automatically shut down the VM on success.
         *
         * Note! We have to release the VM caller object or we'll deadlock in
         *       powerDown.
         */
        AssertLogRelMsg(enmVMState == VMSTATE_SUSPENDED, ("%s\n", VMR3GetStateName(enmVMState)));
        AssertLogRelMsg(enmMachineState == MachineState_TeleportingPausedVM, ("%s\n", Global::stringifyMachineState(enmMachineState)));

        ptrVM.release();

        pState->mptrConsole->mVMIsAlreadyPoweringOff = true; /* (Make sure we stick in the TeleportingPausedVM state.) */
        hrc = pState->mptrConsole->powerDown();
        pState->mptrConsole->mVMIsAlreadyPoweringOff = false;

        pState->mptrProgress->notifyComplete(hrc);
    }
    else
    {
        /*
         * Work the state machinery on failure.
         *
         * If the state is no longer 'Teleporting*', some other operation has
         * canceled us and there is nothing we need to do here.  In all other
         * cases, we've failed one way or another.
         */
        if (   enmMachineState == MachineState_Teleporting
                || enmMachineState == MachineState_TeleportingPausedVM
           )
        {
            if (pState->mfUnlockedMedia)
            {
                ErrorInfoKeeper Oak;
                HRESULT hrc2 = pState->mptrConsole->mControl->LockMedia();
                if (FAILED(hrc2))
                {
                    uint64_t StartMS = RTTimeMilliTS();
                    do
                    {
                        RTThreadSleep(2);
                        hrc2 = pState->mptrConsole->mControl->LockMedia();
                    } while (   FAILED(hrc2)
                                && RTTimeMilliTS() - StartMS < 2000);
                }
                if (SUCCEEDED(hrc2))
                    pState->mfUnlockedMedia = true;
                else
                    LogRel(("FATAL ERROR: Failed to re-take the media locks. hrc2=%Rhrc\n", hrc2));
            }

            switch (enmVMState)
            {
            case VMSTATE_RUNNING:
            case VMSTATE_RUNNING_LS:
            case VMSTATE_DEBUGGING:
            case VMSTATE_DEBUGGING_LS:
            case VMSTATE_POWERING_OFF:
            case VMSTATE_POWERING_OFF_LS:
            case VMSTATE_RESETTING:
            case VMSTATE_RESETTING_LS:
                Assert(!pState->mfSuspendedByUs);
                Assert(!pState->mfUnlockedMedia);
                pState->mptrConsole->setMachineState(MachineState_Running);
                break;

            case VMSTATE_GURU_MEDITATION:
            case VMSTATE_GURU_MEDITATION_LS:
                pState->mptrConsole->setMachineState(MachineState_Stuck);
                break;

            case VMSTATE_FATAL_ERROR:
            case VMSTATE_FATAL_ERROR_LS:
                pState->mptrConsole->setMachineState(MachineState_Paused);
                break;

            default:
                AssertMsgFailed(("%s\n", VMR3GetStateName(enmVMState)));
            case VMSTATE_SUSPENDED:
            case VMSTATE_SUSPENDED_LS:
            case VMSTATE_SUSPENDING:
            case VMSTATE_SUSPENDING_LS:
            case VMSTATE_SUSPENDING_EXT_LS:
                if (!pState->mfUnlockedMedia)
                {
                    pState->mptrConsole->setMachineState(MachineState_Paused);
                    if (pState->mfSuspendedByUs)
                    {
                        autoLock.release();
                        int rc = VMR3Resume(VMR3GetVM(pState->mpUVM));
                        AssertLogRelMsgRC(rc, ("VMR3Resume -> %Rrc\n", rc));
                        autoLock.acquire();
                    }
                }
                else
                {
                    /* Faking a guru meditation is the best I can think of doing here... */
                    pState->mptrConsole->setMachineState(MachineState_Stuck);
                }
                break;
            }
        }
    }
    autoLock.release();

    /*
     * Cleanup.
     */
    Assert(pState->mhSocket == NIL_RTSOCKET);
    delete pState;

    return VINF_SUCCESS; /* ignored */
}