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();
        }