Grepマルチスレッド対応・除外ファイル正規表現フィルタリング#2459
Conversation
Test Results1 297 tests 1 296 ✅ 6m 33s ⏱️ Results for commit d05711d. ♻️ This comment has been updated with latest results. |
efaa191 to
d4727b4
Compare
|
マジメに設計しようとするとまず 「 sakura/sakura_core/agent/CGrepAgent.h Lines 64 to 86 in 71514c3 妥当なシグニチャは、たぶんこんな感じ。 // Grep実行
DWORD DoGrep(
CEditView* pcViewDst,
CDlgGrep& cDlgGrep,
bool bGrepHeader = true,
bool bGrepStdout = false,
bool bGrepCurFolder = false
);「メソッド引数を何個まで許容するか?」の指標によく使われるのは「7±2」の規則。 ※サクラエディターのソースコードにはパラメーターオブジェクトの適用失敗例も含まれています。 「Grep実行のオプション」を格納する構造体が5つ以上あるのも変な感じ。
対応するとコード行数がガッツリ減ってカバレッジが下がるので長いこと放置中。 Grepに関して、思っていること。
CSearchAgentのロジックを流用できないことは分かっていますが、「CSearchAgent の実装がおかしい、からGrepに流用できない」という見方もできると思うわけで。 |
|
確かに構造体が複数あるのはイビツですね。 ただ本PRではDoGrepの既存シグネチャをそのまま使っていますが、 シグネチャ整理が先でも後でも、マルチスレッド部分の手戻りは 本PRでは既存のインターフェースを変更せずにマルチスレッド化を導入する方針としていますが、 高速化の要望も過去にありますし、本PRの実装内容(スレッドプールの同期機構、排他制御、 |
|
ご確認ありがとうございます。 以前の対応の切り出しと追加PRで補えていると考えています。
ご指摘の3点について、こちらの認識です。 カバレッジ セキュリティ(wcscpy / wcslen) 保守性 引き続き、実装・テストの参照用として活用いただければ幸いです。 |
3dac619 to
d4727b4
Compare
grep 関連の機能リファクタリングとテスト強化#2460 カバレッジ対応としてマージ。 概要
変更規模: 11 ファイル / テストコード約 2,500 行・138 テスト追加 関連主な変更内容
リファクタリングの目的とテスト実行方法プロダクトコードから HWND 非依存の純粋ロジックを CGrepAgent 切り出し関数
CDlgGrep 切り出し関数
DoGrepFileWorker(既存 + 異常系)全件継続課題test-grep-irregular.cpp ソースIRR-16: 長大コマンドライン引数テスト(コメントアウト中)
変更ファイル一覧変更ファイル一覧(11 ファイル)
テスト一覧(ソース出現順)📂
|
| # | スイート | テスト名 |
|---|---|---|
| 1 | GrepSearchEngine |
LiteralSearchCaseSensitivity |
| 2 | GrepRealFileTest |
JapaneseLiteralSearchAcrossMixedEncodings |
| 3 | GrepRealFileTest |
RegexCompileAndMatch |
| 4 | CGrepEnumKeys |
ParseRegexExcludePattern |
| 5 | CGrepEnumKeys |
ParseFileAndFolderKeysWithDefaults |
| 6 | CGrepEnumKeys |
AbsolutePathInSearchKeyIsRejected |
| 7 | CCodeMediator |
AutoDetectBomAndDefaultCode |
| 8 | GrepRealFileTest |
EnumerateFiles_FiltersByExtensionAndExcludeKey |
| 9 | GrepRealFileTest |
EnumerateFolders_ExcludesByHashKey |
| 10 | GrepRealFileTest |
EnumerateFiles_SubfolderRecursion |
| 11 | GrepRealFileTest |
FileWorker_BasicLiteralHitsInUtf8File |
| 12 | GrepRealFileTest |
FileWorker_CaseSensitivityIsRespected |
| 13 | GrepRealFileTest |
FileWorker_RegexHitsInUtf8File |
| 14 | GrepRealFileTest |
FileWorker_JapaneseAcrossEncodings |
| 15 | GrepRealFileTest |
FileWorker_32000LinesIsSearchable |
| 16 | GrepRealFileTest |
FileWorker_CancelTerminatesScan |
| 17 | GrepRealFileTest |
MultiThread_HitCountMatchesSingleThread |
| 18 | GrepRealFileTest |
MultiThread_StressNoDeadlockAcrossRepeats |
| 19 | GrepRealFileTest |
RegexExclude_AppliedToEnumeratedFiles |
| 20 | GrepRealFileTest |
RegexExclude_InvalidPatternFailsToCompile |
Phase 2-B 追加(21 テスト)
| # | テスト名 | 検証観点 |
|---|---|---|
| 21 | SplitPattern_SemicolonSeparator |
; 区切り |
| 22 | SplitPattern_SpaceSeparator |
スペース区切り |
| 23 | SplitPattern_CommaSeparator |
, 区切り |
| 24 | SplitPattern_MixedSeparators |
3 種混在 |
| 25 | SplitPattern_ConsecutiveSeparators |
連続区切り |
| 26 | SplitPattern_TrailingSeparator |
末尾区切り |
| 27 | SplitPattern_QuotesRemoved |
クォート除去 |
| 28 | SplitPattern_QuotesInMiddle |
途中クォート |
| 29 | SetFileKeys_EmptyInputFallsBackToWildcard |
空入力 → *.* |
| 30 | SetFileKeys_WildcardInPathReturns1 |
パス中ワイルドカード |
| 31 | SetFileKeys_AbsolutePathInSearchReturns2 |
絶対パス |
| 32 | SetFileKeys_ExcludeFolderHashRelative |
# 相対除外 |
| 33 | SetFileKeys_ExcludeFolderHashAbsolute |
# 絶対除外 |
| 34 | SetFileKeys_RegexExcludeNoValidation |
! バリデーションスキップ |
| 35 | SetFileKeys_DuplicateKeyDeduplicated |
重複排除 |
| 36 | AddExceptFile_RelativePath |
相対除外ファイル |
| 37 | AddExceptFile_AbsolutePath |
絶対除外ファイル |
| 38 | AddExceptFolder_DefaultPattern |
既定除外フォルダー |
| 39 | AddExceptFile_HashIsNotSpecial |
# 非特殊 |
| 40 | GetExcludeFiles_MergesAllThreeArrays |
3 配列集約 |
| 41 | GetExcludeFolders_MergesRelAndAbs |
相対+絶対集約 |
📂 test-cgrepagent-flow.cpp(20 テスト)
| ID | テスト名 | 検証観点 |
|---|---|---|
GA-01 |
FormatGrepResultLine_NormalStyle1_ProducesPathLineColContent |
Normal + 該当行 |
GA-02 |
FormatGrepResultLine_WzStyle2_FileGrouped |
WZ 風 |
GA-03 |
FormatGrepResultLine_ResultOnlyStyle3_NoPath |
結果のみ |
GA-04 |
FormatGrepResultLine_OutputLineType0_OnlyMatchPart |
該当部分のみ |
GA-05 |
FormatGrepResultLine_OutputLineType1_FullLine |
行全体 |
GA-06 |
FormatGrepResultLine_OutputLineType2_NegativeLine |
否該当行 |
GA-07 |
BuildGrepHeader_BasicShape |
基本要素 |
GA-08 |
BuildGrepHeader_WithRegexOption_IncludesMarker |
正規表現マーカー |
GA-09 |
BuildGrepHeader_WithCaseSensitive_IncludesMarker |
大文字小文字マーカー |
GA-10 |
BuildGrepFooter_ZeroHits_HasZeroMessage |
0 件 |
GA-11 |
BuildGrepFooter_PositiveHits_HasCount |
42 件(通常/置換) |
GA-12 |
CreateFolders_SemicolonSeparated_SplitsCorrectly |
; 分割 |
GA-13 |
CreateFolders_QuotedSemicolon_PreservesAsOne |
クォート内 ; |
GA-14 |
CreateFolders_LongFileName_Resolved |
8.3 → ロングパス |
GA-15 |
ChopYen_LastBackslashRemoved |
末尾 \ 除去 |
GA-16 |
ChopYen_NoTrailingYen_Unchanged |
無変更 |
GA-17 |
ChopYen_RootPath_AlsoTrimmed |
C:\ → C: |
GA-18 |
AddTail_StdoutMode_WritesToStdout |
stdout 書き込み |
GA-19 |
OnBeforeClose_DuringGrepReturnsInterrupt |
Grep 中割り込み |
GA-20 |
OnBeforeClose_NotRunningReturnsContinue |
停止中継続 |
📂 test-cdlggrep.cpp(26 テスト / DG-19 欠番)
| ID | テスト名 | 検証観点 |
|---|---|---|
DG-01 |
DefaultMemberValues_Constructor |
初期値 |
DG-02 |
DefaultExcludePatterns_Constants |
定数値 |
DG-03 |
GetPackedGFileString_NoExclusions |
除外なし |
DG-04 |
GetPackedGFileString_WithExcludeFolders |
# 付加 |
DG-05 |
GetPackedGFileString_WithExcludeFiles |
! 付加・*.* 補完 |
DG-06 |
GetPackedGFileString_EscapeBangInExcludeFolder |
! エスケープ |
DG-07 |
GetPackedGFileString_EscapeHashInExcludeFolder |
# エスケープ |
DG-08 |
GetPackedGFileString_EscapeSpaceInExcludeFolder |
スペース分割 |
DG-09 |
GetPackedGFileString_EscapeSemicolonInExcludeFolder |
セミコロン分割 |
DG-10 |
GetPackedGFileString_CombinedAllExclusions |
複合 |
DG-11 |
RoundTrip_GuiToCliToEnumKeys |
Pack→Parse 往復 |
DG-12 |
DetermineDefaultExcludePatterns_EmptyHistorySetsDefaults |
履歴空 |
DG-13 |
DetermineDefaultExcludePatterns_NonEmptyHistoryUsesHistoryTop |
履歴先頭 |
DG-14 |
DetermineDefaultExcludePatterns_AlreadySetSkipped |
設定済みスキップ |
DG-15 |
BuildHwndFileToken_Format |
HWND 文字列化 |
DG-16 |
BuildHwndFileToken_NullHwnd |
NULL ゼロ埋め |
DG-17 |
IsHwndFileToken_PositiveCases |
正常 → true |
DG-18 |
IsHwndFileToken_NegativeCases |
異常 → false |
DG-20 |
DoModalCancelImmediately_NoException |
IDCANCEL → rc=0 |
DG-21 |
DoModalOK_WithValidInputs_ReturnsOK |
有効入力 → rc=1 |
DG-22 |
DoModalOK_EmptyFolder_FallsBackToCurrentDir |
空フォルダー |
DG-23 |
DoModalOK_InvalidRegex_StillReturnsOK |
不正正規表現 |
DG-24 |
DoModalOK_FolderWithSemicolon_IsSplitBySeparator |
セミコロン区切り |
DG-25 |
DoModalOK_MultipleFolders_AllResolved |
複数絶対パス |
DG-26 |
DoModalOK_HistoryRecordedExceptFromThisText |
履歴追加 |
DG-27 |
DoModalOK_FromThisText_DoesNotPolluteHistory |
履歴非汚染 |
📂 test-grep-irregular.cpp(46 テスト + IRR-16 コメントアウト 1)
CGrepEnumKeys 境界(IRR-01〜IRR-08 + IRR-07b)
| ID | テスト名 |
|---|---|
IRR-01 |
SetFileKeys_EmptyString_DefaultsApplied |
IRR-02 |
SetFileKeys_OnlyWhitespace_DefaultsApplied |
IRR-03 |
SetFileKeys_OnlyExcludePatterns_DefaultSearchApplied |
IRR-04 |
SetFileKeys_VeryLongPattern_4096Chars |
IRR-05 |
SetFileKeys_ManyDuplicates_DeduplicatedTo1 |
IRR-06 |
SetFileKeys_NullByteInMiddle_TruncatesAtNull |
IRR-07 |
SetFileKeys_BangPrefixWithInvalidRegex_RegisteredButFailsLater |
IRR-07b |
Regex_InvalidExcludePatternFailsToCompile |
IRR-08 |
SetFileKeys_PathWithDriveLetterInMiddle_TreatedAsRelative |
CCommandLine 境界(IRR-09〜IRR-19)
| ID | テスト名 |
|---|---|
IRR-09 |
CommandLine_GKEY_EmptyValue_Ignored |
IRR-10 |
CommandLine_GREPR_EmptyValue_AcceptedAsEmpty |
IRR-11 |
CommandLine_GFOLDER_NonExistentPath_StoredAsIs |
IRR-12 |
CommandLine_GCODE_OutOfRange_StoredAsIs |
IRR-13 |
CommandLine_GCODE_Negative_StoredAsIs |
IRR-14 |
CommandLine_GKEY_WithControlChars_Stored |
IRR-15 |
CommandLine_GKEY_WithSurrogatePair |
IRR-16 |
CommandLine_TotalLength_NearWindowsLimit_32767Chars |
IRR-17 |
CommandLine_ResponseFileWithGrepArgs_Parsed |
IRR-18 |
CommandLine_DoubleDashStopsOptionParsing |
IRR-19 |
CommandLine_ColonSeparatorEquivalentToEqual |
除外正規表現コンパイル検証(IRR-20〜IRR-25)
| ID | テスト名 |
|---|---|
IRR-20 |
ExcludeFile_ValidRegex_CompilesSuccessfully |
IRR-21 |
ExcludeFile_GlobPattern_CompileFails |
IRR-22 |
ExcludeFile_EmptyPattern_NoCompileNoError |
IRR-23 |
ExcludeFile_MixedRegexAndGlob_FirstSucceedsSecondFails |
IRR-24 |
ExcludeFile_MixedGlobAndRegex_OrderReversed |
IRR-25 |
ExcludeFile_AllValidRegex_AllCompileSuccessfully |
DoGrepFileWorker 境界(IRR-26〜IRR-34)
| ID | テスト名 |
|---|---|
IRR-26 |
FileWorker_ZeroByteFile_NoHit |
IRR-27 |
FileWorker_OnlyBomFile_NoHit |
IRR-28 |
FileWorker_InvalidUtf8Sequence_DoesNotCrash |
IRR-29 |
FileWorker_FileWithoutFinalNewline_LastLineSearched |
IRR-30 |
FileWorker_MixedLineEndings_LineNumbersConsistent |
IRR-31 |
FileWorker_VeryLongSingleLine_64KChars |
IRR-32 |
FileWorker_LockedFile_ReturnsError |
IRR-33 |
FileWorker_ReadOnlyAttribute_Searched |
IRR-34 |
FileWorker_HiddenSystemFile_Searched |
CCodeMediator 境界(IRR-35〜IRR-38)
| ID | テスト名 |
|---|---|
IRR-35 |
CodeMediator_ShortFile_FallbackToDefault |
IRR-36 |
CodeMediator_SjisSecondByteInAsciiRange_NotMisdetected |
IRR-37 |
CodeMediator_PriorCesu8Flag_TogglesDetection |
IRR-38 |
CodeMediator_Utf32LeBom_NotMisdetectedAsUtf16 |
正規表現・並列処理(IRR-39〜IRR-46)
| ID | テスト名 |
|---|---|
IRR-39 |
Regex_EmptyPattern_CompileFailsOrEmptyMatch |
IRR-40 |
Regex_DotStar_DoesNotHang |
IRR-41 |
Regex_CatastrophicBacktracking_TimeoutGuarded |
IRR-42 |
MultiThread_CancelImmediatelyAfterStart |
IRR-43 |
MultiThread_FileDeletedMidScan |
IRR-44 |
MultiThread_ZeroExcludeRegexes_Equivalent |
IRR-45 |
MultiThread_100ExcludeRegexes_AllApplied |
IRR-46 |
MultiThread_RepeatedStartCancel_50Iterations |
📂 test-ccommandline.cpp Phase 2-A(25 テスト追加)
-GOPT 複合・境界値(9 テスト)
| # | テスト名 |
|---|---|
| 1 | ParseGrepOpt_MultipleFlagsCombined |
| 2 | ParseGrepOpt_AllKnownFlagsOn |
| 3 | ParseGrepOpt_DuplicateSameFlag |
| 4 | ParseGrepOpt_StyleLastWins |
| 5 | ParseGrepOpt_OutputLineTypeLastWins |
| 6 | ParseGrepOpt_UnknownCharIgnored |
| 7 | ParseGrepOpt_EmptyValueRejected |
| 8 | ParseGrepOpt_MixedDigitsAndLetters |
-GREPR / -GKEY 連動(4 テスト)
| # | テスト名 |
|---|---|
| 9 | ParseGrepReplace_GREPRSetsReplaceFlag |
| 10 | ParseGrepReplace_OnlyGKEYDoesNotSetReplaceFlag |
| 11 | ParseGrepReplace_GREPREmptyAllowed |
| 12 | ParseGrepReplace_ClipboardPasteFlag |
Case-insensitive・引数順序(5 テスト)
| # | テスト名 |
|---|---|
| 13 | ParseGrep_OptionNameLowerCase |
| 14 | ParseGrep_OptionNameMixedCase |
| 15 | ParseGrep_ArgumentOrderInvariant |
| 16 | ParseGrep_GrepModeAndGrepDlgBothSpecified |
| 17 | ParseGrep_GKeyDuplicate_LaterWins |
-GKEY / -GFILE / -GFOLDER 値境界(8 テスト)
| # | テスト名 |
|---|---|
| 18 | ParseGrepKey_SingleChar_Boundary |
| 19 | ParseGrepKey_VeryLongValue_4096Chars |
| 20 | ParseGrepKey_JapaneseCharsAndEmoji |
| 21 | ParseGrepKey_ContainsEqualsInValue |
| 22 | ParseGrepKey_EscapedDoubleQuotes |
| 23 | ParseGrepFile_RegexExcludePatternFromPR2449 |
| 24 | ParseGrepFolder_RelativePath |
| 25 | ParseGrepFolder_UncPath |
テスト数サマリー
| ファイル | テスト数 |
|---|---|
test-grep.cpp |
41 |
test-cgrepagent-flow.cpp |
20 |
test-cdlggrep.cpp |
26 |
test-grep-irregular.cpp |
46(+ IRR-16 コメントアウト 1) |
test-ccommandline.cpp Phase 2-A |
25 |
| 合計 | 158 定義 / 138 新規追加 |
ライセンス
Zlib License (SPDX-License-Identifier: Zlib)
Grep並列化: RunParallelGrep 切り出し・デッドロック対策・カバレッジ追加 + SonarQube セキュリティ Hotspot 対応概要
本コミット( 関連
主な変更内容
SonarQube セキュリティ Hotspot の修正内容(
|
| 関数 | 変更前 | 変更後 |
|---|---|---|
DoGrepTreeEnumerate |
int(wcslen(pszBasePath)) |
int(std::wstring_view(pszBasePath).length()) |
DoGrepTreeEnumerate |
(int)wcslen(pszPath) |
(int)std::wstring_view(pszPath).length() |
DoGrepFileWorker |
int(wcslen(pszKey)) |
int(std::wstring_view(pszKey).length()) |
DoGrepFileWorker |
task.fullPath.size() + wcslen(pszCodeName) + 10 |
task.fullPath.size() + std::wstring_view(pszCodeName).length() + 10 |
BuildGrepHeader |
(int)wcslen(pszKey) |
(int)std::wstring_view(pszKey).length() |
DoGrepFileWorker |
wcscpy( szCpName, CCodeTypeName(nCharCode).Bracket() ) |
wcscpy_s( szCpName, std::size(szCpName), CCodeTypeName(nCharCode).Bracket() ) |
加えて #include <string_view> を追加(#include <set> の直後)。
補足
wcslen置換は NUL 終端文字列の長さ取得で、std::wstring_viewのコンストラクタが内部で長さを計算するため戻り値・挙動は不変。外側のint(...)/(int)キャストは維持。wcscpy置換のコピー先szCpNameはWCHAR[100]、コピー元CCodeTypeName::Bracket()は短い固定文字列でオーバーランは発生しない。コピー先要素数を渡すwcscpy_sで Hotspot を解消。- 既存関数
DoGrepTree/DoGrepFile/DoGrepReplaceFileのwcslen/wcscpyは新規コードの対象行ではないため変更しない(CLAUDE.md の「動いている既存行は触らない」方針)。
変更ファイル一覧(3本)
| # | ファイル | 種別 | 変更規模 | 変更内容 |
|---|---|---|---|---|
| 1 | sakura_core/agent/CGrepAgent.cpp |
改修 | 958 行 | DoGrep() 内並列オーケストレーションを RunParallelGrep へ切り出し。ワーカー初期化失敗時のデッドロック対策。新規コードの wcslen×5 を std::wstring_view().length()、wcscpy×1 を wcscpy_s に置換、#include <string_view> 追加(SonarQube セキュリティ Hotspot 対応)。 |
| 2 | sakura_core/agent/CGrepAgent.h |
改修 | 93 行 | RunParallelGrep(スレッドプール管理・バッチ駆動コア)の宣言追加。pcViewDst/pcDlgCancel は nullptr 可。 |
| 3 | src/test/cpp/tests1/test-grep.cpp |
追加 | 1435 行 | RunParallelGrep 本体の主要分岐・デッドロック非再現を直接検証するテストを追加。 |
検証
- ローカルビルドは行わない。差分はこの3ファイルに限定され、既存の直列パス(
DoGrepFile/DoGrepReplaceFile)には波及しない。 - 追加テストで
RunParallelGrepの主要分岐が covered になること、初期化失敗時にハングしないことを確認する。 - SonarQube セキュリティ Hotspot(
wcslen/wcscpy)が Resolved になること、新規コードカバレッジ・Quality Gate を CI / SonarCloud の再スキャンで確認する。
ライセンス
Zlib License (SPDX-License-Identifier: Zlib)
b0afc7e to
d6a43bc
Compare
d6a43bc to
5461b02
Compare
SonarQube Security Hotspot 対応: 新規コードの wcscpy / wcslen を境界付き操作へ置換概要PR #2459 の SonarCloud 解析で報告された Security Hotspot(Buffer Overflow カテゴリ 6 件: 変更規模: 2 ファイル / +8 -7(プロダクトコードのみ・機能変更なし) 関連#2459 主な変更内容
既存コードの移動分( 変更ファイル一覧
テスト機能変更がないため、既存の Grep 回帰テストでカバーする。
マージ後、SonarCloud の PR 解析で Security Hotspots(New Code)が 0 件になることを確認する。 ライセンスZlib License (SPDX-License-Identifier: Zlib) |
Grep新規コードのテストカバレッジ追加#2459 のマルチスレッドGrep・除外ファイル正規表現フィルタリングに対し、SonarCloud の New Code カバレッジを満たすためのテストを追加する。 1. 概要#2459 で追加した 本PRは テストコードのみ を追加し、プロダクションコード( 2. 方針
3. 追加テスト
4. 変更ファイル一覧
5. テスト結果(ローカル)
6. 制約・注意事項
|
299f764 to
c8989f7
Compare
|
対応ありがとうございます。 ざっくり、マージ前提で評価する条件は満たしてもらえたと思ってます。 仕様に関しての話をするかどうかについて若干悩み中です。 Grepマルチスレッド化の提案内容 もしかしたら伝わるかも知れませんが、この話をするかどうかに迷いがあります。 Grepがやること 保守性に関する指摘のうち、対応できるものがありそうです。 たとえばこういうの。(変数に値を入れているが読まれていない。) 保守性に関する指摘をパスして構わないと言ってたのは「かなり頑張らないと対応できない」系が多いからです。 たとえばこういうの。(ネストは3レベルまでにしましょう。) たとえばこういうの。(関数の認知複雑度は25までにおさえましょう。) |
|
対応が固まってきたんでコメントをそろそろ書き込もうとしてたところで、先にご確認ありがとうございます。 マルチスレッド仕様に関してですが、修正に手間を掛ける割にメリットがあまりない気がします。 ファイル内検索(マルチスレッド化可能)を行うのはファイルサイズが大きい場合に >2.1.10 出力順序 保守性保守性に関する指摘ですが、既存だったので対応しようとして止めたんですが 変更内容(計14件)
|
d36fbc8 to
e7c984d
Compare
|
追加対応概要挙動を変えない範囲で、並列 Grep(CGrepAgent)まわりのコードの改善
変更内容1. Grep 系シグネチャの構造体化(S107 対策)
上記に合わせてシグネチャを更新し、全呼び出し側を修正
呼び出し側: 2. SonarCloud 指摘対応(機械的・挙動を変えないもの)
3. テスト項目の追加
|
…ev/sakura into feature/grepmatulithreads








Grep機能拡張 仕様書
#2449 から一部修正、関係ないソースは除外し再度PR
1. 概要
サクラエディタのGrep機能に対し、以下2つの拡張を実装する。
!プレフィックスで正規表現によるファイル除外を可能にする2. 機能仕様
2.1 マルチスレッドGrep
2.1.1 対象
通常Grep(
bGrepReplace=false)のみ。Grep置換は副作用(ファイル上書き)を伴うため従来の直列処理を維持する。2.1.2 処理フロー
サブフォルダー単位でバッチを分割することで、1バッチ分のみメモリに保持し、サブフォルダー順(A→B→C)の出力順序を保証する。
2.1.3 スレッド数
nGrepThreadCount(1〜8にクランプ)と論理コア数 / 4の大きい方を採用hardware_concurrency() / 4に上限なし2.1.4 ini設定
[Common]nGrepThreadCount2.1.5 スレッドプール同期機構
poolBatchIdsize_tbPoolShutdownbooltrueでワーカーに終了を指示pPoolBatchconst vector<SGrepFileTask>*poolNextTaskatomic<size_t>poolBatchActiveatomic<int>nActiveWorkersatomic<unsigned int>bWorkCancelledatomic<bool>cvPoolStartcondition_variable2.1.6 排他制御
atomic<size_t> poolNextTask(ロックフリー)atomic<int> atomicHitCountsharedMessage)std::mutex resultMutex+std::lock_guardstd::set<std::wstring>をresultMutexで保護2.1.7 CBregexpスレッド安全性
CBregexpは内部状態を持つためスレッドセーフではない。各ワーカースレッドはスレッド生存期間中に1回だけ独自インスタンスを生成・コンパイルし、スレッド間で共有しない。2.1.8 キャンセル処理
BlockingHook()/IsCanceled()で検出しbWorkCancelled.store(true, release)bWorkCancelled.load(relaxed)を確認RunBatchはboolを返し、呼び出し元でnGrepTreeResult = -1を設定(一元管理)2.1.9 例外安全性
ワーカーラムダは
try/catch(...)で囲み、poolBatchActive.fetch_sub(1)をcatchブロックの外に配置。正常終了・例外発生どちらの経路でも必ずデクリメントされ、デッドロックを防止する。ワーカー初期化(
SetPattern)が例外で失敗した場合はnActiveWorkersをデクリメントし、RunBatchではnActiveWorkersの値をpoolBatchActiveの初期値に使用する。これにより、初期化失敗ワーカーが存在してもpoolBatchActiveが正確にゼロに到達し、メインスレッドのデッドロックを防止する。2.1.10 出力順序
2.2 除外ファイル指定の正規表現対応
2.2.1 構文
Grepダイアログのファイルパターン欄で
!プレフィックスを付けると、以降の文字列が正規表現パターンとして解釈される。*.cpp##.git!!.*\.bak$2.2.2 マッチング対象
ファイルのフルパス全体に対して正規表現マッチングを行う。
2.2.3 大文字小文字
大文字小文字を区別する(
CBregexp::optCaseSensitive)。大文字小文字を無視したい場合は、正規表現内で(?i)を使用する。2.2.4 入力バリデーション
Grep実行前にメインスレッドで全パターンの
InitRegexp()+Compile()を事前検証する。無効なパターンはエラーダイアログ("無効な除外正規表現パターン: %s")を表示してGrep実行を中止する。2.2.5 適用範囲
通常Grep・Grep置換の両方で有効。
DoGrepTree()ファイルループ内2.2.6 ヘッダー表示
Grep結果ウィンドウの「除外ファイル」行に正規表現パターンが表示される。
2.2.7 デフォルト除外パターン
CDlgGrep.hのDEFAULT_EXCLUDE_FILE_PATTERNを正規表現形式に変更。3. アーキテクチャ
3.1 クラス・関数構成
3.2 新規追加した構造体・関数
SGrepFileTaskDoGrepTreeEnumerate()vecTasksにファイル情報を収集(メインスレッド用)DoGrepFileWorker()3.3 DoGrepTree の変更
nNestパラメータを削除(並列処理導入に伴いネスト管理が不要となったため)std::vector<CBregexp>* pExclRegexps = nullptr引数を追加。最上位呼び出しのみ関数内でコンパイルし、再帰呼び出しにはコンパイル済みポインタを引き継ぐ4. 変更ファイル一覧
sakura_core/agent/DoGrepTree正規表現除外・nNest削除、DoGrepTreeEnumerate/DoGrepFileWorker実装、デッドロック防止(nActiveWorkers)sakura_core/agent/SGrepFileTask構造体、DoGrepTreeEnumerate/DoGrepFileWorker宣言、DoGrepTreeシグネチャ変更sakura_core/dlg/DEFAULT_EXCLUDE_FILE_PATTERNを正規表現形式に変更sakura_core/grep/m_vecExceptFileRegexPatterns追加、!プレフィックスパーサー変更、GetExcludeFiles()戻り値型変更、ClearItems()クリア漏れ修正sakura_core/env/m_nGrepThreadCountメンバー追加sakura_core/env/m_nGrepThreadCountデフォルト値2sakura_core/env/nGrepThreadCountini読み書き([Common]セクション)5. 制約・注意事項
#プレフィックスは従来のワイルドカード方式を維持InitRegexp(nullptr, ..., false)でUI表示なしにDLLをロードstd::filesystemFindFirstFile/FindNextFileベース)!プレフィックスの互換性CDocTypeManager/GetDllShareData()のワーカースレッドからの読み取りアクセスが安全であることの継続的な検証が必要関連
#2448
#2449