Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CompactGUI.Core/Analyser.cs
Original file line numberDiff line numberDiff line change
Expand Up@@ -157,6 +157,6 @@ public List<ExtensionResult> GetPoorlyCompressedExtensions()
public void Dispose()
{
_folderMonitor.Dispose();
_analysedFileDetails?.Clear();
_analysedFileDetails = null;
}
}
45 changes: 31 additions & 14 deletions CompactGUI.Watcher/BackgroundCompactor.vb
Original file line numberDiff line numberDiff line change
Expand Up@@ -51,12 +51,15 @@ Public Class BackgroundCompactor

Private Function CreateCompactor(folder As String,
compressionLevel As Core.WOFCompressionAlgorithm,
ByRef analyser As Core.Analyser,
Optional excludedFileTypes As String() = Nothing) As Core.Compactor

analyser = Nothing
If compressionLevel = Core.WOFCompressionAlgorithm.NO_COMPRESSION Then Return Nothing

Dim effectiveExclusions = If(excludedFileTypes Is Nothing, _excludedFileTypes, excludedFileTypes)
Return New Core.Compactor(folder, compressionLevel, effectiveExclusions, New Core.Analyser(folder, NullLogger(Of Core.Analyser).Instance))
analyser = New Core.Analyser(folder, NullLogger(Of Core.Analyser).Instance)
Return New Core.Compactor(folder, compressionLevel, effectiveExclusions, analyser)

End Function

Expand All@@ -74,8 +77,10 @@ Public Class BackgroundCompactor
isCompactingPaused = False

Dim currentProcess As Process = Process.GetCurrentProcess()
Dim originalPriority As ProcessPriorityClass = ProcessPriorityClass.Normal

Try
originalPriority = currentProcess.PriorityClass
currentProcess.PriorityClass = ProcessPriorityClass.Idle

For Each folder In folders.ToList
Expand All@@ -88,16 +93,21 @@ Public Class BackgroundCompactor

folder.IsWorking = True
Dim compactor As Core.Compactor = Nothing
Dim disposeCompactor As Boolean = True
Dim compactorAnalyser As Core.Analyser = Nothing
Dim disposeResources As Boolean = True

Try
WatcherLog.CompactingFolder(_logger, folder.DisplayName)
Dim folderSkipList As String() = If(folder.SkipList Is Nothing, Nothing, folder.SkipList.ToArray())
compactor = CreateCompactor(folder.Folder, folder.CompressionLevel, folderSkipList)
compactor = CreateCompactor(folder.Folder, folder.CompressionLevel, compactorAnalyser, folderSkipList)
If compactor Is Nothing Then Return False

'Pause can arrive after the background run starts but before this folder's
'native compactor exists. Publish the compactor and inherit the current pause
'state atomically so a newly-created compactor cannot run while the user is active.
SyncLock _compactorLock
_compactor = compactor
If isCompactingPaused Then compactor.Pause()
End SyncLock

Dim compactingTask = compactor.RunAsync(Nothing)
Expand All@@ -107,11 +117,11 @@ Public Class BackgroundCompactor
compactor.Cancel()
End If

Dim waitResult = Await WaitForCompactorAsync(compactor, compactingTask, folder, runCancellation.Token)
Dim waitResult = Await WaitForCompactorAsync(compactor, compactorAnalyser, compactingTask, folder, runCancellation.Token)
If Not waitResult.TaskCompleted Then
'The native operation did not return after cancellation. Its task now owns
'the compactor lifetime and will dispose it when Windows finally returns.
disposeCompactor = False
'the compactor/analyser lifetime and will dispose them when Windows finally returns.
disposeResources = False
Return False
End If

Expand DownExpand Up@@ -149,8 +159,9 @@ Public Class BackgroundCompactor
End If
End SyncLock

If disposeCompactor Then
If disposeResources Then
compactor?.Dispose()
compactorAnalyser?.Dispose()
End If
End Try
Next
Expand All@@ -161,8 +172,8 @@ Public Class BackgroundCompactor
Trace.WriteLine("Compacting cancelled by user.")
Return False
Finally
'Each folder task owns its compactor lifetime. A task detached after a stuck
'native call disposes its compactor only after that task actually exits.
'Each folder task owns its compactor/analyser lifetime. A task detached after a stuck
'native call disposes both only after that task actually exits.
isCompacting = False
isCompactingPaused = False
IsCompactorActive = False
Expand All@@ -177,14 +188,17 @@ Public Class BackgroundCompactor
runCancellation.Dispose()

Try
currentProcess.PriorityClass = ProcessPriorityClass.Normal
currentProcess.PriorityClass = originalPriority
Catch ex As Exception
_logger.LogDebug(ex, "Unable to restore CompactGUI process priority.")
Finally
currentProcess.Dispose()
End Try
End Try
End Function

Private Async Function WaitForCompactorAsync(compactor As Core.Compactor,
compactorAnalyser As Core.Analyser,
compactingTask As Task(Of Boolean),
folder As WatchedFolder,
cancellationToken As CancellationToken) As Task(Of (TaskCompleted As Boolean, Result As Boolean))
Expand All@@ -197,7 +211,7 @@ Public Class BackgroundCompactor
End If

If cancellationToken.IsCancellationRequested Then
Return Await StopOrDetachCompactorAsync(compactor, compactingTask, folder, "user cancellation")
Return Await StopOrDetachCompactorAsync(compactor, compactorAnalyser, compactingTask, folder, "user cancellation")
End If

