mirror of
https://github.com/2dust/v2rayN.git
synced 2026-04-17 13:05:46 +00:00
Limit MsgView log queue growth to reduce memory usage
This commit is contained in:
parent
214a09bc48
commit
d186b1d8e9
3 changed files with 120 additions and 31 deletions
|
|
@ -2,9 +2,15 @@ namespace ServiceLib.ViewModels;
|
||||||
|
|
||||||
public class MsgViewModel : MyReactiveObject
|
public class MsgViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
|
private const int MaxQueuedMessagesVisible = 2_000;
|
||||||
|
private const int MaxQueuedMessagesHidden = 500;
|
||||||
|
private const int MaxFlushMessages = 200;
|
||||||
|
private const int MaxFlushChars = 64 * 1024;
|
||||||
private readonly ConcurrentQueue<string> _queueMsg = new();
|
private readonly ConcurrentQueue<string> _queueMsg = new();
|
||||||
private volatile bool _lastMsgFilterNotAvailable;
|
private volatile bool _lastMsgFilterNotAvailable;
|
||||||
|
private int _queuedMessageCount = 0;
|
||||||
private int _showLock = 0; // 0 = unlocked, 1 = locked
|
private int _showLock = 0; // 0 = unlocked, 1 = locked
|
||||||
|
private long _droppedMessageCount = 0;
|
||||||
public int NumMaxMsg { get; } = 500;
|
public int NumMaxMsg { get; } = 500;
|
||||||
|
|
||||||
[Reactive]
|
[Reactive]
|
||||||
|
|
@ -31,19 +37,28 @@ public class MsgViewModel : MyReactiveObject
|
||||||
|
|
||||||
AppEvents.SendMsgViewRequested
|
AppEvents.SendMsgViewRequested
|
||||||
.AsObservable()
|
.AsObservable()
|
||||||
//.ObserveOn(RxSchedulers.MainThreadScheduler)
|
//.ObserveOn(RxApp.MainThreadScheduler)
|
||||||
.Subscribe(content => _ = AppendQueueMsg(content));
|
.Subscribe(content => _ = AppendQueueMsg(content));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task AppendQueueMsg(string msg)
|
private Task AppendQueueMsg(string msg)
|
||||||
{
|
{
|
||||||
if (AutoRefresh == false)
|
if (AutoRefresh == false)
|
||||||
{
|
{
|
||||||
return;
|
return Task.CompletedTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
EnqueueQueueMsg(msg);
|
if (!EnqueueQueueMsg(msg))
|
||||||
|
{
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
TryScheduleDrain();
|
||||||
|
return Task.CompletedTask;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryScheduleDrain()
|
||||||
|
{
|
||||||
if (!AppManager.Instance.ShowInTaskbar)
|
if (!AppManager.Instance.ShowInTaskbar)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
|
|
@ -54,25 +69,59 @@ public class MsgViewModel : MyReactiveObject
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_ = DrainQueueMsgAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task DrainQueueMsgAsync()
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await Task.Delay(500).ConfigureAwait(false);
|
await Task.Delay(500).ConfigureAwait(false);
|
||||||
|
|
||||||
var sb = new StringBuilder();
|
var batch = DequeueBatch();
|
||||||
while (_queueMsg.TryDequeue(out var line))
|
if (_updateView != null && !string.IsNullOrEmpty(batch))
|
||||||
{
|
{
|
||||||
sb.Append(line);
|
await _updateView(EViewAction.DispatcherShowMsg, batch);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _updateView?.Invoke(EViewAction.DispatcherShowMsg, sb.ToString());
|
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
Interlocked.Exchange(ref _showLock, 0);
|
Interlocked.Exchange(ref _showLock, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (AppManager.Instance.ShowInTaskbar && !_queueMsg.IsEmpty)
|
||||||
|
{
|
||||||
|
TryScheduleDrain();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnqueueQueueMsg(string msg)
|
private string DequeueBatch()
|
||||||
|
{
|
||||||
|
var sb = new StringBuilder();
|
||||||
|
var droppedCount = Interlocked.Exchange(ref _droppedMessageCount, 0);
|
||||||
|
if (droppedCount > 0)
|
||||||
|
{
|
||||||
|
sb.Append($"----- Message queue trimmed: dropped {droppedCount} messages -----{Environment.NewLine}");
|
||||||
|
}
|
||||||
|
|
||||||
|
var dequeuedCount = 0;
|
||||||
|
while (dequeuedCount < MaxFlushMessages
|
||||||
|
&& _queueMsg.TryDequeue(out var line))
|
||||||
|
{
|
||||||
|
Interlocked.Decrement(ref _queuedMessageCount);
|
||||||
|
sb.Append(line);
|
||||||
|
dequeuedCount++;
|
||||||
|
|
||||||
|
if (sb.Length >= MaxFlushChars)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool EnqueueQueueMsg(string msg)
|
||||||
{
|
{
|
||||||
//filter msg
|
//filter msg
|
||||||
if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
|
if (MsgFilter.IsNotEmpty() && !_lastMsgFilterNotAvailable)
|
||||||
|
|
@ -81,23 +130,41 @@ public class MsgViewModel : MyReactiveObject
|
||||||
{
|
{
|
||||||
if (!Regex.IsMatch(msg, MsgFilter))
|
if (!Regex.IsMatch(msg, MsgFilter))
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_queueMsg.Enqueue(ex.Message);
|
msg = ex.Message;
|
||||||
_lastMsgFilterNotAvailable = true;
|
_lastMsgFilterNotAvailable = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_queueMsg.Enqueue(msg);
|
_queueMsg.Enqueue(NormalizeMessage(msg));
|
||||||
if (!msg.EndsWith(Environment.NewLine))
|
Interlocked.Increment(ref _queuedMessageCount);
|
||||||
|
TrimQueuedMessages();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TrimQueuedMessages()
|
||||||
|
{
|
||||||
|
var maxQueuedMessages = AppManager.Instance.ShowInTaskbar
|
||||||
|
? MaxQueuedMessagesVisible
|
||||||
|
: MaxQueuedMessagesHidden;
|
||||||
|
|
||||||
|
while (Volatile.Read(ref _queuedMessageCount) > maxQueuedMessages
|
||||||
|
&& _queueMsg.TryDequeue(out _))
|
||||||
{
|
{
|
||||||
_queueMsg.Enqueue(Environment.NewLine);
|
Interlocked.Decrement(ref _queuedMessageCount);
|
||||||
|
Interlocked.Increment(ref _droppedMessageCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string NormalizeMessage(string msg)
|
||||||
|
{
|
||||||
|
return msg.EndsWith(Environment.NewLine) ? msg : msg + Environment.NewLine;
|
||||||
|
}
|
||||||
|
|
||||||
//public void ClearMsg()
|
//public void ClearMsg()
|
||||||
//{
|
//{
|
||||||
// _queueMsg.Clear();
|
// _queueMsg.Clear();
|
||||||
|
|
|
||||||
|
|
@ -43,24 +43,32 @@ public partial class MsgView : ReactiveUserControl<MsgViewModel>
|
||||||
|
|
||||||
private void ShowMsg(object msg)
|
private void ShowMsg(object msg)
|
||||||
{
|
{
|
||||||
//var lineCount = txtMsg.LineCount;
|
|
||||||
//if (lineCount > ViewModel?.NumMaxMsg)
|
|
||||||
//{
|
|
||||||
// var cutLine = txtMsg.Document.GetLineByNumber(lineCount - KeepLines);
|
|
||||||
// txtMsg.Document.Remove(0, cutLine.Offset);
|
|
||||||
//}
|
|
||||||
if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
|
|
||||||
{
|
|
||||||
ClearMsg();
|
|
||||||
}
|
|
||||||
|
|
||||||
txtMsg.AppendText(msg.ToString());
|
txtMsg.AppendText(msg.ToString());
|
||||||
|
TrimMsg();
|
||||||
if (togScrollToEnd.IsChecked ?? true)
|
if (togScrollToEnd.IsChecked ?? true)
|
||||||
{
|
{
|
||||||
txtMsg.ScrollToEnd();
|
txtMsg.ScrollToEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TrimMsg()
|
||||||
|
{
|
||||||
|
var maxLines = ViewModel?.NumMaxMsg ?? 500;
|
||||||
|
if (txtMsg.LineCount <= maxLines || txtMsg.Document == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var firstKeepLine = txtMsg.LineCount - maxLines + 1;
|
||||||
|
var cutLine = txtMsg.Document.GetLineByNumber(firstKeepLine);
|
||||||
|
if (cutLine.Offset <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
txtMsg.Document.Remove(0, cutLine.Offset);
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearMsg()
|
public void ClearMsg()
|
||||||
{
|
{
|
||||||
txtMsg.Clear();
|
txtMsg.Clear();
|
||||||
|
|
|
||||||
|
|
@ -45,18 +45,32 @@ public partial class MsgView
|
||||||
|
|
||||||
private void ShowMsg(object msg)
|
private void ShowMsg(object msg)
|
||||||
{
|
{
|
||||||
if (txtMsg.LineCount > ViewModel?.NumMaxMsg)
|
|
||||||
{
|
|
||||||
ClearMsg();
|
|
||||||
}
|
|
||||||
|
|
||||||
txtMsg.AppendText(msg.ToString());
|
txtMsg.AppendText(msg.ToString());
|
||||||
|
TrimMsg();
|
||||||
if (togScrollToEnd.IsChecked ?? true)
|
if (togScrollToEnd.IsChecked ?? true)
|
||||||
{
|
{
|
||||||
txtMsg.ScrollToEnd();
|
txtMsg.ScrollToEnd();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void TrimMsg()
|
||||||
|
{
|
||||||
|
var maxLines = ViewModel?.NumMaxMsg ?? 500;
|
||||||
|
if (txtMsg.LineCount <= maxLines)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var keepFromCharIndex = txtMsg.GetCharacterIndexFromLineIndex(txtMsg.LineCount - maxLines);
|
||||||
|
if (keepFromCharIndex <= 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
txtMsg.Select(0, keepFromCharIndex);
|
||||||
|
txtMsg.SelectedText = string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
public void ClearMsg()
|
public void ClearMsg()
|
||||||
{
|
{
|
||||||
txtMsg.Clear();
|
txtMsg.Clear();
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue