public unsafe void TestProfilerSessionEmitsMarkerInfoBeforeMarker() { SpoofInitListenInternal(); Connection.ConnectDirect(spoofIp, spoofPort); Baselib_Timer_WaitForAtLeast(200); SpoofAcceptInternal(); ProfilerTestFixture.ReceiveConnectMessage(); #if !NET_DOTS HashSet <uint> markerDefined = new HashSet <uint>(); #else Dictionary <uint, bool> markerDefined = new Dictionary <uint, bool>(); #endif for (int i = 0; i < 8; i++) { for (ulong j = 0; j < 8; j++) { ProfilerMarker marker = new ProfilerMarker($"{Baselib_Timer_GetHighPrecisionTimerTicks() + j} test"); ProfilerMarker marker2 = new ProfilerMarker($"{Baselib_Timer_GetHighPrecisionTimerTicks() + j + 1} test2"); marker.Begin(); marker2.Begin(); marker2.End(); marker.End(); } ProfilerProtocolSession.SendNewMarkersAndThreads(); SendAndValidate(markerDefined); } SpoofExitInternal(); }
public static void TransmitAndReceive() { #if ENABLE_MULTICAST Multicast.Broadcast(); #endif Connect(); if (!Connected) { if (state == ConnectionState.Invalid) { MessageStreamManager.RecycleAll(); } return; } // Check if got disconnected #if UNITY_WEBGL if (WebSocketLostConnection() == 1) #else Baselib_Socket_PollFd pollFd = new Baselib_Socket_PollFd(); unsafe { pollFd.handle.handle = hSocket.handle; pollFd.errorState = (Baselib_ErrorState *)UnsafeUtility.AddressOf(ref errState); pollFd.requestedEvents = Baselib_Socket_PollEvents.Connected; Baselib_Socket_Poll(&pollFd, 1, 0, (Baselib_ErrorState *)UnsafeUtility.AddressOf(ref errState)); }; if (errState.code != Baselib_ErrorCode.Success) #endif { Disconnect(); return; } // Disconnection didn't occur, but we could still be waiting on a connection #if UNITY_WEBGL if (WebSocketIsConnecting() == 1) #else if (pollFd.resultEvents != Baselib_Socket_PollEvents.Connected) #endif { return; } #if ENABLE_PROFILER // While the profiler is generally lock-free, this ensures no new threads or markers are initialized and used // after sending the session stream (which contains the identification for new threads and markers) // but just before sending the related stream using this new thread or marker. The timing is pretty specific // but does happen - especially with threads unsynchronized from the main thread - such as the render thread. // // This will not have any performance implications once a marker or thread is intialized so typically we'll // stall for a couple microseconds on, say, the first frame or two and then no longer have contention. Profiler.PlayerConnectionMt_LockProfilerHashTables(); ProfilerProtocolSession.SendNewMarkersAndThreads(); ProfilerProtocolSession.SendProfilerStats(); // Calculated per frame ProfilerStats.GatheredStats = ProfilerModes.ProfileDisabled; unsafe { // It's ugly here, but this needs to be before other profiler data that is sent - so we do it manually // and only if we know we're going to TrySubmitAll() after other checks above ProfilerProtocolSession.streamSession.buffer->TrySubmitStream(true); } #endif MessageStreamManager.TrySubmitAll(); #if ENABLE_PROFILER Profiler.PlayerConnectionMt_UnlockProfilerHashTables(); #endif Receive(); if (!Connected) { return; } Transmit(); }
public unsafe void TestProfilerSessionEmitsMarkerInfoBeforeMarkerThreaded() { SpoofInitListenInternal(); Connection.ConnectDirect(spoofIp, spoofPort); Baselib_Timer_WaitForAtLeast(200); SpoofAcceptInternal(); ProfilerTestFixture.ReceiveConnectMessage(); ManualResetEvent mre = new ManualResetEvent(false); HashSet <uint> markerDefined = new HashSet <uint>(); Thread[] threads = new Thread[16]; for (int i = 0; i < threads.Length; i++) { threads[i] = new Thread(() => { for (ulong g = 0; g < 8; g++) { mre.WaitOne(); ProfilerMarker marker = new ProfilerMarker($"{Baselib_Timer_GetHighPrecisionTimerTicks() + g * 2} test"); ProfilerMarker marker2 = new ProfilerMarker($"{Baselib_Timer_GetHighPrecisionTimerTicks() + g * 2 + 1} test2"); marker.Begin(); marker2.Begin(); marker2.End(); marker.End(); Thread.Sleep(100); } }); threads[i].Start(); } // This test is set up to try to make new markers get created in the midst of buffers being sent over // the player connection. If timed just right without proper locks, we can get into a situation where // marker begin sample and marker end sample messages are sent without the marker info having been sent first. // (It ends up in the next frame's player connection datastream) for (int g = 0; g < 16; g++) { if ((g & 1) == 0) { mre.Set(); } ProfilerProtocolSession.SendNewMarkersAndThreads(); SendAndValidate(markerDefined); if ((g & 1) == 1) { mre.Reset(); } Thread.Sleep(50); } for (int i = 0; i < threads.Length; i++) { threads[i].Join(); } ProfilerProtocolSession.SendNewMarkersAndThreads(); SendAndValidate(markerDefined); SpoofExitInternal(); }