godotengine / godot

Godot Engine – Multi-platform 2D and 3D game engine
https://godotengine.org
MIT License
91.26k stars 21.22k forks source link

ENet server memory leak when clients disconnect abruptly #98358

Open htmiel opened 1 month ago

htmiel commented 1 month ago

Tested versions

Reproducible in Godot 4.4 dev3, Godot 4.3 stable

System information

Godot v4.4.dev3 - Windows 10.0.19045 - Multi-window, 2 monitors - OpenGL 3 (Compatibility) - NVIDIA GeForce RTX 3060 (NVIDIA; 32.0.15.6109) - 13th Gen Intel(R) Core(TM) i9-13900K (32 threads)

Issue description

I am building a server app for my multiplayer game using ENet Connection with DTLS, and I think I found a memory leak: when clients are closed suddenly with kill process, they disconnect on the server side but without freeing the used memory.

In addition, after the Server app reaches ~500MB of leaked memory, I observed a noticeble CPU usage, even when there are apparently no clients connected.

The attached MRP was tested on 3 PCs: locally, over LAN, and over Internet, in both debug and release builds.

Steps to reproduce

Testing with the attached MRP:

  1. Start the Client and Server apps.
  2. Press the button in the Client to add 300 clients. This will add ~75MB to the Server app memory.
  3. Close the Client window normally (from X button or Alt+F4). The Server app memory will decrease and the clients will disconnect.
  4. Repeat 1 & 2, but this time close the Client app by killing its process from Task Manager (or using the Stop button in the editor). The clients on the Server app will disconnect as usual, but the memory will not decrease.

Minimal reproduction project (MRP)

ENet Client Server.zip

htmiel commented 3 weeks ago

I just noticed that the issue happens actually on any kind of client disconnect, by calling on the client side any of the disconnect methods:

peer.peer_disconnect()
peer.peer_disconnect_later()
peer.peer_disconnect_now()

The memory used by the server is not freed until the client app gracefully closes.

htmiel commented 2 hours ago

Alternatively, would it be possible to have the option to manually free the memory used by an ENetPacketPeer? (since I do receive the disconnect event when servicing the connection)

I noticed the ENetPacketPeer has a reset() method, and I tried to call it when a peer disconnects, to hopefully clear the memory, but I get the error: Peer not connected.

As the ENetPacketPeer is based on RefCounted, I also tied to call the unreference() method when the peer disconnects, but it had no effect.

In case I am doing something wrong in using ENetConnection/ENetPacketPeer, I would much appreciate any guidance.