Skip to content

Commit 101df50

Browse files
committed
refactor: ensure thread safety for _collectionsByLocal; extracted add collection logic; removed duplicate calls
Adding / removing collections do not require framework thread access. It is only when assigning via AssignTemporaryCollection(), it needs to access `(ObjectManager)objects[actorIndex]` when framework thread is needed.
1 parent c4b6e4e commit 101df50

File tree

1 file changed

+15
-9
lines changed

1 file changed

+15
-9
lines changed

Penumbra/Collections/Manager/CollectionStorage.cs

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,7 @@ public ModCollection Create(string name, int index, ModCollection? duplicate)
3333
{
3434
var newCollection = duplicate?.Duplicate(name, CurrentCollectionId, index)
3535
?? ModCollection.CreateEmpty(name, CurrentCollectionId, index, _modStorage.Count);
36-
_collectionsByLocal[CurrentCollectionId] = newCollection;
37-
CurrentCollectionId += 1;
36+
Add(newCollection);
3837
return newCollection;
3938
}
4039

@@ -43,21 +42,29 @@ public ModCollection CreateFromData(Guid id, string name, int version, Dictionar
4342
{
4443
var newCollection = ModCollection.CreateFromData(_saveService, _modStorage,
4544
new ModCollectionIdentity(id, CurrentCollectionId, name, Count), version, allSettings, inheritances);
46-
_collectionsByLocal[CurrentCollectionId] = newCollection;
47-
CurrentCollectionId += 1;
45+
Add(newCollection);
4846
return newCollection;
4947
}
5048

5149
public ModCollection CreateTemporary(string name, int index, int globalChangeCounter)
5250
{
5351
var newCollection = ModCollection.CreateTemporary(name, CurrentCollectionId, index, globalChangeCounter);
54-
_collectionsByLocal[CurrentCollectionId] = newCollection;
55-
CurrentCollectionId += 1;
52+
Add(newCollection);
5653
return newCollection;
5754
}
5855

56+
/// <remarks> Atomically add to _collectionLocal and increments _currentCollectionIdValue. </remarks>
57+
private void Add(ModCollection newCollection)
58+
{
59+
_collectionsByLocal.AddOrUpdate(CurrentCollectionId,
60+
static (_, newColl) => newColl,
61+
static (_, _, newColl) => newColl,
62+
newCollection);
63+
Interlocked.Increment(ref _currentCollectionIdValue);
64+
}
65+
5966
public void Delete(ModCollection collection)
60-
=> _collectionsByLocal.Remove(collection.Identity.LocalId);
67+
=> _collectionsByLocal.TryRemove(collection.Identity.LocalId, out _);
6168

6269
/// <remarks> The empty collection is always available at Index 0. </remarks>
6370
private readonly List<ModCollection> _collections =
@@ -66,7 +73,7 @@ public void Delete(ModCollection collection)
6673
];
6774

6875
/// <remarks> A list of all collections ever created still existing by their local id. </remarks>
69-
private readonly Dictionary<LocalCollectionId, ModCollection>
76+
private readonly ConcurrentDictionary<LocalCollectionId, ModCollection>
7077
_collectionsByLocal = new() { [LocalCollectionId.Zero] = ModCollection.Empty };
7178

7279

@@ -186,7 +193,6 @@ public bool RemoveCollection(ModCollection collection)
186193
// Update indices.
187194
for (var i = collection.Identity.Index; i < Count; ++i)
188195
_collections[i].Identity.Index = i;
189-
_collectionsByLocal.Remove(collection.Identity.LocalId);
190196

191197
Penumbra.Messager.NotificationMessage($"Deleted collection {collection.Identity.AnonymizedName}.", NotificationType.Success, false);
192198
_communicator.CollectionChange.Invoke(CollectionType.Inactive, collection, null, string.Empty);

0 commit comments

Comments
 (0)