Closed maasaimosh closed 5 years ago
Thanks for the very detailed issue report! I will try to reproduce this tomorrow.
The parts of code you've modified to monitor for multiple threads are not meant to be multithreaded. The issue appears to be that the editor calling into this code for some reason on a background thread, thread 9 in your tests.
Okay... I've looked in more detail about what is calling what. The Editor stuff may be a red herring. However, this means that deployed games without the Editor may still exhibit the bad behaviour.
There are still two threads running at the same time and clashing at important parts like ProcessPacketRelay()
and SendUnsequenced()
Using a stacktrace at the point where I detect a thread change in ServerRelay.ProcessPacketRelay()
The 'Editor' thread... originates straight out of ForgeRemastered threading. Perhaps merely logged within the Editor thread.
[Dissonance:Network] (16:31:13.229) ServerRelay`1: ServerRelay.ProcessPacketRelay(): Switch thread id [ 1 ] to [ 4 ]
Stack Trace:
at Dissonance.Networking.Server.ServerRelay`1[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].ProcessPacketRelay(PacketReader ByRef reader, Boolean reliable)
at Dissonance.Networking.BaseServer`3[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].NetworkReceivedPacket(ForgeRemasteredPeer source, ArraySegment`1 data)
at Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer.ForgeNetworkMessageReceived(BeardedManStudios.Forge.Networking.NetworkingPlayer player, BeardedManStudios.Forge.Networking.Frame.FrameStream frame, BeardedManStudios.Forge.Networking.NetWorker networker)
at BeardedManStudios.Forge.Networking.NetWorker.OnMessageReceived(BeardedManStudios.Forge.Networking.NetworkingPlayer player, BeardedManStudios.Forge.Networking.Frame.FrameStream frame)
at BeardedManStudios.Forge.Networking.UDPServer.FireRead(BeardedManStudios.Forge.Networking.Frame.FrameStream frame, BeardedManStudios.Forge.Networking.NetworkingPlayer currentPlayer)
at BeardedManStudios.Forge.Networking.UDPServer.PacketSequenceComplete(BeardedManStudios.BMSByte data, Int32 groupId, Byte receivers, Boolean isReliable)
at BeardedManStudios.Forge.Networking.UDPPacketGroup.CompleteSequence(UInt64 id, BeardedManStudios.Forge.Networking.UDPPacketSequence sequence, BeardedManStudios.Forge.Networking.PacketComplete packetCompleteHandle, BeardedManStudios.Forge.Networking.NetWorker networker)
at BeardedManStudios.Forge.Networking.UDPPacketGroup.AddPacket(UDPPacket packet, BeardedManStudios.Forge.Networking.PacketComplete packetCompleteHandle, BeardedManStudios.Forge.Networking.NetWorker networker)
at BeardedManStudios.Forge.Networking.UDPPacketManager.AddPacket(UDPPacket packet, BeardedManStudios.Forge.Networking.PacketComplete packetCompleteHandle, BeardedManStudios.Forge.Networking.NetWorker networker)
at BeardedManStudios.Forge.Networking.UDPServer.ReadPacket(BeardedManStudios.BMSByte packet)
at BeardedManStudios.Forge.Networking.UDPServer.ReadClients()
at BeardedManStudios.Threading.Task+<>c__DisplayClass1_0.<Queue>b__0(System.Object state)
The normal update thread ( this will be expected codepath I presume ). This originates from Dissonance:
[Dissonance:Network] (16:31:13.262) ServerRelay`1: ServerRelay.ProcessPacketRelay(): Switch thread id [ 4 ] to [ 1 ]
Stack Trace:
at Dissonance.Networking.Server.ServerRelay`1[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].ProcessPacketRelay(PacketReader ByRef reader, Boolean reliable)
at Dissonance.Networking.BaseServer`3[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].NetworkReceivedPacket(ForgeRemasteredPeer source, ArraySegment`1 data)
at Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredCommsNetwork.PreprocessPacketToServer(ArraySegment`1 packet)
at Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient.Send(ArraySegment`1 packet, Int32 channel, Boolean reliable)
at Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient.SendUnreliable(ArraySegment`1 packet)
at Dissonance.Networking.BaseClient`3[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].Dissonance.Networking.Client.IClient<TPeer>.SendUnreliable(ArraySegment`1 packet)
at Dissonance.Networking.BaseClient`3[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].SendUnreliableP2P(System.Collections.Generic.List`1 destinations, ArraySegment`1 packet)
at Dissonance.Networking.BaseClient`3[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].Dissonance.Networking.Client.IClient<TPeer>.SendUnreliableP2P(System.Collections.Generic.List`1 destinations, ArraySegment`1 packet)
at Dissonance.Networking.Client.SendQueue`1[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].Update()
at Dissonance.Networking.BaseClient`3[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].RunUpdate(DateTime utcNow)
at Dissonance.Networking.BaseClient`3[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].Update()
at Dissonance.Networking.BaseCommsNetwork`5+Session[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Unit, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Unit, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].Update()
at Dissonance.Networking.BaseCommsNetwork`5[[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredServer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredClient, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredPeer, Assembly-CSharp, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Unit, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null],[Dissonance.Unit, Assembly-CSharp-firstpass, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]].Update()
at Dissonance.Integrations.ForgeNetworkingRemastered.ForgeRemasteredCommsNetwork.Update()
I guess one important thing to mention is that in my configuration, I am running both the Server and a Client.
This is because I don't want to have a separate dissonance server process to maintain.
Is that a configuration you have planned ?
The 'Editor' thread... originates straight out of ForgeRemastered threading
Aha, that certainly makes more sense that the editor itself calling things. It's possible the Dissonnce Forge Remastered integration doesn't properly handle any multithreading that's built into FNR itself - it looks like FNR is delivering packets (in OnMessageReceived
) on a background thread so perhaps we're not properly handling that (i.e. marshalling the event back onto the main thread before passing it to Dissonance).
in my configuration, I am running both the Server and a Client.
This configuration is built into Dissonance, we call it Host
mode where one single peer runs as both a Dissonance client and server. Have you made a modification to Dissonance ro run both, or are you just using the built in Host
mode?
I asked on the FNR Discord support channel and apparently rhe various events raised by FNR (e.g. playerDisconnected
and binaryMessageReceived
) will not be invoked on the main thread. I'm pretty sure that the Dissonance FNR integration does not handle this properly, I'll fix it first thing tomorrow!
I've fixed this and submitted an update to the asset store, that will probably take around a week to be approved and released. If anyone using FNR has this issue in the meantime please email me (martin@placeholder-software.co.uk) your invoice number and I can send you the updated package :)
This fix is now available on the asset store as v6.4.2 (they rushed it through for us since it was a critical bug).
Context
I was getting lots of exceptions when using Dissonance in the editor. The errors were suspicious of multi-threading problems ( simple loops running out of elements )
e.g.
which is a fairly simple
for loop
like:So I whacked in some thread logging and see two threads clashing on important codepaths
Sorting this out will help all your users
Expected Behavior
No exceptions in logs. Single thread accessing important collections at one time.
Actual Behavior
Worrying exceptions in logs. When thread checks put in, evidence of multiple threads accessing important codepaths at the same time
Workaround
None, needs code fix
Fix
a) synchronise important sections b) make sure only one thread ever calls important sections c) threadlocal structures... though this may still have a race on the data freshness level instead
Steps to Reproduce
Provide a detailed set of steps to reproduce the problem
Change ServerRelay.cs.ProcessPacketRelay() to log incoming thread ids
Run the code in a Unity Editor
Notice the logging ( error level ) shows two threads accessing this important call. Often at the same time :(
e.g. (abbreviated)
and then another thread:
Your Environment
DISSONANCE: 6.4.2 NETWORKING: ForgeRemastered UNITY: 2017.4.28 OS: Windows 10
Runtime: EDITOR MODE