bool Lock()
    {
        if (m_mutex->OpenOrCreate(s_mutexName) == false)
        {
            LogConsole(logERROR, "Could not create Mutex (%d).\n", osGetLastSystemError());
            return (false);
        }

        if (m_mutex->Lock() == false)
        {
            LogConsole(logERROR, "Could not Lock Mutex (%d).\n", osGetLastSystemError());
            return (false);
        }

        return true;
    }
//-----------------------------------------------------------------------------
/// Lock global shared memory region for exclusive access.
/// \return true if the shared memory could be locked; false otherwise
//-----------------------------------------------------------------------------
bool SharedGlobal::Lock(void)
{
    PsAssert(this != NULL);
    PsAssert(m_Mutex != NULL);

    if (m_Mutex->lock() == false)
    {
        Log(logERROR, "Error occurred while waiting for Mutex :%d\n", osGetLastSystemError());
        return false;
    }

    return true;
}
//-----------------------------------------------------------------------------
/// Initialize all class data. This should only be called once
/// \return true if everything is properly initialized; false otherwise
//-----------------------------------------------------------------------------
bool SharedGlobal::Initialize()
{
    // Create the Mutex used to control access to the Shared Memory block
    m_Mutex = new osMutex();

    SharedMemory::MemStatus  status = m_MapFile->OpenOrCreate(sizeof(PsSharedGlobal), SHARED_MEMORY_NAME);

    if (SharedMemory::ERROR_CREATE == status)
    {
        LogConsole(logERROR, "Could not create file mapping object (%d).\n", osGetLastSystemError());
        return (false);
    }

    if (SharedMemory::ERROR_MAPPING == status)
    {
        LogConsole(logERROR, "Could not map view of file (%d).\n", osGetLastSystemError());
        return (false);
    }

    m_bInitialized = true;
    return (true);
}
// ---------------------------------------------------------------------------
// Name:        osGetLastSystemErrorAsString
// Description: Outputs the last error code recorded by the operating system
//              for the calling thread, translated into a string.
// Author:      AMD Developer Tools Team
// Date:        28/1/2008
// ---------------------------------------------------------------------------
void osGetLastSystemErrorAsString(gtString& systemErrorAsString)
{
    systemErrorAsString = OS_STR_unknownSystemError;

    // Get the system's last recorded error code:
    osSystemErrorCode systemLastError = osGetLastSystemError();

    // If no system error was recorded:
    if (systemLastError == 0)
    {
        systemErrorAsString = OS_STR_noSystemError;
    }
    else
    {
#if GR_LINUX_VARIANT == GR_GENERIC_LINUX_VARIANT
        // Get a string describing the system error:
        char buff[OS_SYSTEM_ERROR_BUFF_SIZE];
        char* pErrMsg = strerror_r(systemLastError, buff, OS_SYSTEM_ERROR_BUFF_SIZE);

        if (pErrMsg != NULL)
        {
            // Output the string we got:
            systemErrorAsString.fromASCIIString(pErrMsg);
        }

#elif GR_LINUX_VARIANT == GR_MAC_OS_X_LINUX_VARIANT
        // Get a string describing the system error:
        char buff[OS_SYSTEM_ERROR_BUFF_SIZE + 1];
        int rc1 = strerror_r(systemLastError, buff, OS_SYSTEM_ERROR_BUFF_SIZE);

        if (rc1 == 0)
        {
            // Null-terminate the string:
            buff[OS_SYSTEM_ERROR_BUFF_SIZE] = '\0';

            // Output the string we got:
            systemErrorAsString.fromASCIIString(buff);
        }

#else
#error Unknown Linux variant
#endif
    }
}
//---------------------------------------------------------
/// SendResponse
///
/// Sends the response either over sockets or shared memory
///
/// \param requestID the requestID to send the response to
/// \param cpsMimeType the mimetype to send the response as
/// \param cpsResponse the response to send
/// \param uResponseSize the size of the response being sent
/// \param bStreaming indicates that the response it to a streaming request
///
/// \return true if the response is 'sent' correctly; false otherwise
//---------------------------------------------------------
bool SendResponse(CommunicationID requestID, const char* cpsMimeType, const char* cpsResponse, unsigned int uResponseSize, bool bStreaming)
{
    // find out if this is a streaming response
    if (bStreaming)
    {
        Log(logTRACE, "Sending response over socket\n");
        // this is a streaming response
        // use the socket for comms

        return SendMimeResponse(requestID, cpsMimeType, cpsResponse, uResponseSize);
    }

    // use Shared memory if this is not a streaming response
    if (smLockPut("PLUGINS_TO_GPS", sizeof(requestID) + ((unsigned long) strlen(cpsMimeType) * sizeof(const char)) + uResponseSize, 3) == false)
    {
        Log(logASSERT, "Not enough space in shared memory for response.\n");
        return false;
    }

    NamedSemaphore semaphore;
    bool opened = semaphore.Open("PLUGINS_TO_GPS_SEMAPHORE");

    if (opened)
    {
        if (semaphore.Signal() == false)
        {
            Log(logWARNING, "Failed to signal PLUGINS_TO_GPS_SEMAPHORE. Response may be lost. Error is %d, Previous count is 0\n", osGetLastSystemError());
        }

        semaphore.Close();
    }
    else
    {
        Log(logWARNING, "Failed to open PLUGINS_TO_GPS_SEMAPHORE. Response may be delayed.\n");
    }

    bool bResult = (smPut("PLUGINS_TO_GPS", &requestID, sizeof(requestID)) &&
                    smPut("PLUGINS_TO_GPS", (void*)cpsMimeType, (unsigned long) strlen(cpsMimeType) * sizeof(const char)) &&
                    smPut("PLUGINS_TO_GPS", (void*)cpsResponse, uResponseSize));

    smUnlockPut("PLUGINS_TO_GPS");

    if (bResult == false)
    {
        Log(logASSERT, "Failed to put part of the response into shared memory\n");
    }
    else
    {
        // remove the request
        RemoveRequest(requestID);
    }

    return bResult;
}
//-----------------------------------------------------------------------------
bool OutputHTTPError(NetSocket* socket, int nErrorCode)
{
    /// generate the error code html
    static char headerBuffer[COMM_BUFFER_SIZE];
    static char htmlBuffer[COMM_BUFFER_SIZE];

    sprintf_s(htmlBuffer, COMM_BUFFER_SIZE, "<html><body><h2>Error: %d</h2></body></html>", nErrorCode);
    sprintf_s(headerBuffer, COMM_BUFFER_SIZE, "HTTP/1.0 %d\r\nContent-Type: text/html\r\nContent-Length: %zd\r\n\r\n", nErrorCode, strlen(htmlBuffer));

    bool nRes1;
    bool nRes2;
    nRes1 = socket->Send(headerBuffer, (DWORD)strlen(headerBuffer));
    nRes2 = socket->Send(htmlBuffer, (DWORD)strlen(htmlBuffer));

    socket->close();

    if ((nRes1 == false) ||
        (nRes2 == false))
    {
        Log(logERROR, "Failed to send HTTPError %d over socket %lu because of error %lu\n", nErrorCode, socket, osGetLastSystemError());
        return false;
    }

    return true;
}
//-----------------------------------------------------------------------------
bool Send(Response& rResponse, const char* mime, const char* pData, unsigned long dwSize)
{
    char sendbuffer[COMM_BUFFER_SIZE];
    sendbuffer[0] = 0;

    // send the http header and the file contents to the browser
    if (rResponse.m_bNeedToSendHeader == true)
    {
        GenerateHeader(rResponse, sendbuffer, COMM_BUFFER_SIZE);
        rResponse.m_bNeedToSendHeader = false;
    }

    if (rResponse.m_bStreamingEnabled == true)
    {
        strncat_s(sendbuffer, COMM_BUFFER_SIZE, "--BoundaryString\r\n", COMM_BUFFER_SIZE);
    }

    DWORD len = (DWORD)strlen(sendbuffer);
    sprintf_s(sendbuffer + len, COMM_BUFFER_SIZE - len, "Content-Type: %s\r\n"
              "Content-Length: %ld\r\n"
              "\r\n",
              mime,
              dwSize);

    // send header
    bool res = rResponse.client_socket->Send(sendbuffer, (DWORD)strlen(sendbuffer));

    if (res == true)
    {
        // if header could be sent
        // send data
        res = rResponse.client_socket->Send(pData, dwSize);
    }
    else
    {
        osSystemErrorCode systemLastError = osGetLastSystemError();

        // If no system error was recorded:
        if (systemLastError != 0)
        {
            gtString systemErrorString;
            osGetLastSystemErrorAsString(systemErrorString);

            Log(logERROR, "Failed to send %s response data due to error %d: %s\n", mime, systemLastError, systemErrorString.asASCIICharArray());
        }

        // there was an error sending
        // so close the connection and return false
        CloseConnection(rResponse);
        return false;
    }

    // if the response is not streaming,
    // the connection should be closed
    // otherwise it should be left open
    if (rResponse.m_bStreamingEnabled == false)
    {
        CloseConnection(rResponse);
    }

    return true;
}
//--------------------------------------------------------------
//  LaunchAppInNewProcess
//--------------------------------------------------------------
PROCESS_INFORMATION ProcessTracker::LaunchAppInNewProcess(gtASCIIString strApp, gtASCIIString strDir, gtASCIIString strArgs, osModuleArchitecture binaryType)
{
#ifdef _LINUX
    PS_UNREFERENCED_PARAMETER(binaryType);
#endif
    LogConsole(logMESSAGE, "About to launch: %s\n", strApp.asCharArray());
    LogConsole(logMESSAGE, "Params: %s\n", strArgs.asCharArray());
    LogConsole(logMESSAGE, "Working Directory: %s\n", strDir.asCharArray());

    // Get app directory and make it default
    if (strDir.isEmpty())
    {
        size_t pos = strApp.find_last_of("\\");

        if (pos != std::string::npos)
        {
            strDir = strApp.substr(0, (int)pos);

            if (strApp[0] == '\"')
            {
                strDir += "\"";
            }
        }
    }

    PROCESS_INFORMATION pi;
    ZeroMemory(&pi, sizeof(pi));

#ifdef _WIN32
    DWORD dwFlags = CREATE_DEFAULT_ERROR_MODE | CREATE_SUSPENDED;

    SetLastError(0);

    STARTUPINFO si;
    ZeroMemory(&si, sizeof(si));
    si.cb = sizeof(si);
#endif

    // Cmd line has to include the exe name since many apps expect the executable name to be in argv[0]!
    // Note argv[0] on the command line needs to be surrounded with quotes if it contains spaces.
    // The arguments in strArgs have already been "quoted" as they are parsed.

    gtASCIIString strCmdLine = AddQuotesIfStringHasSpaces(strApp.asCharArray());
    strCmdLine += " ";
    strCmdLine += strArgs;

    LogConsole(logMESSAGE, "strApp: %s\n", strApp.asCharArray());
    LogConsole(logMESSAGE, "strCmdLine: %s\n", strCmdLine.asCharArray());

    // Attempt to initialize the environment that the new process will run in. The child process should inherit "this" environment.
    if (!PrelaunchEnvironmentInitialization())
    {
        // Log a warning if this failed- initializing the environment for the new process can fail if Mantle support isn't installed.
        // In these cases, if the user is attempting to debug a Mantle application, they will have bigger problems to deal with.
        // In cases where a DX/GL app is being debugged, this warning can be ignored without any side effects.
        Log(logWARNING, "Environment initialization failed. If using DX/GL, it is safe to ignore this warning.\n");
    }

    BOOL succeeded = FALSE;

#ifdef _WIN32

    char microDLLPath[PS_MAX_PATH];
    const char* strServerPath;
    strServerPath = SG_GET_PATH(ServerPath);

    if (SG_GET_BOOL(OptionDllReplacement) == true)
    {
        DllReplacement::SetDllDirectory(binaryType == OS_X86_64_ARCHITECTURE);
    }

    // if using manual dll replacement or the AppInit_DLLs registry setting, don't use any kind of dll injection
    if (SG_GET_BOOL(OptionManualDllReplacement) == true || SG_GET_BOOL(OptionAppInitDll))
    {
        succeeded = CreateProcess(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(), NULL, NULL, TRUE, dwFlags, NULL, strDir.asCharArray(), &si, &pi);
    }
    else
    {
#ifdef X64

        // can only launch 64 bit applications
        if (binaryType != OS_X86_64_ARCHITECTURE)
        {
            sprintf_s(microDLLPath, PS_MAX_PATH, "%s" MICRODLLNAME "%s%s.dll", SG_GET_PATH(ServerPath), GDT_DEBUG_SUFFIX, GDT_BUILD_SUFFIX);
            succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(),
                                                        NULL, NULL, TRUE, dwFlags, NULL,
                                                        strDir.asCharArray(),
                                                        &si, &pi,
                                                        microDLLPath);
        }

#else

        if (binaryType != OS_I386_ARCHITECTURE)
        {
            sprintf_s(microDLLPath, PS_MAX_PATH, "%s" MICRODLLNAME "-x64%s%s.dll", SG_GET_PATH(ServerPath), GDT_DEBUG_SUFFIX, GDT_BUILD_SUFFIX);
            succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(),
                                                        NULL, NULL, TRUE, dwFlags, NULL,
                                                        strDir.asCharArray(),
                                                        &si, &pi,
                                                        microDLLPath);
        }

#endif // X64
        else
        {
            succeeded = AMDT::CreateProcessAndInjectDll(strApp.asCharArray(), (LPSTR)strCmdLine.asCharArray(),
                                                        NULL, NULL, TRUE, dwFlags, NULL,
                                                        strDir.asCharArray(),
                                                        &si, &pi,
                                                        SG_GET_PATH(MicroDLLPath));
        }
    }

#else

    // Create the app process
    succeeded = CreateProcess(strApp.asCharArray(), strCmdLine.asCharArray(), strDir.asCharArray(), &pi);
#endif // _WIN32

    if (!succeeded)
    {
        osSystemErrorCode systemLastError = osGetLastSystemError();

        gtString systemErrorString;
        osGetLastSystemErrorAsString(systemErrorString);

        Log(logERROR, "CreateProcessAndInjectDll failed; Error %d: %s\n", systemLastError, systemErrorString.asASCIICharArray());
        pi.dwProcessId = 0;
    }

#ifdef _WIN32
    else
    {
        // Check to see if the Steam.exe has been hooked and if so, set the value in shared memory
        // If Steam.exe was used to launch the target application, then the checks for cmd.exe and fcx.exe
        // need to be ignored.
        if (strApp.length() > 0)
        {
            if (strstr(strApp.toLowerCase().asCharArray(), "steam.exe") != NULL)
            {
                SG_SET_BOOL(SteamInjected, true);
            }

            ShowLauncherReminder(strApp.toLowerCase().asCharArray());
        }
        else
        {
            if (strstr(strCmdLine.toLowerCase().asCharArray(), "steam.exe") != NULL)
            {
                SG_SET_BOOL(SteamInjected, true);
            }

            ShowLauncherReminder(strCmdLine.toLowerCase().asCharArray());
        }
    }