Await Task.Delay(WatchdogPollInterval)
Expand All@@ -207,7 +221,7 @@ Public Class BackgroundCompactor
End If

If cancellationToken.IsCancellationRequested Then
Return Await StopOrDetachCompactorAsync(compactor, compactingTask, folder, "user cancellation")
Return Await StopOrDetachCompactorAsync(compactor, compactorAnalyser, compactingTask, folder, "user cancellation")
End If

'A background run can legitimately remain paused while the user is active.
Expand All@@ -234,12 +248,13 @@ Public Class BackgroundCompactor
compactor.CurrentPhase,
If(compactor.CurrentFile, "<none>"))

Return Await StopOrDetachCompactorAsync(compactor, compactingTask, folder, "watchdog timeout")
Return Await StopOrDetachCompactorAsync(compactor, compactorAnalyser, compactingTask, folder, "watchdog timeout")
End If
Loop
End Function

Private Async Function StopOrDetachCompactorAsync(compactor As Core.Compactor,
compactorAnalyser As Core.Analyser,
compactingTask As Task(Of Boolean),
folder As WatchedFolder,
reason As String) As Task(Of (TaskCompleted As Boolean, Result As Boolean))
Expand All@@ -262,13 +277,14 @@ Public Class BackgroundCompactor
compactor.CurrentPhase,
If(compactor.CurrentFile, "<none>"))

RegisterDetachedCompaction(folder.Folder, folder.DisplayName, compactor, compactingTask)
RegisterDetachedCompaction(folder.Folder, folder.DisplayName, compactor, compactorAnalyser, compactingTask)
Return (TaskCompleted:=False, Result:=False)
End Function

Private Sub RegisterDetachedCompaction(folderPath As String,
displayName As String,
compactor As Core.Compactor,
compactorAnalyser As Core.Analyser,
compactingTask As Task(Of Boolean))
If Not _detachedCompactions.TryAdd(folderPath, compactingTask) Then Return

Expand All@@ -282,6 +298,7 @@ Public Class BackgroundCompactor
End If
Finally
compactor.Dispose()
compactorAnalyser?.Dispose()
Dim removedTask As Task(Of Boolean) = Nothing
_detachedCompactions.TryRemove(folderPath, removedTask)
End Try
Expand Down
12 changes: 7 additions & 5 deletions CompactGUI.Watcher/Watcher.vb
Original file line numberDiff line numberDiff line change
Expand Up@@ -20,7 +20,7 @@ Imports Microsoft.Win32
Imports Microsoft.Win32.Registry


Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipient(Of PropertyChangedMessage(Of Boolean))
Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipient(Of PropertyChangedMessage(Of Boolean)), IRecipient(Of PropertyChangedMessage(Of BackgroundMode))

Private ReadOnly _DataFolder As IO.DirectoryInfo
Private ReadOnly _parseWatchersSemaphore As New SemaphoreSlim(1, 1)
Expand DownExpand Up@@ -533,13 +533,15 @@ Partial Public Class Watcher : Inherits ObservableRecipient : Implements IRecipi
End Function

Public Sub Receive(message As PropertyChangedMessage(Of Boolean)) Implements IRecipient(Of PropertyChangedMessage(Of Boolean)).Receive
If (message.Sender.GetType() IsNot GetType(Settings)) Then Return
If message.Sender.GetType() IsNot GetType(Settings) Then Return

If message.PropertyName = NameOf(Settings.EnableBackgroundWatcher) Then : IsWatchingEnabled = message.NewValue
ElseIf message.PropertyName = NameOf(Settings.BackgroundModeSelection) Then : IsBackgroundCompactingEnabled = (CType(message.NewValue, BackgroundMode) = BackgroundMode.IdleOnly)
End If
If message.PropertyName = NameOf(Settings.EnableBackgroundWatcher) Then IsWatchingEnabled = message.NewValue
End Sub

Public Sub Receive(message As PropertyChangedMessage(Of BackgroundMode)) Implements IRecipient(Of PropertyChangedMessage(Of BackgroundMode)).Receive
If message.Sender.GetType() IsNot GetType(Settings) Then Return

If message.PropertyName = NameOf(Settings.BackgroundModeSelection) Then IsBackgroundCompactingEnabled = message.NewValue = BackgroundMode.IdleOnly
End Sub


Expand Down
12 changes: 8 additions & 4 deletions CompactGUI/Models/CompressableFolders/CompressableFolder.vb
Original file line numberDiff line numberDiff line change
Expand Up@@ -123,12 +123,16 @@ Public MustInherit Class CompressableFolder : Inherits ObservableObject : Implem

Public Sub Dispose() Implements IDisposable.Dispose
Compressor?.Dispose()
Analyser?.Dispose()
Compressor = Nothing

AnalysisResults?.Clear()
PoorlyCompressedFiles?.Clear()
WikiPoorlyCompressedFiles?.Clear()
Analyser?.Dispose()
Analyser = Nothing

AnalysisResults = New ObservableCollection(Of AnalysedFileDetails)()
PoorlyCompressedFiles = Nothing
WikiPoorlyCompressedFiles = New List(Of String)()
WikiCompressionResults = Nothing
FolderBGImage = Nothing

GC.SuppressFinalize(Me)
End Sub
Expand Down
Loading
Loading