Skip to content

Commit

Permalink
Merge pull request #2880 from Wox-launcher/bao
Browse files Browse the repository at this point in the history
more cancelation for smooth ui and less cpu useage
  • Loading branch information
bao-qian authored Apr 27, 2020
2 parents 3c44c4a + a851da9 commit a2e328f
Show file tree
Hide file tree
Showing 5 changed files with 117 additions and 61 deletions.
3 changes: 2 additions & 1 deletion Wox.Core/Plugin/PluginManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using NLog;
using Wox.Infrastructure;
Expand Down Expand Up @@ -171,7 +172,7 @@ public static List<Result> QueryForPlugin(PluginPair pair, Query query)
{
List<Result> results = null;
var metadata = pair.Metadata;
var milliseconds = Logger.StopWatchDebug($"Cost for {metadata.Name}", () =>
var milliseconds = Logger.StopWatchDebug($"Query <{query.RawQuery}> Cost for {metadata.Name}", () =>
{
results = pair.Plugin.Query(query) ?? new List<Result>();
UpdatePluginMetadata(results, metadata, query);
Expand Down
5 changes: 3 additions & 2 deletions Wox.Core/Plugin/PluginsLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.IO;
using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using NLog;
using Wox.Infrastructure;
using Wox.Infrastructure.Exception;
Expand Down Expand Up @@ -33,7 +34,7 @@ public static IEnumerable<PluginPair> CSharpPlugins(List<PluginMetadata> source)
var plugins = new List<PluginPair>();
var metadatas = source.Where(o => o.Language.ToUpper() == AllowedLanguage.CSharp);

foreach (var metadata in metadatas)
Parallel.ForEach(metadatas, metadata =>
{
var milliseconds = Logger.StopWatchDebug($"Constructor init cost for {metadata.Name}", () =>
{
Expand Down Expand Up @@ -85,7 +86,7 @@ public static IEnumerable<PluginPair> CSharpPlugins(List<PluginMetadata> source)
});
metadata.InitTime += milliseconds;

}
});
return plugins;
}

Expand Down
5 changes: 4 additions & 1 deletion Wox/PublicAPIInstance.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using Squirrel;
Expand Down Expand Up @@ -157,7 +158,9 @@ public void PushResults(Query query, PluginMetadata plugin, List<Result> results
});
Task.Run(() =>
{
_mainVM.UpdateResultView(results, plugin, query);

var t = new CancellationTokenSource().Token;
_mainVM.UpdateResultView(results, plugin, query, t);
});
}

Expand Down
114 changes: 65 additions & 49 deletions Wox/ViewModel/MainViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ public class MainViewModel : BaseModel, ISavable
private readonly TopMostRecord _topMostRecord;

private CancellationTokenSource _updateSource;
private CancellationToken _updateToken;
private bool _saved;