#endif // _WIN32

    return pi;
}
//--------------------------------------------------------------
/// Writes information about the allowed plugins into shared memory
/// and launches the application specified in s_strInjectedAppName
/// \return true if the app is successfully launched; false otherwise
//--------------------------------------------------------------
bool ProcessTracker::WritePluginsToSharedMemoryAndLaunchApp()
{
    bool bGetType = false;

    if (OSDependentModulesInitialization() == false)
    {
        Log(logERROR, "Failed to initialize for plugins\n");
        return false;
    }

    osModuleArchitecture binaryType;

    bGetType = OSWrappers::GetBinaryType(m_injectedAppName.c_str(), &binaryType);

#ifdef _WIN32

    if (!bGetType)
    {
        // try without quotes, should be W8x preference.  This will update m_injectedAppName for later use also.
        m_injectedAppName.erase(0, 1);
        m_injectedAppName.resize(m_injectedAppName.size() - 1);

        bGetType = OSWrappers::GetBinaryType(m_injectedAppName.c_str(), &binaryType);
    }

#endif // def _WIN32

    if (!bGetType)
    {
        // file is not executable
        LogConsole(logERROR, "%s is not an executable file\n", m_injectedAppName.c_str());
        return false;
    }

    Log(logMESSAGE, "WritePluginsToSharedMemoryAndLaunchApp()\n");

    if (false == CreateSharedMemory())
    {
        return false;
    }

    // now actually start the application
    PROCESS_INFORMATION pi = LaunchAppInNewProcess(m_injectedAppName.c_str(), m_injectedAppDir.c_str(), m_injectedAppArgs.c_str(), binaryType);

    if (pi.dwProcessId == 0)
    {
        LogConsole(logWARNING, "Failed to start application: %s %s\n\n", m_injectedAppName.c_str(), m_injectedAppArgs.c_str());
        return false;
    }

    Log(logMESSAGE, "About to resume application thread\n");
#ifdef _WIN32

    if (osResumeSuspendedProcess(0, 0, pi.hThread, false) == false)
    {
        osSystemErrorCode systemLastError = osGetLastSystemError();

        gtString systemErrorString;
        osGetLastSystemErrorAsString(systemErrorString);

        Log(logERROR, "Resuming thread failed; Error %d: %s\n", systemLastError, systemErrorString.asASCIICharArray());
    }

#endif

    m_injectedAppID = pi.dwProcessId;

    return true;
}
////////////////////////////////////////////////////////////////////////////////////////////
/// Passes the supplied request header to the specifed plugin
/// \param strDestination the short description of the plugin to pass the request to
/// \param pRequestHeader the request header to pass to the plugin
/// \param pid the process id to pass the request to.
/// \param pClientSocket the socket used to read the request
/// \return true if the request was passed to the plugin; false otherwise
////////////////////////////////////////////////////////////////////////////////////////////
bool ProcessTracker::PassRequestToPlugin(const char* strDestination,
                                         HTTPRequestHeader* pRequestHeader,
                                         unsigned long pid,
                                         NetSocket* pClientSocket)
{
    if (strstr(pRequestHeader->GetHeaderData()->url, STR_STREAM_TOKEN) != NULL)
    {
        Log(logTRACE, "Duplicating Socket for request: %s\n", pRequestHeader->GetHeaderData()->url);

        // this is a streaming request, duplicate the socket
        // to reduce the overhead incurred by the shared memory approach
#if defined (_WIN32)

        if (pClientSocket->DuplicateToPID(pid, &pRequestHeader->GetHeaderData()->ProtoInfo) != 0)
#else
        PS_UNREFERENCED_PARAMETER(pid);

        if (CreateStreamSocket(pRequestHeader, pClientSocket) == false)
#endif
        {
            Log(logERROR, "Failed to duplicate socket for streaming request. Error %lu\n", osGetLastSystemError());
            return false;
        }
    }

    bool bResult = false;

    if (smLockPut(strDestination, sizeof(HTTPRequestHeader), 1))
    {
        // Check to see if the postdata failed to come over in some way.
        if (pRequestHeader->GetPostDataSize() > 0 &&  pRequestHeader->GetPostData() == NULL)
        {
            Log(logERROR, "PostData size is %d, but PostData is NULL\n", pRequestHeader->GetPostDataSize());
            // Force the data size to zero
            pRequestHeader->SetPostDataSize(0);
        }

        // Create a new record of this request
        // We will check to see when it comes back from the server.
        //RequestInFlight* pNewRequest = new RequestInFlight(pRequestHeader, pClientSocket);

        // Add the new record to the DB.
        //RequestsInFlightDatabase::Instance()->Add(pClientSocket, pNewRequest);

        bResult = smPut(strDestination, pRequestHeader->GetHeaderData(), sizeof(HTTPHeaderData));

        // Keep in for debugging
        //StreamLog::Ref() << "1) smPut HTTPRequestHeader\n" ;

        // If there is POST data we must send that too.
        if (pRequestHeader->GetPostDataSize() > 0 && pRequestHeader->GetPostData() != NULL)
        {
            bResult = smPut(strDestination, pRequestHeader->GetPostData(), pRequestHeader->GetPostDataSize());

            // Keep in for debugging
            //StreamLog::Ref() << "2) smPut Shader Code: " << pRequestHeader->pPostData << "\n";
        }

        smUnlockPut(strDestination);
#ifdef DEBUG_COMMS_PERFORMANCE
        CommandTimingManager::Instance()->IncrementServerLoadingCount(1) ;
#endif
    }

#ifdef DEBUG_COMMS_PERFORMANCE
    // Record the time now that we have sent the command over the shared memory.
    CommandTiming* cmdTiming = CommandTimingManager::Instance()->GetTimingFromPendingList(pClientSocket);

    if (cmdTiming != NULL)
    {
        LARGE_INTEGER nPerformanceCount;
        QueryPerformanceCounter(&nPerformanceCount);
        cmdTiming->SetPostSharedMemorySend(nPerformanceCount);
        // Set the current server loading value
        cmdTiming->SetServerLoadingCount(CommandTimingManager::Instance()->GetServerLoadingCount());
    }

#endif

    return bResult;
}
Example #11
0
//--------------------------------------------------------------
/// Creates and then waits for the PLUGINS_TO_GPS_SEMAPHORE to be
/// signaled, then reads the responses in PLUGINS_TO_GPS shared
/// memory and sends them to the requester.
/// \param pData should be NULL (it is ignored though)
//--------------------------------------------------------------
void PluginResponseThread::WaitForPluginResponses(void* pData)
{
    PS_UNREFERENCED_PARAMETER(pData);

    //create a semaphore which allows putting at most MAX_SEM_COUNT response into SM;
    NamedSemaphore semaphore;
    semaphore.Create("PLUGINS_TO_GPS_SEMAPHORE");

    bool bEvent;

    for (;;)    // loop forever
    {
        bEvent = semaphore.Wait();

        if (bEvent == false)
        {
            Log(logERROR, "Failed to wait on an event (Error %d). Closing response thread.\n", osGetLastSystemError());
            smClose("PLUGINS_TO_GPS");
            semaphore.Close();
            return;
        }

        // the PLUGINS_TO_GPS_EVENT was signaled
        // retrieve and send the response
        if (smLockGet("PLUGINS_TO_GPS"))
        {
            // read data from shared memory
            CommunicationID requestID = 0;
            char pcMimeType[PS_MAX_PATH];

            // the response was signaled, read out one response
            if (bEvent == true)
            {
                requestID = 0;
                memset(pcMimeType, 0, PS_MAX_PATH);
                char* pResponse = NULL;
                unsigned long uResponseSize = 0;

                LARGE_INTEGER nPreSharedMemoryGetTime;
                OSWrappers::QueryPerformanceCounter(&nPreSharedMemoryGetTime);

                if (smGet("PLUGINS_TO_GPS", &requestID, sizeof(CommunicationID)) == sizeof(CommunicationID))
                {
                    // successfully got the requestID

                    // Get the socket associated with this requestID and remove the socket since we're now donw with it.
                    NetSocket* client_socket = ProcessTracker::Instance()->GetSocketFromHandle(requestID);
                    ProcessTracker::Instance()->RemoveSocketFromMap(requestID);

#ifdef CODEXL_GRAPHICS
#ifdef USE_GRAPHICS_SERVER_STATUS_RETURN_CODES
                    // We can remove this message from the DB as we no longer need to monitor it anymore
                    RequestsInFlightDatabase::Instance()->Remove(client_socket);
#endif
#endif
                    // now try to get the mime type
                    if (smGet("PLUGINS_TO_GPS", &pcMimeType, PS_MAX_PATH) > 0)
                    {
                        // successfully got the mime type

                        // get response size
                        while (uResponseSize == 0)
                        {
                            uResponseSize = smGet("PLUGINS_TO_GPS", NULL, 0);
                        }

                        try
                        {
                            pResponse = new char[uResponseSize];
                            memset(pResponse, 0, uResponseSize * sizeof(char));
                        }
                        catch (std::bad_alloc)
                        {
                            Log(logERROR, "Failed to allocate memory for response of size: %lu\n", uResponseSize);
                            pResponse = NULL;
                        }

                        if (pResponse != NULL)
                        {
                            // Read from shared memory
                            if (smGet("PLUGINS_TO_GPS", pResponse, uResponseSize) == 0)
                            {
                                Log(logERROR, "Failed to get response from sharedMemory.\n");
                                smReset("PLUGINS_TO_GPS");
                            }

#ifdef DEBUG_COMMS_PERFORMANCE
                            // Record the time now
                            CommandTiming* pTiming = CommandTimingManager::Instance()->HandleResponse((NetSocket*)requestID);

                            if (pTiming != NULL)
                            {
                                LARGE_INTEGER nPerformanceCount;
                                QueryPerformanceCounter(&nPerformanceCount);
                                pTiming->SetWebServerRoundTripEnd(nPerformanceCount);    // Must set this one before SetPreSharedMemoryGet
                                pTiming->SetPreSharedMemoryGet(nPreSharedMemoryGetTime);
                                pTiming->SetResponseSize(uResponseSize);
                            }

#endif

                            // Send the data back to the client
                            SendMimeResponse(requestID, pcMimeType, pResponse, uResponseSize, client_socket);

#ifdef DEBUG_COMMS_PERFORMANCE
                            CommandTimingManager::Instance()->IncrementServerLoadingCount(-1);
#endif

                            SAFE_DELETE_ARRAY(pResponse);
                        }
                        else
                        {
                            smReset("PLUGINS_TO_GPS");
                            SendMimeResponse(requestID, "plain/text", "Error: Failed to get response from shared memory\n", 49 * sizeof(char), client_socket);
                        }

                    }
                    else
                    {
                        smReset("PLUGINS_TO_GPS");
                        SendMimeResponse(requestID, "plain/text", "Error: Could not read mime type\n", 32 * sizeof(char), client_socket);
                    }
                }
                else
                {
                    // in this case, we don't know socket the communication was on, so we can't send any errors or even close the socket
                    // just have to let the client time out.
                    smReset("PLUGINS_TO_GPS");
                    Log(logERROR, "Failed to get requestID from sharedMemory.\n");
                }

            } // end while sm has data or no responses have been read

            smUnlockGet("PLUGINS_TO_GPS");

        }
        else
        {
            Log(logERROR, "LockGet Failed\n");
        }
    }
}