Skip to content

Commit 6a75011

Browse files
fix: Fixed a few memory leak cases when shutting down NetworkManager during Incoming Message Queue processing (#1323) (#1466)
* fix: Fixed memory leak on shutdown * fix: Fixed a few memory leak cases when shutting down NetworkManager during Incoming Message Queue processing * standards check * Fixed test not cleaning up quite correctly. * -Reworked the way shutdown is handled so it can't be processed while handling incoming messages. -Added tests for both ways to shut down to make sure they both work correctly -Fixed an issue in the snapshot system that was causing the new tests to fail * standards * Fixed broken test, slight naming change to be more accurate. Co-authored-by: Jaedyn Draper <284434+ShadauxCat@users.noreply.github.com>
1 parent 7433a26 commit 6a75011

File tree

10 files changed

+278
-24
lines changed

10 files changed

+278
-24
lines changed

com.unity.netcode.gameobjects/Runtime/Core/NetworkManager.cs

Lines changed: 41 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,9 @@ public NetworkPrefabHandler PrefabHandler
7373
}
7474
}
7575

76+
private bool m_ShuttingDown;
77+
private bool m_StopProcessingMessages;
78+
7679
private class NetworkManagerHooks : INetworkHooks
7780
{
7881
private NetworkManager m_NetworkManager;
@@ -116,7 +119,7 @@ public void OnAfterReceiveBatch(ulong senderId, int messageCount, int batchSizeI
116119

117120
public bool OnVerifyCanSend(ulong destinationId, Type messageType, NetworkDelivery delivery)
118121
{
119-
return true;
122+
return !m_NetworkManager.m_StopProcessingMessages;
120123
}
121124

122125
public bool OnVerifyCanReceive(ulong senderId, Type messageType)
@@ -134,7 +137,7 @@ public bool OnVerifyCanReceive(ulong senderId, Type messageType)
134137
return false;
135138
}
136139

137-
return true;
140+
return !m_NetworkManager.m_StopProcessingMessages;
138141
}
139142
}
140143

