Beispiel #1
0
			virtual void OnThreadExecute(void)
			{
				SetThreadName("CaptureServer");

				FileOutStream outStream(m_file);

				// Technically any Labels that get initialized on another thread bettween the barrier and loop
				// will get recorded twice, but OVRMonitor will handle that scenario gracefully.
				g_labelLock.Lock();
				for(Label *l=Label::GetHead(); l; l=l->GetNext())
				{
					SendLabelPacket(*l);
				}
				g_labelLock.Unlock();

				// Start CPU/GPU/Thermal sensors...
				StandardSensors stdsensors;
				if(CheckConnectionFlag(Enable_CPU_Clocks) || CheckConnectionFlag(Enable_GPU_Clocks) || CheckConnectionFlag(Enable_Thermal_Sensors))
				{
					stdsensors.Start();
				}

				// as long as we are running, continuously flush the latest stream data to disk...
				while(!QuitSignaled())
				{
					const UInt64 flushBeginTime = GetNanoseconds();
					if(!AsyncStream::FlushAll(outStream))
					{
						break;
					}
					const UInt64 flushEndTime   = GetNanoseconds();
					const UInt64 flushDeltaTime = flushEndTime - flushBeginTime;
					const UInt64 sleepTime      = 4000000; // 4ms
					if(flushDeltaTime < sleepTime)
					{
						// Sleep just a bit to keep the thread from killing a core and to let a good chunk of data build up
						ThreadSleepNanoseconds((UInt32)(sleepTime - flushDeltaTime));
					}
				}

				// Clear the connection flags...
				AtomicExchange(g_connectionFlags, (UInt32)0);

				// Close down our sensor thread...
				stdsensors.QuitAndWait();
			}
    void StandardSensors::OnThreadExecute(void)
    {
        // Pre-load what files we can... reduces open/close overhead (which is significant)

        // Setup CPU Clocks Support...
        FileHandle cpuOnlineFiles[g_maxCpus];
        FileHandle cpuFreqFiles[g_maxCpus];
        for(unsigned int i=0; i<g_maxCpus; i++)
        {
            cpuOnlineFiles[i] = cpuFreqFiles[i] = NullFileHandle;
        }
        if(CheckConnectionFlag(Enable_CPU_Zones))
        {
            for(unsigned int i=0; i<g_maxCpus; i++)
            {
                const CpuSensorDesc &desc = g_cpuDescs[i];
                cpuOnlineFiles[i] = OpenFile(desc.onlinePath);
                cpuFreqFiles[i]   = NullFileHandle;
                if(cpuOnlineFiles[i] != NullFileHandle)
                {
                    const int maxFreq = ReadIntFile(desc.maxFreqPath);
                    SensorSetRange(desc.label, 0, (float)maxFreq, Sensor_Interp_Nearest, Sensor_Unit_KHz);
                }
            }
        }

        // Setup GPU Clocks Support...
        FileHandle gpuFreqFile = NullFileHandle;
        if(CheckConnectionFlag(Enable_GPU_Clocks))
        {
            gpuFreqFile = OpenFile("/sys/class/kgsl/kgsl-3d0/gpuclk");
        }
        if(gpuFreqFile != NullFileHandle)
        {
            const int maxFreq = ReadIntFile("/sys/class/kgsl/kgsl-3d0/max_gpuclk");
            SensorSetRange(g_gpuLabel, 0, (float)maxFreq, Sensor_Interp_Nearest, Sensor_Unit_Hz);
        }

        // Setup Memory Clocks Support...
        FileHandle memFreqFile = NullFileHandle;
        //memFreqFile = OpenFile("/sys/class/devfreq/0.qcom,cpubw/cur_freq");
        if(memFreqFile != NullFileHandle)
        {
            const int maxFreq = ReadIntFile("/sys/class/devfreq/0.qcom,cpubw/max_freq");
            SensorSetRange(g_memLabel, 0, (float)maxFreq, Sensor_Interp_Nearest, Sensor_Unit_MByte_Second);
        }

        // Setup thermal sensors...
        static const unsigned int maxThermalSensors = 20;
        static ThermalSensorDesc  thermalDescs[maxThermalSensors];
        FileHandle                thermalFiles[maxThermalSensors];
        for(unsigned int i=0; i<maxThermalSensors; i++)
        {
            thermalFiles[i] = NullFileHandle;
        }
        if(CheckConnectionFlag(Enable_Thermal_Sensors))
        {
            for(unsigned int i=0; i<maxThermalSensors; i++)
            {
                ThermalSensorDesc &desc = thermalDescs[i];

                char typePath[64] = {0};
                char tempPath[64] = {0};
                char modePath[64] = {0};
                sprintf(typePath, "/sys/devices/virtual/thermal/thermal_zone%d/type", i);
                sprintf(tempPath, "/sys/devices/virtual/thermal/thermal_zone%d/temp", i);
                sprintf(modePath, "/sys/devices/virtual/thermal/thermal_zone%d/mode", i);

                // If either of these files don't exist, then we got to the end of the thermal zone list...
                if(!CheckFileExists(typePath) || !CheckFileExists(tempPath))
                    break;

                // check to see if the zone is disabled... its okay if there is no mode file...
                char mode[16] = {0};
                if(ReadFileLine(modePath, mode, sizeof(mode))>0 && !strcmp(mode, "disabled"))
                    continue;

                if(!desc.initialized)
                {
                    // Read the sensor name in...
                    ReadFileLine(typePath, desc.name, sizeof(desc.name));

                    // Initialize the Label...
                    desc.initialized = desc.label.ConditionalInit(desc.name);
                }

                // Finally... open the file.
                thermalFiles[i] = OpenFile(tempPath);
        
                if(thermalFiles[i] != NullFileHandle)
                {
                    // by default 0 to 100 degrees...
                    SensorSetRange(desc.label, 0, 100, Sensor_Interp_Linear);
                }
            }
        }

        // For clocks, we store the last value and only send updates when it changes since we
        // use blocking chart rendering.
        int lastCpuFreq[g_maxCpus] = {0};
        int lastGpuFreq            = 0;
        int lastMemValue           = 0;

        unsigned int sampleCount = 0;

        while(!QuitSignaled() && IsConnected())
        {
            // Sample CPU Frequencies...
            for(unsigned int i=0; i<g_maxCpus; i++)
            {
                // If the 'online' file can't be found, then we just assume this CPU doesn't even exist
                if(cpuOnlineFiles[i] == NullFileHandle)
                    continue;

                const CpuSensorDesc &desc   = g_cpuDescs[i];
                const bool           online = ReadIntFile(desc.onlinePath);
                if(online && cpuFreqFiles[i]==NullFileHandle)
                {
                    // Open the frequency file if we are online and its not already open...
                    cpuFreqFiles[i] = OpenFile(desc.freqPath);
                }
                else if(!online && cpuFreqFiles[i]!=NullFileHandle)
                {
                    // close the frequency file if we are no longer online
                    CloseFile(cpuFreqFiles[i]);
                    cpuFreqFiles[i] = NullFileHandle;
                }
                const int freq = cpuFreqFiles[i]==NullFileHandle ? 0 : ReadIntFile(cpuFreqFiles[i]);
                if(freq != lastCpuFreq[i])
                {
                    // Convert from KHz to Hz
                    SensorSetValue(desc.label, (float)freq);
                    lastCpuFreq[i] = freq;
                }
                ThreadYield();
            }

            // Sample GPU Frequency...
            if(gpuFreqFile != NullFileHandle)
            {
                const int freq = ReadIntFile(gpuFreqFile);
                if(freq != lastGpuFreq)
                {
                    SensorSetValue(g_gpuLabel, (float)freq);
                    lastGpuFreq = freq;
                }
            }

            // Sample Memory Bandwidth
            if(memFreqFile != NullFileHandle)
            {
                const int value = ReadIntFile(memFreqFile);
                if(value != lastMemValue)
                {
                    SensorSetValue(g_memLabel, (float)value);
                    lastMemValue = value;
                }
            }

            // Sample thermal sensors...
            if((sampleCount&15) == 0) // sample temperature at a much lower frequency as clocks... thermals don't change that fast.
            {
                for(unsigned int i=0; i<maxThermalSensors; i++)
                {
                    FileHandle file = thermalFiles[i];
                    if(file != NullFileHandle)
                    {
                        SensorSetValue(thermalDescs[i].label, (float)ReadIntFile(file));
                    }
                }
                ThreadYield();
            }

            // Sleep 5ms between samples...
            ThreadSleepMicroseconds(5000);
            sampleCount++;
        }

        // Close down cached file handles...
        for(unsigned int i=0; i<g_maxCpus; i++)
        {
            if(cpuOnlineFiles[i] != NullFileHandle) CloseFile(cpuOnlineFiles[i]);
            if(cpuFreqFiles[i]   != NullFileHandle) CloseFile(cpuFreqFiles[i]);
        }
        if(gpuFreqFile != NullFileHandle) CloseFile(gpuFreqFile);
        if(memFreqFile != NullFileHandle) CloseFile(memFreqFile);
        for(unsigned int i=0; i<maxThermalSensors; i++)
        {
            if(thermalFiles[i] != NullFileHandle) CloseFile(thermalFiles[i]);
        }
    }
            virtual void OnThreadExecute(void)
            {
                SetThreadName("OVR::Capture");
                while(m_listenSocket && !QuitSignaled())
                {
                    // try and accept a new socket connection...
                    SocketAddress streamAddr;
                    m_streamSocket = m_listenSocket->Accept(streamAddr);

                    // If no connection was established, something went totally wrong and we should just abort...
                    if(!m_streamSocket)
                        break;
                    
                    // Before we start sending capture data... first must exchange connection headers...
                    // First attempt to read in the request header from the Client...
                    ConnectionHeaderPacket clientHeader = {0};
                    if(!m_streamSocket->Receive(&clientHeader, sizeof(clientHeader)))
                    {
                        m_streamSocket->Release();
                        m_streamSocket = NULL;
                        continue;
                    }

                    // Load our connection flags...
                    const UInt32 connectionFlags = clientHeader.flags & g_initFlags;
                    
                    // Build and send return header... We *always* send the return header so that if we don't
                    // like something (like version number or feature flags), the client has some hint as to
                    // what we didn't like.
                    ConnectionHeaderPacket serverHeader = {0};
                    serverHeader.size    = sizeof(serverHeader);
                    serverHeader.version = ConnectionHeaderPacket::s_version;
                    serverHeader.flags   = connectionFlags;
                    if(!m_streamSocket->Send(&serverHeader, sizeof(serverHeader)))
                    {
                        m_streamSocket->Release();
                        m_streamSocket = NULL;
                        continue;
                    }

                    // Check version number...
                    if(clientHeader.version != serverHeader.version)
                    {
                        m_streamSocket->Release();
                        m_streamSocket = NULL;
                        continue;
                    }

                    // Check that we have any capture features even turned on...
                    if(!connectionFlags)
                    {
                        m_streamSocket->Release();
                        m_streamSocket = NULL;
                        continue;
                    }

                    // Finally, send our packet descriptors...
                    const PacketDescriptorPacket packetDescs[] =
                    {
                        BuildPacketDescriptorPacket<ThreadNamePacket>(),
                        BuildPacketDescriptorPacket<LabelPacket>(),
                        BuildPacketDescriptorPacket<FramePacket>(),
                        BuildPacketDescriptorPacket<VSyncPacket>(),
                        BuildPacketDescriptorPacket<CPUZoneEnterPacket>(),
                        BuildPacketDescriptorPacket<CPUZoneLeavePacket>(),
                        BuildPacketDescriptorPacket<GPUZoneEnterPacket>(),
                        BuildPacketDescriptorPacket<GPUZoneLeavePacket>(),
                        BuildPacketDescriptorPacket<GPUClockSyncPacket>(),
                        BuildPacketDescriptorPacket<SensorRangePacket>(),
                        BuildPacketDescriptorPacket<SensorPacket>(),
                        BuildPacketDescriptorPacket<FrameBufferPacket>(),
                        BuildPacketDescriptorPacket<LogPacket>(),
                    };
                    const PacketDescriptorHeaderPacket packetDescHeader = { sizeof(packetDescs) / sizeof(packetDescs[0]) };
                    if(!m_streamSocket->Send(&packetDescHeader, sizeof(packetDescHeader)))
                    {
                        m_streamSocket->Release();
                        m_streamSocket = NULL;
                        continue;
                    }
                    if(!m_streamSocket->Send(&packetDescs, sizeof(packetDescs)))
                    {
                        m_streamSocket->Release();
                        m_streamSocket = NULL;
                        continue;
                    }
                    
                    // Connection established!

                    // Signal that we are connected!
                    AtomicExchange(g_connectionFlags, connectionFlags);

                    // Technically any Labels that get initialized on another thread bettween the barrier and loop
                    // will get sent over the network twice, but OVRMonitor will handle that.
                    SpinLock(g_labelLock);
                    for(Label *l=Label::GetHead(); l; l=l->GetNext())
                    {
                        SendLabelPacket(*l);
                    }
                    SpinUnlock(g_labelLock);

                    // Start CPU/GPU/Thermal sensors...
                    StandardSensors stdsensors;
                    if(CheckConnectionFlag(Enable_CPU_Clocks) || CheckConnectionFlag(Enable_GPU_Clocks) || CheckConnectionFlag(Enable_Thermal_Sensors))
                    {
                        stdsensors.Start();
                    }

                    // Spin as long as we are connected flushing data from our data stream...
                    while(!QuitSignaled())
                    {
                        const UInt64 flushBeginTime = GetNanoseconds();
                        if(!AsyncStream::FlushAll(*m_streamSocket))
                        {
                            // Error occured... shutdown the connection.
                            AtomicExchange(g_connectionFlags, (UInt32)0);
                            m_streamSocket->Shutdown();
                            break;
                        }
                        const UInt64 flushEndTime   = GetNanoseconds();
                        const UInt64 flushDeltaTime = flushEndTime - flushBeginTime;
                        const UInt64 sleepTime      = 5000000; // 5ms
                        if(flushDeltaTime < sleepTime)
                        {
                            // Sleep just a bit to keep the thread from killing a core and to let a good chunk of data build up
                            ThreadSleepNanoseconds(sleepTime - flushDeltaTime);
                        }
                    }

                    // TODO: should we call AsyncStream::Shutdown() here???

                    // Close down our sensor thread...
                    stdsensors.QuitAndWait();

                    // Connection was closed at some point, lets clean up our socket...
                    m_streamSocket->Release();
                    m_streamSocket = NULL;

                } // while(m_listenSocket && !QuitSignaled())
            }
	void StandardSensors::OnThreadExecute(void)
	{
		SetThreadName("CaptureSensors");

		// Pre-load what files we can... reduces open/close overhead (which is significant)

		// Setup CPU Clocks Support...
		static const UInt32 maxCpus = 8;
		static SensorLabelDesc cpuDescs[maxCpus];
		FileHandle cpuOnlineFiles[maxCpus];
		FileHandle cpuFreqFiles[maxCpus];
		for(UInt32 i=0; i<maxCpus; i++)
		{
			cpuOnlineFiles[i] = cpuFreqFiles[i] = NullFileHandle;
			SensorLabelDesc &desc = cpuDescs[i];
			FormatString(desc.name, sizeof(desc.name), "CPU%u Clocks", i);
			desc.label.ConditionalInit(desc.name);
		}
		if(CheckConnectionFlag(Enable_CPU_Zones))
		{
			int maxFreq = 0;
			for(UInt32 i=0; i<maxCpus; i++)
			{
				char onlinePath[64] = {0};
				FormatString(onlinePath, sizeof(onlinePath), "/sys/devices/system/cpu/cpu%u/online", i);
				cpuOnlineFiles[i] = OpenFile(onlinePath);
				cpuFreqFiles[i]   = NullFileHandle;
				if(cpuOnlineFiles[i] != NullFileHandle)
				{
					char maxFreqPath[64] = {0};
					FormatString(maxFreqPath, sizeof(maxFreqPath), "/sys/devices/system/cpu/cpu%u/cpufreq/cpuinfo_max_freq", i);
					maxFreq = std::max(maxFreq, ReadIntFile(maxFreqPath));
				}
			}
			for(UInt32 i=0; i<maxCpus; i++)
			{
				const SensorLabelDesc &desc = cpuDescs[i];
				SensorSetRange(desc.label, 0, (float)maxFreq, Sensor_Interp_Nearest, Sensor_Unit_KHz);
			}
		}

		// Setup GPU Clocks Support...
		FileHandle gpuFreqFile = NullFileHandle;
		if(CheckConnectionFlag(Enable_GPU_Clocks))
		{
			if(gpuFreqFile == NullFileHandle) // Adreno
			{
				gpuFreqFile = OpenFile("/sys/class/kgsl/kgsl-3d0/gpuclk");
				if(gpuFreqFile != NullFileHandle)
				{
					const int maxFreq = ReadIntFile("/sys/class/kgsl/kgsl-3d0/max_gpuclk");
					SensorSetRange(g_gpuLabel, 0, (float)maxFreq, Sensor_Interp_Nearest, Sensor_Unit_Hz);
				}
			}
			if(gpuFreqFile == NullFileHandle) // Mali
			{
				gpuFreqFile = OpenFile("/sys/devices/14ac0000.mali/clock");
				if(gpuFreqFile != NullFileHandle)
				{
					// TODO: query max GPU clocks on Mali, for now hacked to what we know the S6 is
					SensorSetRange(g_gpuLabel, 0, 0, Sensor_Interp_Nearest, Sensor_Unit_MHz);
				}
			}
		}

		// Setup Memory Clocks Support...
		FileHandle memFreqFile = NullFileHandle;
		//memFreqFile = OpenFile("/sys/class/devfreq/0.qcom,cpubw/cur_freq");
		if(memFreqFile != NullFileHandle)
		{
			const int maxFreq = ReadIntFile("/sys/class/devfreq/0.qcom,cpubw/max_freq");
			SensorSetRange(g_memLabel, 0, (float)maxFreq, Sensor_Interp_Nearest, Sensor_Unit_MByte_Second);
		}

		// Setup thermal sensors...
		static const UInt32 maxThermalSensors = 20;
		static SensorLabelDesc  thermalDescs[maxThermalSensors];
		FileHandle              thermalFiles[maxThermalSensors];
		for(UInt32 i=0; i<maxThermalSensors; i++)
		{
			thermalFiles[i] = NullFileHandle;
		}
		if(CheckConnectionFlag(Enable_Thermal_Sensors))
		{
			for(UInt32 i=0; i<maxThermalSensors; i++)
			{
				SensorLabelDesc &desc = thermalDescs[i];

				char typePath[64]      = {0};
				char tempPath[64]      = {0};
				char tripPointPath[64] = {0};
				FormatString(typePath,      sizeof(typePath),      "/sys/devices/virtual/thermal/thermal_zone%u/type", i);
				FormatString(tempPath,      sizeof(tempPath),      "/sys/devices/virtual/thermal/thermal_zone%u/temp", i);
				FormatString(tripPointPath, sizeof(tripPointPath), "/sys/devices/virtual/thermal/thermal_zone%u/trip_point_0_temp", i);

				// If either of these files don't exist, then we got to the end of the thermal zone list...
				if(!CheckFileExists(typePath) || !CheckFileExists(tempPath) || !CheckFileExists(tripPointPath))
					break;

				// Initialize the Label...
				if(ReadFileLine(typePath, desc.name, sizeof(desc.name)) <= 0)
					continue; // failed to read sensor name...
				desc.label.ConditionalInit(desc.name);

				char modePath[64] = {0};
				FormatString(modePath, sizeof(modePath), "/sys/devices/virtual/thermal/thermal_zone%d/mode", i);

				// check to see if the zone is disabled... its okay if there is no mode file...
				char mode[16] = {0};
				if(ReadFileLine(modePath, mode, sizeof(mode))>0 && !strcmp(mode, "disabled"))
					continue;

				// Finally... open the file.
				thermalFiles[i] = OpenFile(tempPath);

				// Check to see if the temperature file was found...
				if(thermalFiles[i] == NullFileHandle)
					continue;

				// Read in the critical temperature value.
				const int tripPoint = ReadIntFile(tripPointPath);
				if(tripPoint > 0)
				{
					SensorSetRange(desc.label, 0, (float)tripPoint, Sensor_Interp_Linear);
				}
			}
		}

		// For clocks, we store the last value and only send updates when it changes since we
		// use blocking chart rendering.
		int lastCpuFreq[maxCpus] = {0};
		int lastGpuFreq          = 0;
		int lastMemValue         = 0;

		UInt32 sampleCount = 0;

		while(!QuitSignaled() && IsConnected())
		{
			// Sample CPU Frequencies...
			for(UInt32 i=0; i<maxCpus; i++)
			{
				// If the 'online' file can't be found, then we just assume this CPU doesn't even exist
				if(cpuOnlineFiles[i] == NullFileHandle)
					continue;

				const SensorLabelDesc &desc   = cpuDescs[i];
				const bool             online = ReadIntFile(cpuOnlineFiles[i]) ? true : false;
				if(online && cpuFreqFiles[i]==NullFileHandle)
				{
					// Open the frequency file if we are online and its not already open...
					char freqPath[64] = {0};
					FormatString(freqPath, sizeof(freqPath), "/sys/devices/system/cpu/cpu%u/cpufreq/scaling_cur_freq", i);
					cpuFreqFiles[i] = OpenFile(freqPath);
				}
				else if(!online && cpuFreqFiles[i]!=NullFileHandle)
				{
					// close the frequency file if we are no longer online
					CloseFile(cpuFreqFiles[i]);
					cpuFreqFiles[i] = NullFileHandle;
				}
				const int freq = cpuFreqFiles[i]==NullFileHandle ? 0 : ReadIntFile(cpuFreqFiles[i]);
				if(freq != lastCpuFreq[i])
				{
					// Convert from KHz to Hz
					SensorSetValue(desc.label, (float)freq);
					lastCpuFreq[i] = freq;
				}
				ThreadYield();
			}

			// Sample GPU Frequency...
			if(gpuFreqFile != NullFileHandle)
			{
				const int freq = ReadIntFile(gpuFreqFile);
				if(freq != lastGpuFreq)
				{
					SensorSetValue(g_gpuLabel, (float)freq);
					lastGpuFreq = freq;
				}
			}

			// Sample Memory Bandwidth
			if(memFreqFile != NullFileHandle)
			{
				const int value = ReadIntFile(memFreqFile);
				if(value != lastMemValue)
				{
					SensorSetValue(g_memLabel, (float)value);
					lastMemValue = value;
				}
			}

			// Sample thermal sensors...
			if((sampleCount&15) == 0) // sample temperature at a much lower frequency as clocks... thermals don't change that fast.
			{
				for(UInt32 i=0; i<maxThermalSensors; i++)
				{
					FileHandle file = thermalFiles[i];
					if(file != NullFileHandle)
					{
						SensorSetValue(thermalDescs[i].label, (float)ReadIntFile(file));
					}
				}
				ThreadYield();
			}

			// Sleep 5ms between samples...
			ThreadSleepMicroseconds(5000);
			sampleCount++;
		}

		// Close down cached file handles...
		for(UInt32 i=0; i<maxCpus; i++)
		{
			if(cpuOnlineFiles[i] != NullFileHandle) CloseFile(cpuOnlineFiles[i]);
			if(cpuFreqFiles[i]   != NullFileHandle) CloseFile(cpuFreqFiles[i]);
		}
		if(gpuFreqFile != NullFileHandle) CloseFile(gpuFreqFile);
		if(memFreqFile != NullFileHandle) CloseFile(memFreqFile);
		for(UInt32 i=0; i<maxThermalSensors; i++)
		{
			if(thermalFiles[i] != NullFileHandle) CloseFile(thermalFiles[i]);
		}
	}
