Skip to content

Commit 5da5177

Browse files
committed
store index atomically and via shared_ptr
1 parent 7cda32d commit 5da5177

File tree

2 files changed

+52
-35
lines changed

2 files changed

+52
-35
lines changed

src/cascadia/TerminalSettingsEditor/SearchIndex.cpp

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -54,13 +54,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
5454

5555
Editor::FilteredSearchResult FilteredSearchResult::CreateNoResultsItem(const winrt::hstring& query)
5656
{
57-
return winrt::make<FilteredSearchResult>(nullptr, nullptr, hstring{ fmt::format(fmt::runtime(std::wstring{ RS_(L"Search_NoResults") }), query) });
57+
return winrt::make<FilteredSearchResult>(nullptr, nullptr, nullptr, hstring{ fmt::format(fmt::runtime(std::wstring{ RS_(L"Search_NoResults") }), query) });
5858
}
5959

6060
// Creates a FilteredSearchResult with the given search index entry and runtime object.
6161
// The resulting search result will have a label like "<ProfileName>: <display text>" or "<ProfileName>".
6262
// This is so that we can reuse the display text from the search index, but also add additional context to which runtime object this search result maps to.
63-
Editor::FilteredSearchResult FilteredSearchResult::CreateRuntimeObjectItem(const LocalizedIndexEntry* searchIndexEntry, const Windows::Foundation::IInspectable& runtimeObj)
63+
Editor::FilteredSearchResult FilteredSearchResult::CreateRuntimeObjectItem(std::shared_ptr<const IndexData> indexData, const LocalizedIndexEntry* searchIndexEntry, const Windows::Foundation::IInspectable& runtimeObj)
6464
{
6565
hstring runtimeObjLabel{};
6666
hstring runtimeObjContext{};
@@ -96,14 +96,14 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
9696
// - primaryText: <displayText>
9797
// - secondaryText: <runtimeObjLabel>
9898
// navigates to setting container
99-
return winrt::make<FilteredSearchResult>(searchIndexEntry, runtimeObj, displayText, runtimeObjLabel);
99+
return winrt::make<FilteredSearchResult>(indexData, searchIndexEntry, runtimeObj, displayText, runtimeObjLabel);
100100
}
101101
// Partial index entry (for runtime object main pages)
102102
// - primaryText: <runtimeObjLabel>
103103
// - secondaryText: <runtimeObjContext>
104104
// "PowerShell" --> navigates to main runtime object page (i.e. Profiles_Base)
105105
// "SSH" | "Extension" --> navigates to main runtime object page (i.e. Extensions > SSH)
106-
return winrt::make<FilteredSearchResult>(searchIndexEntry, runtimeObj, runtimeObjLabel, runtimeObjContext);
106+
return winrt::make<FilteredSearchResult>(indexData, searchIndexEntry, runtimeObj, runtimeObjLabel, runtimeObjContext);
107107
}
108108

109109
winrt::hstring FilteredSearchResult::AccessibleName() const
@@ -277,7 +277,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
277277
indexData.colorSchemeIndexEntry.Entry = &PartialColorSchemeIndexEntry();
278278
indexData.actionIndexEntry.Entry = &PartialActionIndexEntry();
279279

280-
_index = std::move(indexData);
280+
_index.store(std::make_shared<const IndexData>(std::move(indexData)));
281281
}
282282

