From 92945de4036eebc2a47ceb9cb876b6844d0de11e Mon Sep 17 00:00:00 2001 From: Basile Burg Date: Tue, 5 May 2020 14:15:06 +0200 Subject: [PATCH] todolist, symlist, fix possible corruption of the input data when a new thread was started before the previous one ended close #35 --- CHANGELOG.md | 6 +++++ src/u_symlist.pas | 22 ++++++++++------- src/u_todolist.pas | 61 ++++++++++++++++++++++++++-------------------- 3 files changed, 53 insertions(+), 36 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 44432cf0..7edabb11 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # v3.9.2-dev +## Regressions + +- Symbol list: performance regression, async behavior is now emulated with threads. (#35) +- Todolist: performance regression, async behavior is now emulated with threads. (#35) +- Todolist: small non growing leak introduced in v3.9.0 + ## Bugs fixed - Editor, Diff: the button used to "reload from disk and reset the history" didn't work. diff --git a/src/u_symlist.pas b/src/u_symlist.pas index 2ec4a28a..f2619984 100644 --- a/src/u_symlist.pas +++ b/src/u_symlist.pas @@ -138,13 +138,14 @@ type fAutoExpandErrors: boolean; fSortSymbols: boolean; fSmartExpander: boolean; - fTreeDataToThread: string; + fSourcecodeForThread: string; fTreeDataFromThread: string; + fLockThreadedParsing: boolean; ndAlias, ndClass, ndEnum, ndFunc, ndUni: TTreeNode; ndImp, ndIntf, ndMix, ndStruct, ndTmp: TTreeNode; ndVar, ndWarn, ndErr, ndUt: TTreeNode; - procedure getTreeDataInThread; - procedure gotTreeDataFromThread(sender: TObject); + procedure threadedParsing; + procedure threadedParsingFinished(sender: TObject); procedure TreeDblClick(Sender: TObject); procedure actRefreshExecute(Sender: TObject); procedure actAutoRefreshExecute(Sender: TObject); @@ -733,6 +734,8 @@ end; procedure TSymbolListWidget.getSymbols; begin + if fLockThreadedParsing then + exit; if fDoc.isNil then exit; if (fDoc.Lines.Count = 0) or not fDoc.isDSource then @@ -741,16 +744,17 @@ begin updateVisibleCat; exit; end; - fTreeDataToThread := fDoc.Lines.Text; - TTHread.ExecuteInThread(@getTreeDataInThread, @gotTreeDataFromThread); + fSourcecodeForThread := fDoc.Lines.Text; + fLockThreadedParsing := true; + TTHread.ExecuteInThread(@threadedParsing, @threadedParsingFinished); end; -procedure TSymbolListWidget.getTreeDataInThread; +procedure TSymbolListWidget.threadedParsing; begin - fTreeDataFromThread := listSymbols(PChar(fTreeDataToThread), fDeep); + fTreeDataFromThread := listSymbols(PChar(fSourcecodeForThread), fDeep); end; -procedure TSymbolListWidget.gotTreeDataFromThread(sender: TObject); +procedure TSymbolListWidget.threadedParsingFinished(sender: TObject); function getCatNode(node: TTreeNode; stype: TSymbolType ): TTreeNode; function newCat(const aCat: string): TTreeNode; @@ -832,11 +836,11 @@ procedure TSymbolListWidget.gotTreeDataFromThread(sender: TObject); end; var - s: string; i: Integer; f: string; n: TTreeNode; begin + fLockThreadedParsing := false; if fDoc.isNil then exit; diff --git a/src/u_todolist.pas b/src/u_todolist.pas index 7a7c8651..ea356dbb 100644 --- a/src/u_todolist.pas +++ b/src/u_todolist.pas @@ -7,6 +7,7 @@ interface uses Classes, SysUtils, FileUtil, ListFilterEdit, Forms, Controls, strutils, Graphics, Dialogs, ExtCtrls, Menus, Buttons, ComCtrls, + syncobjs, u_widget, process, u_common, u_interfaces, u_synmemo, u_processes, u_writableComponent, u_observer, u_sharedres, u_dexed_d, u_dsgncontrols; @@ -88,6 +89,9 @@ type procedure mnuAutoRefreshClick(Sender: TObject); procedure toolbarResize(Sender: TObject); private + fFileListForThread: string; + fSerializedTodoItemFromThread: string; + fLockItemsScanning: boolean; fAutoRefresh: Boolean; fSingleClick: Boolean; fColumns: TTodoColumns; @@ -95,8 +99,6 @@ type fDoc: TDexedMemo; fTodos: TTodoItems; fOptions: TTodoOptions; - fTodoItemsDataFromThread: string; - fTodoItemsresultFromThread: string; // IDocumentObserver procedure docNew(document: TDexedMemo); procedure docFocused(document: TDexedMemo); @@ -118,8 +120,8 @@ type // TODOlist things function getContext: TTodoContext; procedure scanTodoItems(autoRefreshed: boolean); - procedure scanTodoInThread; - procedure scannedTodoInThread(Sender : TObject); + procedure threadedScanning; + procedure threadedScanningFinished(Sender : TObject); procedure clearTodoList; procedure fillTodoList; procedure lstItemsColumnClick(Sender: TObject; Column: TListColumn); @@ -418,16 +420,19 @@ end; procedure TTodoListWidget.scanTodoItems(autoRefreshed: boolean); var - ctxt: TTodoContext; - i,j: integer; - nme: string; - str: string = ''; - txt: TMemoryStream; + c: TTodoContext; + i: integer; + j: integer; + n: string; begin + if fLockItemsScanning then + exit; + + fFileListForThread := ''; clearTodoList; - ctxt := getContext; - case ctxt of + c := getContext; + case c of tcNone: exit; tcProject: if (fProj = nil) or (fProj.sourcesCount = 0) then exit; @@ -435,50 +440,51 @@ begin exit; end; - if ctxt = tcProject then + if c = tcProject then begin - i := 0; j := fProj.sourcesCount-1; if autoRefreshed and (j > fOptions.disableIfMoreFilesThan) then exit; for i := 0 to j do begin - nme := fProj.sourceAbsolute(i); - if not hasDlangSyntax(nme.extractFileExt) then + n := fProj.sourceAbsolute(i); + if not hasDlangSyntax(n.extractFileExt) then continue; - str += nme; + if not n.fileExists then + continue; + fFileListForThread += n; if i <> j then - str += PathSeparator; + fFileListForThread += PathSeparator; end; end else if fDoc.fileName <> newdocPageCaption then begin - str := fDoc.fileName; + fFileListForThread := fDoc.fileName; end; - if str.isNotEmpty then + if fFileListForThread.isNotEmpty then begin - fTodoItemsDataFromThread := str; - TThread.ExecuteInThread(@scanTodoInThread, @scannedTodoInThread); + fLockItemsScanning := true; + TThread.ExecuteInThread(@threadedScanning, @threadedScanningFinished); end; end; -procedure TTodoListWidget.scanTodoInThread; +procedure TTodoListWidget.threadedScanning; begin - fTodoItemsResultFromThread := todoItems(PChar(fTodoItemsDataFromThread)); + fSerializedTodoItemFromThread := todoItems(PChar(fFileListForThread)); end; -procedure TTodoListWidget.scannedTodoInThread(Sender : TObject); +procedure TTodoListWidget.threadedScanningFinished(Sender : TObject); var txt: TmemoryStream; begin - if fTodoItemsResultFromThread.length < 10 then + if fSerializedTodoItemFromThread.length < 10 then exit; txt := TMemoryStream.create; try - txt.Write(fTodoItemsResultFromThread[1], fTodoItemsResultFromThread.length); - txt.Position:=0; + txt.Write(fSerializedTodoItemFromThread[1], fSerializedTodoItemFromThread.length); + txt.Position := 0; fTodos.loadFromTxtStream(txt); fillTodoList; finally @@ -542,6 +548,7 @@ begin lstItems.Column[4].Visible := True; end; lstItems.EndUpdate; + fLockItemsScanning := false; end; procedure TTodoListWidget.handleListClick(Sender: TObject);