From 08d76e9038d76fb12e600757496e8ccbb077caf0 Mon Sep 17 00:00:00 2001 From: Randall Flagg Date: Wed, 18 Jun 2025 13:57:11 +0300 Subject: [PATCH 1/2] Refactoring and removing warnings --- src/LogExpert.Core/Classes/Log/LogfileReader.cs | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/src/LogExpert.Core/Classes/Log/LogfileReader.cs b/src/LogExpert.Core/Classes/Log/LogfileReader.cs index 445ffd1e..9c961676 100644 --- a/src/LogExpert.Core/Classes/Log/LogfileReader.cs +++ b/src/LogExpert.Core/Classes/Log/LogfileReader.cs @@ -16,8 +16,6 @@ public class LogfileReader : IAutoLogLineColumnizerCallback, IDisposable private static readonly ILogger _logger = LogManager.GetCurrentClassLogger(); - private readonly GetLogLineFx _logLineFx; - private readonly string _fileName; private readonly int _max_buffers; private readonly int _maxLinesPerBuffer; @@ -68,7 +66,6 @@ public LogfileReader (string fileName, EncodingOptions encodingOptions, bool mul _maxLinesPerBuffer = linesPerBuffer; _multiFileOptions = multiFileOptions; _pluginRegistry = pluginRegistry; - _logLineFx = GetLogLineInternal; _disposed = false; InitLruBuffers(); @@ -108,7 +105,6 @@ public LogfileReader (string[] fileNames, EncodingOptions encodingOptions, int b _maxLinesPerBuffer = linesPerBuffer; _multiFileOptions = multiFileOptions; _pluginRegistry = pluginRegistry; - _logLineFx = GetLogLineInternal; _disposed = false; InitLruBuffers(); @@ -127,12 +123,6 @@ public LogfileReader (string[] fileNames, EncodingOptions encodingOptions, int b #endregion - #region Delegates - - private delegate Task GetLogLineFx (int lineNum); - - #endregion - #region Events public event EventHandler FileSizeChanged; @@ -422,7 +412,7 @@ public async Task GetLogLineWithWait (int lineNum) if (!_isFastFailOnGetLogLine) { - var task = Task.Run(() => _logLineFx(lineNum)); + var task = Task.Run(() => GetLogLineInternal(lineNum)); if (task.Wait(WAIT_TIME)) { result = task.Result; @@ -440,7 +430,7 @@ public async Task GetLogLineWithWait (int lineNum) if (!_isFailModeCheckCallPending) { _isFailModeCheckCallPending = true; - var logLine = await _logLineFx(lineNum); + var logLine = await GetLogLineInternal(lineNum); GetLineFinishedCallback(logLine); } } From 5bb3251be2047b7ac4f086d81a1b751cc1e339cd Mon Sep 17 00:00:00 2001 From: Randall Flagg Date: Wed, 18 Jun 2025 16:53:57 +0300 Subject: [PATCH 2/2] Need to check this changes --- .../Classes/Log/LogfileReader.cs | 155 +++++++++++++----- .../Controls/LogWindow/LogWindowPrivate.cs | 2 +- .../Controls/LogWindow/LogWindowPublic.cs | 6 +- .../Dialogs/LogTabWindow/SettingsDialog.cs | 2 +- 4 files changed, 115 insertions(+), 50 deletions(-) diff --git a/src/LogExpert.Core/Classes/Log/LogfileReader.cs b/src/LogExpert.Core/Classes/Log/LogfileReader.cs index 9c961676..f6dcabc8 100644 --- a/src/LogExpert.Core/Classes/Log/LogfileReader.cs +++ b/src/LogExpert.Core/Classes/Log/LogfileReader.cs @@ -14,7 +14,7 @@ public class LogfileReader : IAutoLogLineColumnizerCallback, IDisposable { #region Fields - private static readonly ILogger _logger = LogManager.GetCurrentClassLogger(); + private static readonly Logger _logger = LogManager.GetCurrentClassLogger(); private readonly string _fileName; private readonly int _max_buffers; @@ -386,7 +386,7 @@ public int ShiftBuffers () public ILogLine GetLogLine (int lineNum) { - return GetLogLineInternal(lineNum).Result; + return GetLogLineInternal(lineNum, _cts.Token).Result; //TODO: Is this token correct? } /// @@ -406,36 +406,70 @@ public ILogLine GetLogLine (int lineNum) /// public async Task GetLogLineWithWait (int lineNum) { - const int WAIT_TIME = 1000; - - ILogLine result = null; + const int WAIT_MS = 1000; + // If we’re not in fast-fail mode, try once with timeout if (!_isFastFailOnGetLogLine) { - var task = Task.Run(() => GetLogLineInternal(lineNum)); - if (task.Wait(WAIT_TIME)) + using var cts = new CancellationTokenSource(); + cts.CancelAfter(WAIT_MS); + + try { - result = task.Result; + // Offload the read so UI never blocks + var line = await GetLogLineInternal(lineNum, cts.Token).ConfigureAwait(false); + + // Completed successfully in time _isFastFailOnGetLogLine = false; + return line; } - else + catch (OperationCanceledException) { + // Timed out _isFastFailOnGetLogLine = true; - _logger.Debug(CultureInfo.InvariantCulture, "No result after {0}ms. Returning .", WAIT_TIME); + _logger.Debug(CultureInfo.InvariantCulture, "Timeout after {0}ms. Returning .", WAIT_MS); + TriggerBackgroundRecovery(lineNum); + return null; } - } - else - { - _logger.Debug(CultureInfo.InvariantCulture, "Fast failing GetLogLine()"); - if (!_isFailModeCheckCallPending) + catch (Exception ex) { - _isFailModeCheckCallPending = true; - var logLine = await GetLogLineInternal(lineNum); - GetLineFinishedCallback(logLine); + // Real error—flip fast-fail and rethrow + _isFastFailOnGetLogLine = true; + _logger.Error(ex, "Exception in GetLogLineInternal for line {0}.", lineNum); + throw; } } - return result; + // Fast-fail path: immediate null, kick off a background refresh + _logger.Debug(CultureInfo.InvariantCulture, "Fast failing GetLogLine()"); + TriggerBackgroundRecovery(lineNum); + return null; + } + + private void TriggerBackgroundRecovery (int lineNum) + { + if (_isFailModeCheckCallPending) + return; + + _isFailModeCheckCallPending = true; + + // Fire-and-forget check (no UI thread marshalling needed) + _ = Task.Run(async () => + { + try + { + var line = await GetLogLineInternal(lineNum, CancellationToken.None).ConfigureAwait(false); + GetLineFinishedCallback(line); + } + catch (Exception ex) + { + _logger.Error(ex, "Deferred GetLogLineInternal failed."); + } + finally + { + _isFailModeCheckCallPending = false; + } + }); } /// @@ -745,43 +779,74 @@ private ILogFileInfo AddFile (string fileName) return info; } - private Task GetLogLineInternal (int lineNum) + private Task GetLogLineInternal (int lineNum, CancellationToken ct) { + // Run all the heavy work off the UI thread, + // and honor the cancellation token + return Task.Run(() => GetLogLineInternalCore(lineNum, ct), ct); + } + + private ILogLine GetLogLineInternalCore (int lineNum, CancellationToken ct) + { + ct.ThrowIfCancellationRequested(); + if (_isDeleted) { - _logger.Debug(CultureInfo.InvariantCulture, "Returning null for line {0} because file is deleted.", lineNum); + _logger.Debug( + CultureInfo.InvariantCulture, + "Returning null for line {0} because file is deleted.", + lineNum); - // fast fail if dead file was detected. Prevents repeated lags in GUI thread caused by callbacks from control (e.g. repaint) return null; } - AcquireBufferListReaderLock(); - LogBuffer logBuffer = GetBufferForLine(lineNum); - if (logBuffer == null) + AcquireBufferListReaderLock(); // blocking call off UI + try { - ReleaseBufferListReaderLock(); - _logger.Error("Cannot find buffer for line {0}, file: {1}{2}", lineNum, _fileName, IsMultiFile ? " (MultiFile)" : ""); - return null; - } + ct.ThrowIfCancellationRequested(); - // disposeLock prevents that the garbage collector is disposing just in the moment we use the buffer - _disposeLock.AcquireReaderLock(Timeout.Infinite); - if (logBuffer.IsDisposed) - { - LockCookie cookie = _disposeLock.UpgradeToWriterLock(Timeout.Infinite); - lock (logBuffer.FileInfo) + var logBuffer = GetBufferForLine(lineNum); + if (logBuffer == null) { - ReReadBuffer(logBuffer); + _logger.Error("Cannot find buffer for line {0}, file: {1}{2}", lineNum, _fileName, IsMultiFile ? " (MultiFile)" : ""); + return null; } - _disposeLock.DowngradeFromWriterLock(ref cookie); - } + _disposeLock.AcquireReaderLock(Timeout.Infinite); + try + { + ct.ThrowIfCancellationRequested(); - ILogLine line = logBuffer.GetLineOfBlock(lineNum - logBuffer.StartLine); - _disposeLock.ReleaseReaderLock(); - ReleaseBufferListReaderLock(); + if (logBuffer.IsDisposed) + { + var cookie = _disposeLock.UpgradeToWriterLock(Timeout.Infinite); + try + { + lock (logBuffer.FileInfo) + { + ReReadBuffer(logBuffer); + } - return Task.FromResult(line); + ct.ThrowIfCancellationRequested(); + } + finally + { + _disposeLock.DowngradeFromWriterLock(ref cookie); + } + } + + // Actual line extraction + return logBuffer.GetLineOfBlock(lineNum - logBuffer.StartLine); + } + finally + { + _disposeLock.ReleaseReaderLock(); + } + } + finally + { + ReleaseBufferListReaderLock(); + } } private void InitLruBuffers () @@ -1775,13 +1840,13 @@ private void DumpBufferInfos (LogBuffer buffer) #endregion - public void Dispose() + public void Dispose () { Dispose(true); GC.SuppressFinalize(this); // Suppress finalization (not needed but best practice) } - protected virtual void Dispose(bool disposing) + protected virtual void Dispose (bool disposing) { if (!_disposed) { @@ -1798,7 +1863,7 @@ protected virtual void Dispose(bool disposing) //TODO: Seems that this can be deleted. Need to verify. ~LogfileReader () { - Dispose (false); + Dispose(false); } protected virtual void OnFileSizeChanged (LogEventArgs e) diff --git a/src/LogExpert.UI/Controls/LogWindow/LogWindowPrivate.cs b/src/LogExpert.UI/Controls/LogWindow/LogWindowPrivate.cs index c50fee65..a859bd8c 100644 --- a/src/LogExpert.UI/Controls/LogWindow/LogWindowPrivate.cs +++ b/src/LogExpert.UI/Controls/LogWindow/LogWindowPrivate.cs @@ -598,7 +598,6 @@ private void LoadingFinished () dataGridView.SuspendLayout(); dataGridView.RowCount = _logFileReader.LineCount; dataGridView.CurrentCellChanged += OnDataGridViewCurrentCellChanged; - dataGridView.Enabled = true; dataGridView.AutoResizeColumns(DataGridViewAutoSizeColumnsMode.DisplayedCells); dataGridView.ResumeLayout(); _progressEventArgs.Visible = false; @@ -622,6 +621,7 @@ private void LoadingFinished () PreferencesChanged(fontName, fontSize, setLastColumnWidth, lastColumnWidth, true, SettingsFlags.All); //LoadPersistenceData(); + dataGridView.Enabled = true; } private void LogEventWorker () diff --git a/src/LogExpert.UI/Controls/LogWindow/LogWindowPublic.cs b/src/LogExpert.UI/Controls/LogWindow/LogWindowPublic.cs index 601f3064..befa9737 100644 --- a/src/LogExpert.UI/Controls/LogWindow/LogWindowPublic.cs +++ b/src/LogExpert.UI/Controls/LogWindow/LogWindowPublic.cs @@ -404,11 +404,11 @@ public void CellPainting (BufferedDataGridView gridView, int rowIndex, DataGridV if (rowIndex < 0 || e.ColumnIndex < 0) { e.Handled = false; - return; + //return; } - ILogLine line = _logFileReader.GetLogLineWithWait(rowIndex).Result; - + //ILogLine line = _logFileReader.GetLogLineWithWait(rowIndex).Result; + ILogLine line = null; if (line != null) { HighlightEntry entry = FindFirstNoWordMatchHilightEntry(line); diff --git a/src/LogExpert.UI/Dialogs/LogTabWindow/SettingsDialog.cs b/src/LogExpert.UI/Dialogs/LogTabWindow/SettingsDialog.cs index bdf5df26..749ba70e 100644 --- a/src/LogExpert.UI/Dialogs/LogTabWindow/SettingsDialog.cs +++ b/src/LogExpert.UI/Dialogs/LogTabWindow/SettingsDialog.cs @@ -165,7 +165,7 @@ private void FillDialog () FillMultifileSettings(); FillEncodingList(); - var temp = Encoding.GetEncoding(Preferences.DefaultEncoding); + //var temp = Encoding.GetEncoding(Preferences.DefaultEncoding);//TODO: Delete comboBoxEncoding.SelectedItem = Encoding.GetEncoding(Preferences.DefaultEncoding); checkBoxMaskPrio.Checked = Preferences.MaskPrio;