private readonly Internationalization _translator;
Expand Down Expand Up @@ -91,13 +90,13 @@ private void RegisterResultsUpdatedEvent()
var plugin = (IResultUpdated)pair.Plugin;
plugin.ResultsUpdated += (s, e) =>
{
var token = _updateToken;
var token = _updateSource.Token;
Task.Run(() =>
{
if (token.IsCancellationRequested) { return; }
PluginManager.UpdatePluginMetadata(e.Results, pair.Metadata, e.Query);
if (token.IsCancellationRequested) { return; }
UpdateResultView(e.Results, pair.Metadata, e.Query);
UpdateResultView(e.Results, pair.Metadata, e.Query, token);
}, token);
};
}
Expand Down Expand Up @@ -382,74 +381,89 @@ private void QueryResults()
{
if (_updateSource != null && !_updateSource.IsCancellationRequested)
{
// first condition used for init run
// second condition used when task has already been canceled in last turn
_updateSource.Cancel();
_updateSource.Dispose();
Logger.WoxDebug($"cancel init {_updateSource.Token.GetHashCode()} {Thread.CurrentThread.ManagedThreadId} {QueryText}");
}
var updateSource = new CancellationTokenSource();
_updateSource = updateSource;
var token = updateSource.Token;
_updateToken = token;
var source = new CancellationTokenSource();
_updateSource = source;
var token = source.Token;

ProgressBarVisibility = Visibility.Hidden;

var queryText = QueryText.Trim();
if (!string.IsNullOrEmpty(queryText))
Task.Run(() =>
{
if (token.IsCancellationRequested) { return; }
var query = QueryBuilder.Build(queryText, PluginManager.NonGlobalPlugins);
if (query != null)
if (!string.IsNullOrEmpty(queryText))
{
// handle the exclusiveness of plugin using action keyword
RemoveOldQueryResults(query);
if (token.IsCancellationRequested) { return; }
var query = QueryBuilder.Build(queryText, PluginManager.NonGlobalPlugins);
_lastQuery = query;
if (query != null)
{
// handle the exclusiveness of plugin using action keyword
if (token.IsCancellationRequested) { return; }
RemoveOldQueryResults(query);

Task.Delay(200, token).ContinueWith(_ =>
{ // start the progress bar if query takes more than 200 ms
if (!token.IsCancellationRequested)
Task.Delay(200, token).ContinueWith(_ =>
{
ProgressBarVisibility = Visibility.Visible;
Logger.WoxDebug($"progressbar visible {query} {token.IsCancellationRequested} {ProgressBarVisibility}");
}
}, token);
Logger.WoxTrace($"progressbar visible 1 {token.GetHashCode()} {token.IsCancellationRequested} {Thread.CurrentThread.ManagedThreadId} {query} {ProgressBarVisibility}");
// start the progress bar if query takes more than 200 ms
if (!token.IsCancellationRequested)
{
ProgressBarVisibility = Visibility.Visible;
}
}, token);


Task.Run(() =>
{
if (token.IsCancellationRequested) { return; }
var plugins = PluginManager.ValidPluginsForQuery(query);

// so looping will stop once it was cancelled
var parallelOptions = new ParallelOptions { CancellationToken = token };
try
var option = new ParallelOptions()
{
CancellationToken = token,
};

Parallel.ForEach(plugins, plugin =>
{
Parallel.ForEach(plugins, parallelOptions, plugin =>
if (!plugin.Metadata.Disabled)
{
if (!plugin.Metadata.Disabled)
if (token.IsCancellationRequested)
{
if (token.IsCancellationRequested) { return; }
var results = PluginManager.QueryForPlugin(plugin, query);
if (token.IsCancellationRequested) { return; }
UpdateResultView(results, plugin.Metadata, query);
Logger.WoxDebug($"canceled {token.GetHashCode()} {Thread.CurrentThread.ManagedThreadId} {queryText} {plugin.Metadata.Name}");
return;
}
});
}
catch (OperationCanceledException)
{
// nothing to do here
}
var results = PluginManager.QueryForPlugin(plugin, query);
if (token.IsCancellationRequested)
{
Logger.WoxDebug($"canceled {token.GetHashCode()} {Thread.CurrentThread.ManagedThreadId} {queryText} {plugin.Metadata.Name}");
return;
}
UpdateResultView(results, plugin.Metadata, query, token);
}
});

Logger.WoxTrace($"progressbar visible 2 {token.GetHashCode()} {token.IsCancellationRequested} {Thread.CurrentThread.ManagedThreadId} {query} {ProgressBarVisibility}");
if (!token.IsCancellationRequested)
{ // update to hidden if this is still the current query
{
// used to cancel previous progress bar visible task
source.Cancel();
source.Dispose();
// update to hidden if this is still the current query
ProgressBarVisibility = Visibility.Hidden;
updateSource.Cancel();
updateSource.Dispose();

}
}, token);
}
}
}
else
{
Results.Clear();
Results.Visbility = Visibility.Collapsed;
}
else
{
Results.Clear();
Results.Visbility = Visibility.Collapsed;
}
}, token);

}

