Skip to content

Commit 635fff6

Browse files
committed
Handle removing mem callbacks from within a callback (resolves #1823)
1 parent ceb64ce commit 635fff6

File tree

1 file changed

+29
-5
lines changed

1 file changed

+29
-5
lines changed

src/BizHawk.Emulation.Common/Base Implementations/MemoryCallbackSystem.cs

Lines changed: 29 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
using System.Collections.Generic;
77
using System.Collections.ObjectModel;
88
using System.Collections.Specialized;
9+
using System.Diagnostics;
910

1011
namespace BizHawk.Emulation.Common
1112
{
@@ -67,15 +68,38 @@ public void Add(IMemoryCallback callback)
6768

6869
private static void Call(ObservableCollection<IMemoryCallback> cbs, uint addr, uint value, uint flags, string scope)
6970
{
70-
// ReSharper disable once ForCanBeConvertedToForeach
71-
// Intentionally a for loop - https://github.com/TASEmulators/BizHawk/issues/1823
72-
for (int i = 0; i < cbs.Count; i++)
71+
var cbsCopy = cbs.ToArray();
72+
var e = cbsCopy.Length;
73+
var i = -1;
74+
void UpdateIndexAndEndpoint(object _, NotifyCollectionChangedEventArgs args)
7375
{
74-
if (!cbs[i].Address.HasValue || (cbs[i].Scope == scope && cbs[i].Address == (addr & cbs[i].AddressMask)))
76+
// this was helpful: https://www.codeproject.com/Articles/1004644/ObservableCollection-Simply-Explained
77+
switch (args.Action)
7578
{
76-
cbs[i].Callback(addr, value, flags);
79+
case NotifyCollectionChangedAction.Add:
80+
Debug.Assert(args.NewStartingIndex >= e);
81+
// no-op
82+
break;
83+
case NotifyCollectionChangedAction.Remove:
84+
Debug.Assert(args.OldItems.Count is 1);
85+
e--;
86+
if (args.OldStartingIndex <= i) i--; // if ==, i will be decremented and incremented, so it ends up pointing to the element which was at i + 1, now at i
87+
break;
88+
default:
89+
Debug.WriteLine("Unexpected operation on memory callback collection!");
90+
break;
7791
}
7892
}
93+
cbs.CollectionChanged += UpdateIndexAndEndpoint;
94+
while (++i < e)
95+
{
96+
var cb = cbsCopy[i];
97+
if (!cb.Address.HasValue || (cb.Scope == scope && cb.Address == (addr & cb.AddressMask)))
98+
{
99+
cb.Callback(addr, value, flags);
100+
}
101+
}
102+
cbs.CollectionChanged -= UpdateIndexAndEndpoint;
79103
}
80104

81105
public void CallMemoryCallbacks(uint addr, uint value, uint flags, string scope)

0 commit comments

Comments
 (0)