283283
// Method Description:
@@ -301,6 +301,16 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
301301
const IVectorView<Editor::ExtensionPackageViewModel> extensionPkgVMs,
302302
const IVectorView<Editor::CommandViewModel> commandVMs)
303303
{
304+
// Snapshot the current index so that Reset() can safely swap
305+
// it without invalidating pointers used by this search.
306+
const auto index = _index.load();
307+
wil::hide_name _index;
308+
if (!index)
309+
{
310+
// invalid search; do not return any results
311+
co_return single_threaded_observable_vector<Windows::Foundation::IInspectable>();
312+
}
313+
304314
co_await winrt::resume_background();
305315

306316
// The search can be cancelled at any time by the caller.
@@ -309,7 +319,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
309319
const auto filter = fzf::matcher::ParsePattern(std::wstring_view{ query });
310320

311321
std::vector<std::pair<int, Editor::FilteredSearchResult>> scoredResults;
312-
for (const auto& entry : _index.mainIndex)
322+
for (const auto& entry : index->mainIndex)
313323
{
314324
if (cancellationToken())
315325
{
@@ -331,7 +341,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
331341

332342
if (bestScore >= MinimumMatchScore)
333343
{
334-
scoredResults.emplace_back(bestScore, winrt::make<FilteredSearchResult>(&entry));
344+
scoredResults.emplace_back(bestScore, winrt::make<FilteredSearchResult>(index, &entry));
335345
}
336346
}
337347

@@ -358,7 +368,7 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
358368
{
359369
// navigates to runtime object main page (i.e. "PowerShell" Profiles_Base page)
360370
scoredResults.emplace_back(objNameMatch->Score * WeightRuntimeObjectMatch,
361-
FilteredSearchResult::CreateRuntimeObjectItem(&partialSearchIndexEntry, runtimeObj));
371+
FilteredSearchResult::CreateRuntimeObjectItem(index, &partialSearchIndexEntry, runtimeObj));
362372
}
363373

364374
for (const auto& entry : searchIndex)
@@ -409,15 +419,15 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
409419
{
410420
// navigates to runtime object's setting (i.e. "PowerShell: Command line")
411421
scoredResults.emplace_back(bestScore * WeightRuntimeObjectSetting,
412-
FilteredSearchResult::CreateRuntimeObjectItem(&entry, runtimeObj));
422+
FilteredSearchResult::CreateRuntimeObjectItem(index, &entry, runtimeObj));
413423
}
414424
}
415425
}
416426
};
417427

418-
appendRuntimeObjectResults(profileVMs, _index.profileIndex, _index.profileIndexEntry);
419-
appendRuntimeObjectResults(ntmFolderVMs, _index.ntmFolderIndex, _index.ntmFolderIndexEntry);
420-
appendRuntimeObjectResults(colorSchemeVMs, _index.colorSchemeIndex, _index.colorSchemeIndexEntry);
428+
appendRuntimeObjectResults(profileVMs, index->profileIndex, index->profileIndexEntry);
429+
appendRuntimeObjectResults(ntmFolderVMs, index->ntmFolderIndex, index->ntmFolderIndexEntry);
430+
appendRuntimeObjectResults(colorSchemeVMs, index->colorSchemeIndex, index->colorSchemeIndexEntry);
421431

422432
// Simple runtime object matching (no associated search index, just match by display name)
423433
auto appendSimpleRuntimeObjectResults = [&](const auto& runtimeObjectList, const LocalizedIndexEntry& indexEntry, auto getDisplayName) {
@@ -432,13 +442,13 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
432442
{
433443
// navigates to runtime object page (i.e. "Copy Text" --> Actions > EditAction page)
434444
scoredResults.emplace_back(match->Score * WeightRuntimeObjectMatch,
435-
FilteredSearchResult::CreateRuntimeObjectItem(&indexEntry, runtimeObj));
445+
FilteredSearchResult::CreateRuntimeObjectItem(index, &indexEntry, runtimeObj));
436446
}
437447
}
438448
};
439449

440-
appendSimpleRuntimeObjectResults(extensionPkgVMs, _index.extensionIndexEntry, [](const auto& ext) { return ext.DisplayName(); });
441-
appendSimpleRuntimeObjectResults(commandVMs, _index.actionIndexEntry, [](const auto& cmd) { return cmd.DisplayName(); });
450+
appendSimpleRuntimeObjectResults(extensionPkgVMs, index->extensionIndexEntry, [](const auto& ext) { return ext.DisplayName(); });
451+
appendSimpleRuntimeObjectResults(commandVMs, index->actionIndexEntry, [](const auto& cmd) { return cmd.DisplayName(); });
442452

443453
// must be IInspectable to be used as ItemsSource in XAML
444454
std::vector<Windows::Foundation::IInspectable> results;

src/cascadia/TerminalSettingsEditor/SearchIndex.h

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,38 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
2424
std::array<std::pair<std::optional<winrt::hstring>, int>, 2> GetSearchableFields() const;
2525
};
2626

