Skip to content

Commit 2bfbcb9

Browse files
authored
fix: improve dispose/cancellation error handling (#28)
1 parent 6a73a18 commit 2bfbcb9

File tree

14 files changed

+706
-281
lines changed

14 files changed

+706
-281
lines changed

Packages/mygamedevtools-scene-loader/Runtime/Interfaces/ISceneLoaderAsync.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Cysharp.Threading.Tasks;
33
#endif
44
using System;
5+
using System.Threading;
56
using System.Threading.Tasks;
67
using UnityEngine;
78
using UnityEngine.SceneManagement;

Packages/mygamedevtools-scene-loader/Runtime/Interfaces/ISceneManager.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ public interface ISceneManager : IDisposable
5353
/// <param name="sceneInfos">References to all scenes to load.</param>
5454
/// <param name="setIndexActive">Index of the desired scene to set active, based on the <paramref name="sceneInfos"/> array.</param>
5555
/// <param name="progress">Object to report the loading operations progress to, from 0 to 1.</param>
56+
/// <param name="token">Optional token to manually cancel the operation. Note that Unity Scene Manager operations cannot be manually canceled and will continue to run.</param>
5657
/// <returns>A <see cref="System.Threading.Tasks.ValueTask{TResult}"/> with all scenes loaded.</returns>
5758
/// <exception cref="ArgumentException">When scene info group is null, empty or the setIndexName is bigger than the scene length.</exception>
5859
/// <exception cref="InvalidOperationException">When the provided scene info group fails to produce valid load scene operations.</exception>
@@ -65,6 +66,7 @@ public interface ISceneManager : IDisposable
6566
/// <param name="sceneInfo">A reference to the scene that's going to be loaded.</param>
6667
/// <param name="setActive">Should the loaded scene be enabled as the active scene?</param>
6768
/// <param name="progress">Object to report the loading operation progress to, from 0 to 1.</param>
69+
/// <param name="token">Optional token to manually cancel the operation. Note that Unity Scene Manager operations cannot be manually canceled and will continue to run.</param>
6870
/// <returns>A <see cref="System.Threading.Tasks.ValueTask{TResult}"/> with the loaded scene as the result.</returns>
6971
/// <exception cref="ArgumentException">When scene info is null.</exception>
7072
/// <exception cref="InvalidOperationException">When the provided scene info fails to produce a valid load scene operation.</exception>
@@ -74,6 +76,7 @@ public interface ISceneManager : IDisposable
7476
/// Unloads all scenes provided by the <paramref name="sceneInfos"/> array in parallel.
7577
/// </summary>
7678
/// <param name="sceneInfos">Reference to all scenes to unload.</param>
79+
/// <param name="token">Optional token to manually cancel the operation. Note that Unity Scene Manager operations cannot be manually canceled and will continue to run.</param>
7780
/// <returns>
7881
/// A <see cref="System.Threading.Tasks.ValueTask{TResult}"/> with all the unloaded scenes.
7982
/// <br/>
@@ -87,6 +90,7 @@ public interface ISceneManager : IDisposable
8790
/// Unloads a scene referenced by the <paramref name="sceneInfo"/>.
8891
/// </summary>
8992
/// <param name="sceneInfo">A reference to the scene that's going to be unloaded.</param>
93+
/// <param name="token">Optional token to manually cancel the operation. Note that Unity Scene Manager operations cannot be manually canceled and will continue to run.</param>
9094
/// <returns>
9195
/// A <see cref="System.Threading.Tasks.ValueTask{TResult}"/> with the unloaded scene as the result.
9296
/// <br/>

Packages/mygamedevtools-scene-loader/Runtime/Managers/SceneManager.cs

Lines changed: 11 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,14 @@ public class SceneManager : ISceneManager, ISceneManagerReporter
2929

3030
readonly List<Scene> _unloadingScenes = new List<Scene>();
3131
readonly List<Scene> _loadedScenes = new List<Scene>();
32-
readonly CancellationTokenSource _lifetimeToken = new CancellationTokenSource();
32+
readonly CancellationTokenSource _lifetimeTokenSource = new CancellationTokenSource();
3333

3434
Scene _activeScene;
3535

3636
public void Dispose()
3737
{
38-
_lifetimeToken.Cancel();
39-
_lifetimeToken.Dispose();
38+
_lifetimeTokenSource.Cancel();
39+
_lifetimeTokenSource.Dispose();
4040

4141
_unloadingScenes.Clear();
4242
_loadedScenes.Clear();
@@ -82,82 +82,32 @@ public Scene GetLoadedSceneByName(string name)
8282

8383
public async ValueTask<Scene[]> LoadScenesAsync(ILoadSceneInfo[] sceneInfos, int setIndexActive = -1, IProgress<float> progress = null, CancellationToken token = default)
8484
{
85-
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeToken.Token, token);
86-
try
87-
{
88-
return await LoadScenesAsync_Internal(sceneInfos, setIndexActive, progress, linkedSource.Token);
89-
}
90-
catch (OperationCanceledException cancelException)
91-
{
92-
Debug.LogWarningFormat("[{0}] LoadScenesAsync was canceled. Exception:\n{1}", GetType().Name, cancelException);
93-
throw;
94-
}
95-
finally
96-
{
97-
linkedSource.Dispose();
98-
}
85+
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeTokenSource.Token, token);
86+
return await LoadScenesAsync_Internal(sceneInfos, setIndexActive, progress, linkedSource.Token).RunAndDisposeToken(linkedSource);
9987
}
10088

