1
0
mirror of https://github.com/ppy/osu.git synced 2026-06-03 02:31:25 +08:00

Fix leak from polling chat client being initialised too early

This one is quite dumb.

`OsuGame` uses
[`loadComponentSingleFile`](https://github.com/ppy/osu/blob/15878f7f9fc7088494d3b66e98a7bc1004a1a06d/osu.Game/OsuGame.cs#L1228)
to load the `ChannelManager`. Importantly, this process does _not_ add
the component to any place in the hierarchy where it would normally be
disposed - this includes `InternalChildren`, but _also_ a lesser known
list of [currently-loading
components](https://github.com/ppy/osu-framework/blob/cfb0d7b4b673583f0cf56273e94352769aa5bc9a/osu.Framework/Graphics/Containers/CompositeDrawable.cs#L316-L323)
(those which have been sent through a `LoadComponentAsync` call).

The end result of this is that, `ChannelManager` creates the
`IChatClient` in its constructor, expecting to be able to dispose it,
but `Dispose` is never called!

And the failure case here is that `PollingChatClient` creates a
background task to continuously poll the API, unfortunately keeping a
reference to the rest of the world in the process.
This commit is contained in:
Dan Balasescu
2025-07-08 20:39:08 +09:00
Unverified
parent 2f374555dc
commit 02cb93d854
+7 -6
View File
@@ -9,6 +9,7 @@ using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Extensions.ObjectExtensions;
using osu.Framework.Graphics.Containers;
using osu.Framework.Logging;
using osu.Framework.Threading;
@@ -64,7 +65,6 @@ namespace osu.Game.Online.Chat
public IBindableList<Channel> AvailableChannels => availableChannels;
private readonly IAPIProvider api;
private readonly IChatClient chatClient;
[Resolved]
private UserLookupCache users { get; set; }
@@ -72,6 +72,7 @@ namespace osu.Game.Online.Chat
private readonly IBindable<APIState> apiState = new Bindable<APIState>();
private ScheduledDelegate scheduledAck;
private IChatClient chatClient = null!;
private long? lastSilenceMessageId;
private uint? lastSilenceId;
@@ -79,14 +80,13 @@ namespace osu.Game.Online.Chat
{
this.api = api;
chatClient = api.GetChatClient();
CurrentChannel.ValueChanged += currentChannelChanged;
}
[BackgroundDependencyLoader]
private void load()
{
chatClient = api.GetChatClient();
chatClient.ChannelJoined += ch => Schedule(() => joinChannel(ch));
chatClient.ChannelParted += ch => Schedule(() => leaveChannel(getChannel(ch), false));
chatClient.NewMessages += msgs => Schedule(() => addMessages(msgs));
@@ -282,8 +282,7 @@ namespace osu.Game.Online.Chat
// Check if the user has joined the requested channel already.
// This uses the channel name for comparison as the PM user's username is unavailable after a restart.
var privateChannel = JoinedChannels.FirstOrDefault(
c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Name.Equals(content, StringComparison.OrdinalIgnoreCase));
var privateChannel = JoinedChannels.FirstOrDefault(c => c.Type == ChannelType.PM && c.Users.Count == 1 && c.Name.Equals(content, StringComparison.OrdinalIgnoreCase));
if (privateChannel != null)
{
@@ -645,7 +644,9 @@ namespace osu.Game.Online.Chat
protected override void Dispose(bool isDisposing)
{
base.Dispose(isDisposing);
chatClient?.Dispose();
if (chatClient.IsNotNull())
chatClient.Dispose();
}
}