private void RemoveOldQueryResults(Query query)
Expand Down Expand Up @@ -671,10 +685,11 @@ public void Save()
/// <summary>
/// To avoid deadlock, this method should not called from main thread
/// </summary>
public void UpdateResultView(List<Result> list, PluginMetadata metadata, Query originQuery)
public void UpdateResultView(List<Result> list, PluginMetadata metadata, Query originQuery, CancellationToken token)
{
foreach (var result in list)
{
if (token.IsCancellationRequested) { return; }
if (_topMostRecord.IsTopMost(result))
{
result.Score = int.MaxValue;
Expand All @@ -685,9 +700,10 @@ public void UpdateResultView(List<Result> list, PluginMetadata metadata, Query o
}
}

if (token.IsCancellationRequested) { return; }
if (originQuery.RawQuery == _lastQuery.RawQuery)
{
Results.AddResults(list, metadata.ID);
Results.AddResults(list, metadata.ID, token);
}

if (Results.Visbility != Visibility.Visible && list.Count > 0)
Expand Down
51 changes: 43 additions & 8 deletions Wox/ViewModel/ResultsViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
Expand Down Expand Up @@ -133,17 +134,39 @@ public void RemoveResultsFor(PluginMetadata metadata)
Results.RemoveAll(r => r.Result.PluginID == metadata.ID);
}

public void AddResults(List<Result> newRawResults, string resultId)
{
lock (_addResultsLock)
{
var t = new CancellationTokenSource().Token;
var newResults = NewResults(newRawResults, resultId, t);
// update UI in one run, so it can avoid UI flickering
Results.Update(newResults, t);

if (Results.Count > 0)
{
Margin = new Thickness { Top = 8 };
SelectedIndex = 0;
}
else
{
Margin = new Thickness { Top = 0 };
}
}
}

/// <summary>
/// To avoid deadlock, this method should not called from main thread
/// </summary>
public void AddResults(List<Result> newRawResults, string resultId)
public void AddResults(List<Result> newRawResults, string resultId, System.Threading.CancellationToken token)
{

lock (_addResultsLock)
{
var newResults = NewResults(newRawResults, resultId);
if (token.IsCancellationRequested) { return; }
var newResults = NewResults(newRawResults, resultId, token);
// update UI in one run, so it can avoid UI flickering
Results.Update(newResults);
if (token.IsCancellationRequested) { return; }
Results.Update(newResults, token);

if (Results.Count > 0)
{
Expand All @@ -157,24 +180,28 @@ public void AddResults(List<Result> newRawResults, string resultId)
}
}

private List<ResultViewModel> NewResults(List<Result> newRawResults, string resultId)
private List<ResultViewModel> NewResults(List<Result> newRawResults, string resultId, CancellationToken token)
{
var newResults = newRawResults.Select(r => new ResultViewModel(r)).ToList();
var results = Results.ToList();
var oldResults = results.Where(r => r.Result.PluginID == resultId).ToList();

if (token.IsCancellationRequested) { return new List<ResultViewModel>(); }
// intersection of A (old results) and B (new newResults)
var intersection = oldResults.Intersect(newResults).ToList();

if (token.IsCancellationRequested) { return new List<ResultViewModel>(); }
// remove result of relative complement of B in A
foreach (var result in oldResults.Except(intersection))
{
if (token.IsCancellationRequested) { return new List<ResultViewModel>(); }
results.Remove(result);
}

// update index for result in intersection of A and B
foreach (var commonResult in intersection)
{
if (token.IsCancellationRequested) { return new List<ResultViewModel>(); }
int oldIndex = results.IndexOf(commonResult);
int oldScore = results[oldIndex].Result.Score;
var newResult = newResults[newResults.IndexOf(commonResult)];
Expand All @@ -198,6 +225,7 @@ private List<ResultViewModel> NewResults(List<Result> newRawResults, string resu
// insert result in relative complement of A in B
foreach (var result in newResults.Except(intersection))
{
if (token.IsCancellationRequested) { return new List<ResultViewModel>(); }
if (results.Count <= maxResults)
{
int newIndex = InsertIndexOf(result.Result.Score, results);
Expand All @@ -209,6 +237,7 @@ private List<ResultViewModel> NewResults(List<Result> newRawResults, string resu
}
}

if (token.IsCancellationRequested) { return new List<ResultViewModel>(); }
if (results.Count > maxResults)
{
var resultsCopy = results.GetRange(0, maxResults);
Expand All @@ -218,7 +247,7 @@ private List<ResultViewModel> NewResults(List<Result> newRawResults, string resu
{
return results;
}

}
#endregion

Expand Down Expand Up @@ -273,7 +302,7 @@ public void RemoveAll(Predicate<ResultViewModel> predicate)
/// Update the results collection with new results, try to keep identical results
/// </summary>
/// <param name="newItems"></param>
public void Update(List<ResultViewModel> newItems)
public void Update(List<ResultViewModel> newItems, System.Threading.CancellationToken token)
{
CheckReentrancy();

Expand All @@ -283,7 +312,9 @@ public void Update(List<ResultViewModel> newItems)

for (int i = 0; i < location; i++)
{
ResultViewModel oldResult = this[i];
if (token.IsCancellationRequested) { return; }

ResultViewModel oldResult = this[i];
ResultViewModel newResult = newItems[i];
Logger.WoxTrace(
$"index {i} " +
Expand Down Expand Up @@ -312,6 +343,8 @@ public void Update(List<ResultViewModel> newItems)
{
for (int i = oldCount; i < newCount; i++)
{
if (token.IsCancellationRequested) { return; }

Logger.WoxTrace($"add {i} new<{newItems[i].Result.Title}");
Add(newItems[i]);
}
Expand All @@ -320,6 +353,8 @@ public void Update(List<ResultViewModel> newItems)
{
for (int i = oldCount - 1; i >= newCount; i--)
{
if (token.IsCancellationRequested) { return; }

RemoveAt(i);
}
}
Expand Down

0 comments on commit a2e328f

Please sign in to comment.