fix(advanced-search): C/G follow-up - cover synchronous setup + test isolation in try/finally

This commit is contained in:
Kamron Batman 2026-07-19 21:53:29 -07:00
parent 98e3451927
commit f8c594779e
2 changed files with 78 additions and 62 deletions

View file

@ -77,10 +77,19 @@ public class AdvancedSearchWorkerTests
public void DoSearch_IsGuarded_AgainstReentry()
{
// White-box: flip the guard, assert a second entry is rejected, then clear.
// _searchInProgress is process-global static state; release it in finally so a
// failed assert here can't leak the guard into other tests.
Assert.False(AdvancedSearchGump.IsSearchInProgress);
Assert.True(AdvancedSearchGump.TryBeginSearch()); // acquires
Assert.False(AdvancedSearchGump.TryBeginSearch()); // rejected
AdvancedSearchGump.EndSearch(); // releases
Assert.True(AdvancedSearchGump.TryBeginSearch()); // acquires
try
{
Assert.False(AdvancedSearchGump.TryBeginSearch()); // rejected
}
finally
{
AdvancedSearchGump.EndSearch(); // releases
}
Assert.False(AdvancedSearchGump.IsSearchInProgress);
}
}

View file

@ -754,80 +754,87 @@ public class AdvancedSearchGump : Gump
_threadId = 0;
var autoSave = AutoSave.SavesEnabled;
if (autoSave)
AutoSave.SavesEnabled = false;
try
{
AutoSave.SavesEnabled = false;
}
_threadWorkers ??= new AdvancedSearchThreadWorker[Math.Max(Environment.ProcessorCount - 1, 1)];
_threadWorkers ??= new AdvancedSearchThreadWorker[Math.Max(Environment.ProcessorCount - 1, 1)];
var ignoreQueue = new ConcurrentQueue<IEntity>();
var results = new ConcurrentQueue<AdvancedSearchResult>();
var worldLocation = new WorldLocation(from.Location, from.Map);
var ignoreQueue = new ConcurrentQueue<IEntity>();
var results = new ConcurrentQueue<AdvancedSearchResult>();
var worldLocation = new WorldLocation(from.Location, from.Map);
for (var i = 0; i < _threadWorkers.Length; i++)
{
(_threadWorkers[i] ??= new AdvancedSearchThreadWorker()).Wake(worldLocation, Filter, results, ignoreQueue);
}
var type = Filter.FilterType ? Filter.Type : null;
// Push the entities
foreach (var item in World.Items.Values)
{
if (type == null || type.IsInstanceOfType(item))
for (var i = 0; i < _threadWorkers.Length; i++)
{
PushToWorkers(item);
(_threadWorkers[i] ??= new AdvancedSearchThreadWorker()).Wake(worldLocation, Filter, results, ignoreQueue);
}
}
foreach (var m in World.Mobiles.Values)
{
if (type == null || type.IsInstanceOfType(m))
{
PushToWorkers(m);
}
}
var type = Filter.FilterType ? Filter.Type : null;
ThreadPool.QueueUserWorkItem(state =>
{
try
// Push the entities
foreach (var item in World.Items.Values)
{
// Block until everything is processed
for (var i = 0; i < _threadWorkers.Length; i++)
if (type == null || type.IsInstanceOfType(item))
{
_threadWorkers[i].Sleep();
PushToWorkers(item);
}
}
var ignoredEntities = new HashSet<IEntity>(ignoreQueue);
// Force the GC to collect the ignored entities
ignoreQueue.Clear();
var resultsList = new List<AdvancedSearchResult>(results.Count);
foreach (var result in results)
foreach (var m in World.Mobiles.Values)
{
if (type == null || type.IsInstanceOfType(m))
{
if (!ignoredEntities.Contains(result.Entity))
PushToWorkers(m);
}
}
ThreadPool.QueueUserWorkItem(state =>
{
try
{
// Block until everything is processed
for (var i = 0; i < _threadWorkers.Length; i++)
{
resultsList.Add(result);
_threadWorkers[i].Sleep();
}
var ignoredEntities = new HashSet<IEntity>(ignoreQueue);
// Force the GC to collect the ignored entities
ignoreQueue.Clear();
var resultsList = new List<AdvancedSearchResult>(results.Count);
foreach (var result in results)
{
if (!ignoredEntities.Contains(result.Entity))
{
resultsList.Add(result);
}
}
SearchResults = resultsList.ToArray();
// Force the GC to collect the results
resultsList.Clear();
resultsList.TrimExcess();
// Send the gump on the main thread
Core.LoopContext.Post(() => Resend(from));
}
SearchResults = resultsList.ToArray();
// Force the GC to collect the results
resultsList.Clear();
resultsList.TrimExcess();
// Send the gump on the main thread
Core.LoopContext.Post(() => Resend(from));
}
finally
{
AutoSave.SavesEnabled = (bool)state!;
EndSearch();
}
}, autoSave);
finally
{
AutoSave.SavesEnabled = (bool)state!;
EndSearch();
}
}, autoSave);
}
catch
{
// Setup failed before the work item took ownership of the release.
AutoSave.SavesEnabled = autoSave;
EndSearch();
throw;
}
}
private void SetSortSwitches(int radioSwitch)