@@ -974,7 +977,7 @@ private void OnApplicationQuit()
974977
// Note that this gets also called manually by OnSceneUnloaded and OnApplicationQuit
975978
private void OnDestroy()
976979
{
977-
Shutdown();
980+
ShutdownInternal();
978981

979982
UnityEngine.SceneManagement.SceneManager.sceneUnloaded -= OnSceneUnloaded;
980983

@@ -994,13 +997,30 @@ private void DisconnectRemoteClient(ulong clientId)
994997
/// Globally shuts down the library.
995998
/// Disconnects clients if connected and stops server if running.
996999
/// </summary>
997-
public void Shutdown()
1000+
/// <param name="discardMessageQueue">
1001+
/// If false, any messages that are currently in the incoming queue will be handled,
1002+
/// and any messages in the outgoing queue will be sent, before the shutdown is processed.
1003+
/// If true, NetworkManager will shut down immediately, and any unprocessed or unsent messages
1004+
/// will be discarded.
1005+
/// </param>
1006+
public void Shutdown(bool discardMessageQueue = false)
9981007
{
9991008
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
10001009
{
10011010
NetworkLog.LogInfo(nameof(Shutdown));
10021011
}
10031012

1013+
m_ShuttingDown = true;
1014+
m_StopProcessingMessages = discardMessageQueue;
1015+
}
1016+
1017+
internal void ShutdownInternal()
1018+
{
1019+
if (NetworkLog.CurrentLogLevel <= LogLevel.Developer)
1020+
{
1021+
NetworkLog.LogInfo(nameof(ShutdownInternal));
1022+
}
1023+
10041024
if (IsServer)
10051025
{
10061026
// make sure all messages are flushed before transport disconnect clients
@@ -1077,6 +1097,7 @@ public void Shutdown()
10771097

10781098
if (SpawnManager != null)
10791099
{
1100+
SpawnManager.CleanupAllTriggers();
10801101
SpawnManager.DestroyNonSceneObjects();
10811102
SpawnManager.ServerResetShudownStateForSceneObjects();
10821103

@@ -1165,6 +1186,11 @@ private void OnNetworkPreUpdate()
11651186
return;
11661187
}
11671188

1189+
if (m_ShuttingDown && m_StopProcessingMessages)
1190+
{
1191+
return;
1192+
}
1193+
11681194
// Only update RTT here, server time is updated by time sync messages
11691195
var reset = NetworkTimeSystem.Advance(Time.deltaTime);
11701196
if (reset)
@@ -1181,9 +1207,18 @@ private void OnNetworkPreUpdate()
11811207

11821208
private void OnNetworkPostLateUpdate()
11831209
{
1184-
MessagingSystem.ProcessSendQueues();
1185-
NetworkMetrics.DispatchFrame();
1210+
1211+
if (!m_ShuttingDown || !m_StopProcessingMessages)
1212+
{
1213+
MessagingSystem.ProcessSendQueues();
1214+
NetworkMetrics.DispatchFrame();
1215+
}
11861216
SpawnManager.CleanupStaleTriggers();
1217+
1218+
if (m_ShuttingDown)
1219+
{
1220+
ShutdownInternal();
1221+
}
11871222
}
11881223

11891224
/// <summary>

com.unity.netcode.gameobjects/Runtime/Core/SnapshotSystem.cs

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -465,30 +465,45 @@ internal void ReadIndex(in SnapshotDataMessage message)
465465
}
466466
}
467467

468-
internal void SpawnObject(SnapshotSpawnCommand spawnCommand)
468+
internal void SpawnObject(SnapshotSpawnCommand spawnCommand, ulong srcClientId)
469469
{
470470
if (m_NetworkManager)
471471
{
472-
var networkObject = m_NetworkManager.SpawnManager.CreateLocalNetworkObject(false,
473-
spawnCommand.GlobalObjectIdHash, spawnCommand.OwnerClientId, (spawnCommand.ParentNetworkId == spawnCommand.NetworkObjectId) ? spawnCommand.NetworkObjectId : spawnCommand.ParentNetworkId, spawnCommand.ObjectPosition,
474-
spawnCommand.ObjectRotation);
472+
NetworkObject networkObject;
473+
if (spawnCommand.ParentNetworkId == spawnCommand.NetworkObjectId)
474+
{
475+
networkObject = m_NetworkManager.SpawnManager.CreateLocalNetworkObject(false,
476+
spawnCommand.GlobalObjectIdHash, spawnCommand.OwnerClientId, null, spawnCommand.ObjectPosition,
477+
spawnCommand.ObjectRotation);
478+
}
479+
else
480+
{
481+
networkObject = m_NetworkManager.SpawnManager.CreateLocalNetworkObject(false,
482+
spawnCommand.GlobalObjectIdHash, spawnCommand.OwnerClientId, spawnCommand.ParentNetworkId, spawnCommand.ObjectPosition,
483+
spawnCommand.ObjectRotation);
484+
}
485+
475486
m_NetworkManager.SpawnManager.SpawnNetworkObjectLocally(networkObject, spawnCommand.NetworkObjectId,
476487
true, spawnCommand.IsPlayerObject, spawnCommand.OwnerClientId, false);
488+
//todo: discuss with tools how to report shared bytes
489+
m_NetworkManager.NetworkMetrics.TrackObjectSpawnReceived(srcClientId, networkObject, 8);
477490
}
478491
else
479492
{
480493
MockSpawnObject(spawnCommand);
481494
}
482495
}
483496

484-
internal void DespawnObject(SnapshotDespawnCommand despawnCommand)
497+
internal void DespawnObject(SnapshotDespawnCommand despawnCommand, ulong srcClientId)
485498
{
486499
if (m_NetworkManager)
487500
{
488501
m_NetworkManager.SpawnManager.SpawnedObjects.TryGetValue(despawnCommand.NetworkObjectId,
489502
out NetworkObject networkObject);
490503

491504
m_NetworkManager.SpawnManager.OnDespawnObject(networkObject, true);
505+
//todo: discuss with tools how to report shared bytes
506+
m_NetworkManager.NetworkMetrics.TrackObjectDestroyReceived(srcClientId, networkObject, 8);
492507
}
493508
else
494509
{
@@ -497,7 +512,7 @@ internal void DespawnObject(SnapshotDespawnCommand despawnCommand)
497512
}
498513

499514

500-
internal void ReadSpawns(in SnapshotDataMessage message)
515+
internal void ReadSpawns(in SnapshotDataMessage message, ulong srcClientId)
501516
{
502517
SnapshotSpawnCommand spawnCommand;
503518
SnapshotDespawnCommand despawnCommand;
@@ -516,7 +531,7 @@ internal void ReadSpawns(in SnapshotDataMessage message)
516531

517532
// Debug.Log($"[Spawn] {spawnCommand.NetworkObjectId} {spawnCommand.TickWritten}");
518533

519-
SpawnObject(spawnCommand);
534+
SpawnObject(spawnCommand, srcClientId);
520535
}
521536
for (var i = 0; i < message.Despawns.Length; i++)
522537
{
@@ -532,7 +547,7 @@ internal void ReadSpawns(in SnapshotDataMessage message)
532547

533548
// Debug.Log($"[DeSpawn] {despawnCommand.NetworkObjectId} {despawnCommand.TickWritten}");
534549

535-
DespawnObject(despawnCommand);
550+
DespawnObject(despawnCommand, srcClientId);
536551
}
537552
}
538553

@@ -1077,7 +1092,7 @@ internal void HandleSnapshot(ulong clientId, in SnapshotDataMessage message)
10771092
ReadBuffer(message);
10781093
ReadIndex(message);
10791094
ReadAcks(clientId, m_ClientData[clientId], message, GetConnectionRtt(clientId));
1080-
ReadSpawns(message);
1095+
ReadSpawns(message, clientId);
10811096
}
10821097