27+
// Data backing the search index. Shared via shared_ptr so that
28+
// in-flight search results keep their snapshot alive even after
29+
// the index is rebuilt by Reset().
30+
struct IndexData
31+
{
32+
// Basic index data loaded from GeneratedSettingsIndex.g.h and wrapped as LocalizedIndexEntry objects.
33+
// Non-main indices are duplicated at runtime when searching for runtime objects.
34+
std::vector<LocalizedIndexEntry> mainIndex;
35+
std::vector<LocalizedIndexEntry> profileIndex;
36+
std::vector<LocalizedIndexEntry> ntmFolderIndex;
37+
std::vector<LocalizedIndexEntry> colorSchemeIndex;
38+
39+
// Links to main page; used when searching runtime objects
40+
LocalizedIndexEntry profileIndexEntry;
41+
LocalizedIndexEntry ntmFolderIndexEntry;
42+
LocalizedIndexEntry colorSchemeIndexEntry;
43+
LocalizedIndexEntry extensionIndexEntry;
44+
LocalizedIndexEntry actionIndexEntry;
45+
};
46+
2747
// WinRT object representing a single filtered search result
2848
struct FilteredSearchResult : FilteredSearchResultT<FilteredSearchResult>
2949
{
30-
FilteredSearchResult(const LocalizedIndexEntry* entry, const Windows::Foundation::IInspectable& navigationArgOverride = nullptr, const std::optional<hstring>& label = std::nullopt, const hstring secondaryLabel = {}) :
50+
FilteredSearchResult(std::shared_ptr<const IndexData> indexData, const LocalizedIndexEntry* entry, const Windows::Foundation::IInspectable& navigationArgOverride = nullptr, const std::optional<hstring>& label = std::nullopt, const hstring secondaryLabel = {}) :
51+
_indexData{ std::move(indexData) },
3152
_SearchIndexEntry{ entry },
3253
_NavigationArgOverride{ navigationArgOverride },
3354
_overrideLabel{ label },
3455
_secondaryLabel{ secondaryLabel } {}
3556

3657
static Editor::FilteredSearchResult CreateNoResultsItem(const winrt::hstring& query);
37-
static Editor::FilteredSearchResult CreateRuntimeObjectItem(const LocalizedIndexEntry* searchIndexEntry, const Windows::Foundation::IInspectable& runtimeObj);
58+
static Editor::FilteredSearchResult CreateRuntimeObjectItem(std::shared_ptr<const IndexData> indexData, const LocalizedIndexEntry* searchIndexEntry, const Windows::Foundation::IInspectable& runtimeObj);
3859

3960
hstring ToString() { return AccessibleName(); }
4061
winrt::hstring AccessibleName() const;
@@ -46,6 +67,9 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
4667
Windows::UI::Xaml::Controls::IconElement Icon() const;
4768

4869
private:
70+
// Prevent the IndexData (and the LocalizedIndexEntry objects within)
71+
// from being freed while this search result is still alive.
72+
const std::shared_ptr<const IndexData> _indexData;
4973
const std::optional<winrt::hstring> _overrideLabel{ std::nullopt };
5074
const winrt::hstring _secondaryLabel{};
5175
const Windows::Foundation::IInspectable _NavigationArgOverride{ nullptr };
@@ -79,23 +103,6 @@ namespace winrt::Microsoft::Terminal::Settings::Editor::implementation
79103

80104
private:
81105
SearchIndex() = default;
82-
struct IndexData
83-
{
84-
IndexData& operator=(const IndexData& other) = default;
85-
86-
// Basic index data loaded from GeneratedSettingsIndex.g.h and wrapped as LocalizedIndexEntry objects.
87-
// Non-main indices are duplicated at runtime when searching for runtime objects.
88-
std::vector<LocalizedIndexEntry> mainIndex;
89-
std::vector<LocalizedIndexEntry> profileIndex;
90-
std::vector<LocalizedIndexEntry> ntmFolderIndex;
91-
std::vector<LocalizedIndexEntry> colorSchemeIndex;
92-
93-
// Links to main page; used when searching runtime objects
94-
LocalizedIndexEntry profileIndexEntry;
95-
LocalizedIndexEntry ntmFolderIndexEntry;
96-
LocalizedIndexEntry colorSchemeIndexEntry;
97-
LocalizedIndexEntry extensionIndexEntry;
98-
LocalizedIndexEntry actionIndexEntry;
99-
} _index;
106+
std::atomic<std::shared_ptr<const IndexData>> _index;
100107
};
101108
}

0 commit comments

Comments
 (0)