From 70ca48cc6b65a2e2973fc5c6502588d9cf092ee4 Mon Sep 17 00:00:00 2001 From: Kamron Batman <3953314+kamronbatman@users.noreply.github.com> Date: Sun, 19 Jul 2026 22:21:09 -0700 Subject: [PATCH] =?UTF-8?q?docs(advanced-search):=20B=20=E2=80=94=20docume?= =?UTF-8?q?nt=20residual=20worker/event-loop=20read=20race=20and=20its=20b?= =?UTF-8?q?ounds?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Engines/Advanced Search/AdvancedSearchGump.cs | 4 +++- .../Advanced Search/AdvancedSearchThreadWorker.cs | 15 +++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs index 9045adf08..cc3a0a20d 100644 --- a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs +++ b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchGump.cs @@ -776,7 +776,9 @@ public class AdvancedSearchGump : Gump var type = Filter.FilterType ? Filter.Type : null; - // Push the entities + // Push the entities. Workers read entity state concurrently with this thread's own + // subsequent mutations of the world; see the class comment on + // AdvancedSearchThreadWorker for the accepted, bounded race this implies. foreach (var item in World.Items.Values) { if (type == null || type.IsInstanceOfType(item)) diff --git a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchThreadWorker.cs b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchThreadWorker.cs index 47a2ead6e..74dd6a41d 100644 --- a/Projects/UOContent/Engines/Advanced Search/AdvancedSearchThreadWorker.cs +++ b/Projects/UOContent/Engines/Advanced Search/AdvancedSearchThreadWorker.cs @@ -9,6 +9,21 @@ using Server.Multis; namespace Server.Engines.AdvancedSearch; +/// +/// Runs entity filtering on a dedicated background thread while the main event loop keeps +/// mutating those same entities. This is an intentional, bounded race: workers read live +/// / state (location, map, properties, etc.) without any +/// synchronization with the main thread. Torn reads of multi-field value types like +/// can yield a stale-but-plausible combination of coordinates rather than +/// the true before- or after-mutation value, and any exception thrown by a getter (e.g. from a +/// property being torn down mid-read) is caught and logged per-entity in +/// , which simply skips that entity instead of crashing the worker +/// or the search. Results are therefore a best-effort snapshot that may occasionally omit or +/// misreport an entity that was concurrently modified or deleted, never a page fault or bad +/// server state. Fully eliminating the race would require copying the fields each filter reads +/// onto the main thread before handing entities off to workers; that snapshotting is a larger +/// change and is deferred. +/// public class AdvancedSearchThreadWorker { private static readonly ILogger _logger = LogFactory.GetLogger(typeof(AdvancedSearchThreadWorker));