10189
public async ValueTask<Scene> LoadSceneAsync(ILoadSceneInfo sceneInfo, bool setActive = false, IProgress<float> progress = null, CancellationToken token = default)
10290
{
10391
sceneInfo = sceneInfo ?? throw new NullReferenceException($"[{GetType().Name}] Provided scene info is null.");
10492

105-
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeToken.Token, token);
106-
Scene[] loadedScenes = null;
107-
try
108-
{
109-
loadedScenes = await LoadScenesAsync_Internal(new ILoadSceneInfo[] { sceneInfo }, setActive ? 0 : -1, progress, linkedSource.Token);
110-
}
111-
catch (OperationCanceledException cancelException)
112-
{
113-
Debug.LogWarningFormat("[{0}] LoadSceneAsync was canceled. Exception:\n{1}", GetType().Name, cancelException);
114-
throw;
115-
}
116-
finally
117-
{
118-
linkedSource.Dispose();
119-
}
93+
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeTokenSource.Token, token);
94+
Scene[] loadedScenes = await LoadScenesAsync_Internal(new ILoadSceneInfo[] { sceneInfo }, setActive ? 0 : -1, progress, linkedSource.Token).RunAndDisposeToken(linkedSource);
12095

12196
return loadedScenes != null && loadedScenes.Length > 0 ? loadedScenes[0] : default;
12297
}
12398

12499
public async ValueTask<Scene[]> UnloadScenesAsync(ILoadSceneInfo[] sceneInfos, CancellationToken token = default)
125100
{
126-
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeToken.Token, token);
127-
try
128-
{
129-
return await UnloadScenesAsync_Internal(sceneInfos, linkedSource.Token);
130-
}
131-
catch (OperationCanceledException cancelException)
132-
{
133-
Debug.LogWarningFormat("[{0}] UnloadScenesAsync was canceled. Exception:\n{1}", GetType().Name, cancelException);
134-
throw;
135-
}
136-
finally
137-
{
138-
linkedSource.Dispose();
139-
}
101+
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeTokenSource.Token, token);
102+
return await UnloadScenesAsync_Internal(sceneInfos, linkedSource.Token).RunAndDisposeToken(linkedSource);
140103
}
141104

142105
public async ValueTask<Scene> UnloadSceneAsync(ILoadSceneInfo sceneInfo, CancellationToken token = default)
143106
{
144107
sceneInfo = sceneInfo ?? throw new ArgumentNullException(nameof(sceneInfo), $"[{GetType().Name}] Provided scene info is null.");
145108

146-
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeToken.Token, token);
147-
Scene[] unloadedScenes = null;
148-
try
149-
{
150-
unloadedScenes = await UnloadScenesAsync_Internal(new ILoadSceneInfo[] { sceneInfo }, linkedSource.Token);
151-
}
152-
catch (OperationCanceledException cancelException)
153-
{
154-
Debug.LogWarningFormat("[{0}] UnloadSceneAsync was canceled. Exception:\n{1}", GetType().Name, cancelException);
155-
throw;
156-
}
157-
finally
158-
{
159-
linkedSource.Dispose();
160-
}
109+
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeTokenSource.Token, token);
110+
Scene[] unloadedScenes = await UnloadScenesAsync_Internal(new ILoadSceneInfo[] { sceneInfo }, linkedSource.Token).RunAndDisposeToken(linkedSource);
161111

162112
return unloadedScenes != null && unloadedScenes.Length > 0 ? unloadedScenes[0] : default;
163113
}

Packages/mygamedevtools-scene-loader/Runtime/Managers/SceneManagerAddressable.cs

Lines changed: 11 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,14 @@ public class SceneManagerAddressable : ISceneManager, ISceneManagerReporter
3030

3131
readonly List<SceneInstance> _unloadingScenes = new List<SceneInstance>();
3232
readonly List<SceneInstance> _loadedScenes = new List<SceneInstance>();
33-
readonly CancellationTokenSource _lifetimeToken = new CancellationTokenSource();
33+
readonly CancellationTokenSource _lifetimeTokenSource = new CancellationTokenSource();
3434

3535
SceneInstance _activeSceneInstance;
3636