Beispiel #5
0
			virtual void OnThreadExecute(void)
			{
				SetThreadName("CaptureServer");

				// Acquire the process name...
			#if defined(OVR_CAPTURE_WINDOWS)
				char packageName[64] = {0};
				GetModuleFileNameA(NULL, packageName, sizeof(packageName));
				if(!packageName[0])
				{
					StringCopy(packageName, "Unknown", sizeof(packageName));
				}
			#else
				char packageName[64] = {0};
				char cmdlinepath[64] = {0};
				FormatString(cmdlinepath, sizeof(cmdlinepath), "/proc/%u/cmdline", (unsigned)getpid());
				if(ReadFileLine(cmdlinepath, packageName, sizeof(packageName)) <= 0)
				{
					StringCopy(packageName, "Unknown", sizeof(packageName));
				}
			#endif

				while(m_listenSocket && !QuitSignaled())
				{
					// Start auto-discovery thread...
					ZeroConfigHost *zeroconfig = ZeroConfigHost::Create(g_zeroConfigPort, m_listenPort, packageName);
					zeroconfig->Start();

					// try and accept a new socket connection...
					SocketAddress streamAddr;
					m_streamSocket = m_listenSocket->Accept(streamAddr);

					// Once connected, shut the auto-discovery thread down.
					zeroconfig->Release();

					// If no connection was established, something went totally wrong and we should just abort...
					if(!m_streamSocket)
						break;

					// Before we start sending capture data... first must exchange connection headers...
					// First attempt to read in the request header from the Client...
					ConnectionHeaderPacket clientHeader = {0};
					if(!m_streamSocket->Receive(&clientHeader, sizeof(clientHeader)))
					{
						m_streamSocket->Release();
						m_streamSocket = NULL;
						continue;
					}

					// Load our connection flags...
					const UInt32 connectionFlags = clientHeader.flags & g_initFlags;

					// Build and send return header... We *always* send the return header so that if we don't
					// like something (like version number or feature flags), the client has some hint as to
					// what we didn't like.
					ConnectionHeaderPacket serverHeader = {0};
					serverHeader.size    = sizeof(serverHeader);
					serverHeader.version = ConnectionHeaderPacket::s_version;
					serverHeader.flags   = connectionFlags;
					if(!m_streamSocket->Send(&serverHeader, sizeof(serverHeader)))
					{
						m_streamSocket->Release();
						m_streamSocket = NULL;
						continue;
					}

					// Check version number...
					if(clientHeader.version != serverHeader.version)
					{
						m_streamSocket->Release();
						m_streamSocket = NULL;
						continue;
					}

					// Check that we have any capture features even turned on...
					if(!connectionFlags)
					{
						m_streamSocket->Release();
						m_streamSocket = NULL;
						continue;
					}

					// Finally, send our packet descriptors...
					const PacketDescriptorHeaderPacket packetDescHeader = { g_numPacketDescs };
					if(!m_streamSocket->Send(&packetDescHeader, sizeof(packetDescHeader)))
					{
						m_streamSocket->Release();
						m_streamSocket = NULL;
						continue;
					}
					if(!m_streamSocket->Send(&g_packetDescs, sizeof(g_packetDescs)))
					{
						m_streamSocket->Release();
						m_streamSocket = NULL;
						continue;
					}

					// Connection established!

					// Initialize the per-thread stream system before flipping on g_connectionFlags...
					AsyncStream::Init();

					if(g_onConnect)
					{
						// Call back into the app to notify a connection is being established.
						// We intentionally do this before enabling the connection flags.
						g_onConnect(connectionFlags);
					}

					// Signal that we are connected!
					AtomicExchange(g_connectionFlags, connectionFlags);

					// Technically any Labels that get initialized on another thread bettween the barrier and loop
					// will get sent over the network twice, but OVRMonitor will handle that.
					g_labelLock.Lock();
					for(Label *l=Label::GetHead(); l; l=l->GetNext())
					{
						SendLabelPacket(*l);
					}
					g_labelLock.Unlock();

					// Start CPU/GPU/Thermal sensors...
					StandardSensors stdsensors;
					if(CheckConnectionFlag(Enable_CPU_Clocks) || CheckConnectionFlag(Enable_GPU_Clocks) || CheckConnectionFlag(Enable_Thermal_Sensors))
					{
						stdsensors.Start();
					}

					// Spin as long as we are connected flushing data from our data stream...
					while(!QuitSignaled())
					{
						const UInt64 flushBeginTime = GetNanoseconds();
						const UInt32 waitflags = m_streamSocket->WaitFor(Socket::WaitFlag_Read | Socket::WaitFlag_Write | Socket::WaitFlag_Timeout, 2);
						if(waitflags & Socket::WaitFlag_Timeout)
						{
							// Connection likely failed somehow...
							break;
						}
						if(waitflags & Socket::WaitFlag_Read)
						{
							PacketHeader header;
							VarSetPacket packet;
							m_streamSocket->Receive((char*)&header, sizeof(header));
							if (header.packetID == Packet_Var_Set)
							{
								m_streamSocket->Receive((char*)&packet, sizeof(packet));
								g_varStore.Set(packet.labelID, packet.value, true);
							}
							else
							{
								Logf(Log_Warning, "OVR::Capture::RemoteServer; Received Invalid Capture Packet");
							}
						}
						if(waitflags & Socket::WaitFlag_Write)
						{
							// Socket is ready to write data... so now is a good time to flush pending capture data.
							SocketOutStream outStream(*m_streamSocket);
							if(!AsyncStream::FlushAll(outStream))
							{
								// Error occured... shutdown the connection.
								break;
							}
						}
						const UInt64 flushEndTime   = GetNanoseconds();
						const UInt64 flushDeltaTime = flushEndTime - flushBeginTime;
						const UInt64 sleepTime      = 4000000; // 4ms
						if(flushDeltaTime < sleepTime)
						{
							// Sleep just a bit to keep the thread from killing a core and to let a good chunk of data build up
							ThreadSleepNanoseconds((UInt32)(sleepTime - flushDeltaTime));
						}
					}

					// Clear the connection flags...
					AtomicExchange(g_connectionFlags, (UInt32)0);

					// Close down our sensor thread...
					stdsensors.QuitAndWait();

					// Connection was closed at some point, lets clean up our socket...
					m_streamSocket->Shutdown();
					m_streamSocket->Release();
					m_streamSocket = NULL;

					if(g_onDisconnect)
					{
						// After the connection is fully shut down, notify the app.
						g_onDisconnect();
					}

					// Clear the buffers for all AsyncStreams to guarantee that no event is 
					// stalled waiting for room on a buffer. Then we wait until there there
					// are no events still writing out.
					AsyncStream::ClearAll();
					while(AtomicGet(g_refcount) > 0)
					{
						ThreadSleepMilliseconds(1);
					}

					// Finally, release any AsyncStreams that were created during this session
					// now that we can safely assume there are no events actively trying to
					// write out to a stream.
					AsyncStream::Shutdown();

					g_varStore.Clear();
				} // while(m_listenSocket && !QuitSignaled())
			}