Skip to content

Commit

Permalink
fixed search cancel race condition
Browse files Browse the repository at this point in the history
  • Loading branch information
ArthurHeitmann committed Oct 25, 2022
1 parent c33024c commit 9d26d28
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 24 deletions.
30 changes: 25 additions & 5 deletions lib/background/searchService.dart
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class SearchService {
final StreamController<SearchResult> controller = StreamController<SearchResult>();
Isolate? _isolate;
SendPort? _sendPort;
final _isDoneCompleter = Completer<void>();
final BoolProp isSearching;

SearchService({ required this.isSearching });
Expand Down Expand Up @@ -96,30 +97,40 @@ class SearchService {
else if (message is SearchResult)
controller.add(message);
else if (message is String && message == "done") {
if (!_isDoneCompleter.isCompleted) {
isSearching.value = false;
_isDoneCompleter.complete();
}
if (controller.isClosed)
return;
controller.close();
_isolate?.kill(priority: Isolate.beforeNextEvent);
isSearching.value = false;
} else
print("Unhandled message: $message");
}

void cancel() async {
Future<void> cancel() {
_sendPort?.send("cancel");
await Future.delayed(Duration(milliseconds: 500));
_isolate?.kill(priority: Isolate.immediate);
Future.delayed(Duration(milliseconds: 500)).then((_) {
_isolate?.kill(priority: Isolate.immediate);
if (!_isDoneCompleter.isCompleted)
_isDoneCompleter.complete();
});
return _isDoneCompleter.future;
}
}

/// In new isolate search recursively for files with given extensions in given path
class _SearchServiceWorker {
bool _isCanceled = false;
int _resultsCount = 0;
static const int _maxResults = 1000;

void search(SearchOptions options) async {
var receivePort = ReceivePort();
receivePort.listen(_onMessage);
options.sendPort!.send(receivePort.sendPort);
var t1 = DateTime.now();

try {
if (options is SearchOptionsText)
Expand All @@ -129,7 +140,8 @@ class _SearchServiceWorker {
}
finally {
options.sendPort!.send("done");
print("Search done");
var t2 = DateTime.now();
print("Search done in ${t2.difference(t1).inSeconds} s");
}
}

Expand Down Expand Up @@ -159,6 +171,11 @@ class _SearchServiceWorker {
if (!test(line))
continue;
options.sendPort!.send(SearchResultText(filePath, line));
_resultsCount++;
if (_resultsCount >= _maxResults) {
_isCanceled = true;
return;
}
}
}
else {
Expand Down Expand Up @@ -230,6 +247,9 @@ class _SearchServiceWorker {
if (idData.id != options.id)
return;
options.sendPort!.send(SearchResultId(idData.xmlPath, idData));
_resultsCount++;
if (_resultsCount >= _maxResults)
_isCanceled = true;
}

void _searchIdInXml(String filePath, XmlElement root, SearchOptionsId options) {
Expand Down
6 changes: 6 additions & 0 deletions lib/utils.dart
Original file line number Diff line number Diff line change
Expand Up @@ -463,3 +463,9 @@ Future<List<String>> _getDatFileListFromMetadata(String metadataPath) async {

return files;
}

String pluralStr(int number, String label, [String numberSuffix = ""]) {
if (number == 1)
return "$number$numberSuffix $label";
return "$number$numberSuffix ${label}s";
}
3 changes: 2 additions & 1 deletion lib/widgets/FileHierarchyExplorer/FileExplorer.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import 'package:desktop_drop/desktop_drop.dart';
import 'package:flutter/material.dart';

import '../../stateManagement/statusInfo.dart';
import '../../utils.dart';
import '../../widgets/theme/customTheme.dart';
import '../../stateManagement/ChangeNotifierWidget.dart';
import '../../stateManagement/FileHierarchy.dart';
Expand All @@ -29,7 +30,7 @@ class _FileExplorerState extends ChangeNotifierState<FileExplorer> {

await Future.wait(futures);

messageLog.add("Opened ${details.files.length} file${details.files.length == 1 ? "" : "s"}");
messageLog.add("Opened ${pluralStr(details.files.length, "file")}");
}

@override
Expand Down
2 changes: 1 addition & 1 deletion lib/widgets/filesView/fileTabView.dart
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ class _FileTabViewState extends ChangeNotifierState<FileTabView> {
windowManager.focus();
setState(() {});

messageLog.add("Opened ${files.length} file${files.length == 1 ? "" : "s"}");
messageLog.add("Opened ${pluralStr(files.length, "file")}");
}

void pruneCachedWidgets() {
Expand Down
117 changes: 100 additions & 17 deletions lib/widgets/filesView/searchPanel.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@

import 'package:flutter/material.dart';
import 'package:mutex/mutex.dart';

import '../../background/IdsIndexer.dart';
import '../../background/searchService.dart';
import '../../stateManagement/ChangeNotifierWidget.dart';
import '../../stateManagement/Property.dart';
import '../../stateManagement/nestedNotifier.dart';
import '../../stateManagement/openFilesManager.dart';
import '../../utils.dart';
import '../misc/RowSeparated.dart';
import '../propEditors/simpleProps/boolPropIcon.dart';
import '../propEditors/simpleProps/propEditorFactory.dart';
Expand All @@ -28,6 +30,8 @@ class _SearchPanelState extends State<SearchPanel> {
Stream<SearchResult>? searchStream;
ValueNestedNotifier<SearchResult> searchResults = ValueNestedNotifier([]);
BoolProp isSearching = BoolProp(false);
Mutex cancelMutex = Mutex();
late final void Function() updateSearchStream;
// common options
StringProp extensions = StringProp("");
StringProp path = StringProp("");
Expand All @@ -41,6 +45,8 @@ class _SearchPanelState extends State<SearchPanel> {

@override
void initState() {
updateSearchStream = throttle(_updateSearchStream, 200);

extensions.addListener(updateSearchStream);
path.addListener(updateSearchStream);
query.addListener(updateSearchStream);
Expand Down Expand Up @@ -80,12 +86,13 @@ class _SearchPanelState extends State<SearchPanel> {
);
}

void resetSearch() {
searchService?.cancel();
Future<void> resetSearch() async {
if (searchService == null)
return;
await searchService?.cancel();
searchService = null;
searchStream = null;
searchResults.clear();
print("#########");
}

bool areAllFieldsFilled() {
Expand All @@ -101,8 +108,9 @@ class _SearchPanelState extends State<SearchPanel> {
return true;
}

void updateSearchStream() {
resetSearch();
void _updateSearchStream() async {
await cancelMutex.protect<void>(() => resetSearch());

if (!areAllFieldsFilled())
return;

Expand Down Expand Up @@ -265,20 +273,57 @@ class _SearchPanelState extends State<SearchPanel> {
child: ChangeNotifierBuilder(
notifier: searchResults,
builder: (context) {
String? errorText;
String? infoText;
if (!areAllFieldsFilled())
return Center(child: Text("Fill all fields to start search"));
errorText = "Fill all fields to start search";
if (searchResults.isEmpty) {
if (isSearching.value)
return Center(child: Text("Searching..."));
errorText = "Searching...";
else
return Center(child: Text("No results"));
errorText = "No results";
}
return ListView.builder(
itemCount: searchResults.length,
itemBuilder: (context, index) => _makeSearchResult(searchResults[index]),
);
var results = searchResults.toList();
var optP = "";
if (searchResults.length >= 1000) {
results = results.sublist(0, 1000);
infoText = "Stopped at 1000 results";
optP = "+";
}

var resultsFilesCount = searchResults.map((e) => e.filePath).toSet().length;
var infoStyle = TextStyle(
color: getTheme(context).textColor!.withOpacity(0.5),
fontSize: 12,
);
return Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 2.0),
child: Text(
errorText ?? "Found ${pluralStr(searchResults.length, "result", optP)} in ${pluralStr(resultsFilesCount, "file", optP)}",
style: infoStyle,
),
),
if (infoText != null)
Padding(
padding: const EdgeInsets.symmetric(horizontal: 8.0, vertical: 2.0),
child: Text(infoText, style: infoStyle),
),
SizedBox(height: 5),
Divider(height: 1,),
if (errorText == null)
Expanded(
child: ListView.builder(
itemCount: results.length,
itemBuilder: (context, index) => _makeSearchResult(results[index]),
),
),
],
);
},
)
),
);
}

Expand All @@ -300,14 +345,52 @@ class _SearchPanelState extends State<SearchPanel> {
}

Widget _makeSearchResultText(SearchResultText result) {
String text = result.line.replaceAll("\t", " ");
List<TextSpan> textSpans;
List<String> fillStrings;
RegExp regex;
if (isRegex.value) {
regex = RegExp(query.value, caseSensitive: isCaseSensitive.value);
textSpans = text.split(regex)
.map((e) => TextSpan(text: e))
.toList();
}
else {
regex = RegExp(RegExp.escape(query.value), caseSensitive: isCaseSensitive.value);
textSpans = text.split(regex)
.map((e) => TextSpan(
text: e,
style: TextStyle(
color: getTheme(context).textColor,
),
))
.toList();
}
fillStrings = regex.allMatches(text)
.map((e) => e.group(0)!)
.toList();
// insert colored spans of query between all text spans
for (int i = 1; i < textSpans.length; i += 2) {
textSpans.insert(i, TextSpan(
text: fillStrings[(i - 1) ~/ 2],
style: TextStyle(
color: getTheme(context).textColor,
backgroundColor: Theme.of(context).colorScheme.secondary.withOpacity(0.35),
overflow: TextOverflow.ellipsis,
),
));
}
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
result.line,
style: TextStyle(
fontSize: 14,
RichText(
text: TextSpan(
children: textSpans,
style: TextStyle(
overflow: TextOverflow.ellipsis,
),
),
overflow: TextOverflow.ellipsis,
maxLines: 1,
),
Tooltip(
Expand Down
7 changes: 7 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.8.0"
mutex:
dependency: "direct main"
description:
name: mutex
url: "https://pub.dartlang.org"
source: hosted
version: "3.0.0"
path:
dependency: "direct main"
description:
Expand Down
1 change: 1 addition & 0 deletions pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ dependencies:
flutter_launcher_icons: ^0.10.0
fluttertoast: ^8.0.9
highlight: ^0.7.0
mutex: ^3.0.0
path: ^1.8.2
shared_preferences: ^2.0.15
tuple: ^2.0.0
Expand Down

0 comments on commit 9d26d28

Please sign in to comment.