3737
public void Dispose()
3838
{
39-
_lifetimeToken.Cancel();
40-
_lifetimeToken.Dispose();
39+
_lifetimeTokenSource.Cancel();
40+
_lifetimeTokenSource.Dispose();
4141

4242
_unloadingScenes.Clear();
4343
_loadedScenes.Clear();
@@ -84,82 +84,32 @@ public Scene GetLoadedSceneByName(string name)
8484

8585
public async ValueTask<Scene[]> LoadScenesAsync(ILoadSceneInfo[] sceneInfos, int setIndexActive = -1, IProgress<float> progress = null, CancellationToken token = default)
8686
{
87-
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeToken.Token, token);
88-
try
89-
{
90-
return await LoadScenesAsync_Internal(sceneInfos, setIndexActive, progress, linkedSource.Token);
91-
}
92-
catch (OperationCanceledException cancelException)
93-
{
94-
Debug.LogWarningFormat("[{0}] LoadScenesAsync was canceled. Exception:\n{1}", GetType().Name, cancelException);
95-
throw;
96-
}
97-
finally
98-
{
99-
linkedSource.Dispose();
100-
}
87+
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeTokenSource.Token, token);
88+
return await LoadScenesAsync_Internal(sceneInfos, setIndexActive, progress, linkedSource.Token).RunAndDisposeToken(linkedSource);
10189
}
10290

10391
public async ValueTask<Scene> LoadSceneAsync(ILoadSceneInfo sceneInfo, bool setActive = false, IProgress<float> progress = null, CancellationToken token = default)
10492
{
10593
sceneInfo = sceneInfo ?? throw new NullReferenceException($"[{GetType().Name}] Provided scene info is null.");
10694

107-
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeToken.Token, token);
108-
Scene[] loadedScenes = null;
109-
try
110-
{
111-
loadedScenes = await LoadScenesAsync_Internal(new ILoadSceneInfo[] { sceneInfo }, setActive ? 0 : -1, progress, linkedSource.Token);
112-
}
113-
catch (OperationCanceledException cancelException)
114-
{
115-
Debug.LogWarningFormat("[{0}] LoadSceneAsync was canceled. Exception:\n{1}", GetType().Name, cancelException);
116-
throw;
117-
}
118-
finally
119-
{
120-
linkedSource.Dispose();
121-
}
95+
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeTokenSource.Token, token);
96+
Scene[] loadedScenes = await LoadScenesAsync_Internal(new ILoadSceneInfo[] { sceneInfo }, setActive ? 0 : -1, progress, linkedSource.Token).RunAndDisposeToken(linkedSource);
12297

12398
return loadedScenes != null && loadedScenes.Length > 0 ? loadedScenes[0] : default;
12499
}
125100

126101
public async ValueTask<Scene[]> UnloadScenesAsync(ILoadSceneInfo[] sceneInfos, CancellationToken token = default)
127102
{
128-
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeToken.Token, token);
129-
try
130-
{
131-
return await UnloadScenesAsync_Internal(sceneInfos, linkedSource.Token);
132-
}
133-
catch (OperationCanceledException cancelException)
134-
{
135-
Debug.LogWarningFormat("[{0}] UnloadScenesAsync was canceled. Exception:\n{1}", GetType().Name, cancelException);
136-
throw;
137-
}
138-
finally
139-
{
140-
linkedSource.Dispose();
141-
}
103+
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeTokenSource.Token, token);
104+
return await UnloadScenesAsync_Internal(sceneInfos, linkedSource.Token).RunAndDisposeToken(linkedSource);
142105
}
143106

144107
public async ValueTask<Scene> UnloadSceneAsync(ILoadSceneInfo sceneInfo, CancellationToken token = default)
145108
{
146109
sceneInfo = sceneInfo ?? throw new ArgumentNullException(nameof(sceneInfo), $"[{GetType().Name}] Provided scene info is null.");
147110

148-
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeToken.Token, token);
149-
Scene[] unloadedScenes = null;
150-
try
151-
{
152-
unloadedScenes = await UnloadScenesAsync_Internal(new ILoadSceneInfo[] { sceneInfo }, linkedSource.Token);
153-
}
154-
catch (OperationCanceledException cancelException)
155-
{
156-
Debug.LogWarningFormat("[{0}] UnloadSceneAsync was canceled. Exception:\n{1}", GetType().Name, cancelException);
157-
throw;
158-
}
159-
finally
160-
{
161-
linkedSource.Dispose();
162-
}
111+
CancellationTokenSource linkedSource = CancellationTokenSource.CreateLinkedTokenSource(_lifetimeTokenSource.Token, token);
112+
Scene[] unloadedScenes = await UnloadScenesAsync_Internal(new ILoadSceneInfo[] { sceneInfo }, linkedSource.Token).RunAndDisposeToken(linkedSource);
163113

164114
return unloadedScenes != null && unloadedScenes.Length > 0 ? unloadedScenes[0] : default;
165115
}

0 commit comments

Comments
 (0)