10831098
// todo --M1--

com.unity.netcode.gameobjects/Runtime/Messaging/MessagingSystem.cs

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ public MessagingSystem(IMessageSender messageSender, object owner, IMessageProvi
100100
}
101101
}
102102

103-
public void Dispose()
103+
public unsafe void Dispose()
104104
{
105105
if (m_Disposed)
106106
{
@@ -113,6 +113,14 @@ public void Dispose()
113113
{
114114
CleanupDisconnectedClient(kvp.Key);
115115
}
116+
117+
for (var queueIndex = 0; queueIndex < m_IncomingMessageQueue.Length; ++queueIndex)
118+
{
119+
// Avoid copies...
120+
ref var item = ref m_IncomingMessageQueue.GetUnsafeList()->ElementAt(queueIndex);
121+
item.Reader.Dispose();
122+
}
123+
116124
m_IncomingMessageQueue.Dispose();
117125
m_Disposed = true;
118126
}
@@ -253,11 +261,15 @@ public void HandleMessage(in MessageHeader header, FastBufferReader reader, ulon
253261

254262
internal unsafe void ProcessIncomingMessageQueue()
255263
{
256-
for (var i = 0; i < m_IncomingMessageQueue.Length; ++i)
264+
for (var index = 0; index < m_IncomingMessageQueue.Length; ++index)
257265
{
258266
// Avoid copies...
259-
ref var item = ref m_IncomingMessageQueue.GetUnsafeList()->ElementAt(i);
267+
ref var item = ref m_IncomingMessageQueue.GetUnsafeList()->ElementAt(index);
260268
HandleMessage(item.Header, item.Reader, item.SenderId, item.Timestamp);
269+
if (m_Disposed)
270+
{
271+
return;
272+
}
261273
}
262274

263275
m_IncomingMessageQueue.Clear();
@@ -461,16 +473,16 @@ internal unsafe void ProcessSendQueues()
461473
try
462474
{
463475
m_MessageSender.Send(clientId, queueItem.NetworkDelivery, queueItem.Writer);
476+
477+
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
478+
{
479+
m_Hooks[hookIdx].OnAfterSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery);
480+
}
464481
}
465482
finally
466483
{
467484
queueItem.Writer.Dispose();
468485
}
469-
470-
for (var hookIdx = 0; hookIdx < m_Hooks.Count; ++hookIdx)
471-
{
472-
m_Hooks[hookIdx].OnAfterSendBatch(clientId, queueItem.BatchHeader.BatchSize, queueItem.Writer.Length, queueItem.NetworkDelivery);
473-
}
474486
}
475487
sendQueueItem.Clear();
476488
}

com.unity.netcode.gameobjects/Runtime/Spawning/NetworkSpawnManager.cs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -154,6 +154,24 @@ internal unsafe void CleanupStaleTriggers()
154154
m_Triggers.Remove(staleKeys[i]);
155155
}
156156
}
157+
/// <summary>
158+
/// Cleans up any trigger that's existed for more than a second.
159+
/// These triggers were probably for situations where a request was received after a despawn rather than before a spawn.
160+
/// </summary>
161+
internal void CleanupAllTriggers()
162+
{
163+
foreach (var kvp in m_Triggers)
164+
{
165+
foreach (var data in kvp.Value.TriggerData)
166+
{
167+
data.Reader.Dispose();
168+
}
169+
170+
kvp.Value.TriggerData.Dispose();
171+
}
172+
173+
m_Triggers.Clear();
174+
}
157175

158176
internal void RemoveOwnership(NetworkObject networkObject)
159177
{

com.unity.netcode.gameobjects/Tests/Editor/StartStopTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ public void TestStartupServerState()
4545
public void TestFlagShutdown()
4646
{
4747
m_NetworkManager.StartServer();
48-
m_NetworkManager.Shutdown();
48+
m_NetworkManager.ShutdownInternal();
4949

5050
Assert.False(m_NetworkManager.IsServer);
5151
Assert.False(m_NetworkManager.IsClient);

0 commit comments

Comments
 (0)