From 239102170e9d0dfd8e61dc9449ab178767062246 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Thu, 26 Mar 2026 10:38:26 +0000 Subject: [PATCH 1/3] Deps: Bump FluentFTP from 53.0.2 to 54.0.2 --- updated-dependencies: - dependency-name: FluentFTP dependency-version: 54.0.2 dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- src/SharpSync/SharpSync.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/SharpSync/SharpSync.csproj b/src/SharpSync/SharpSync.csproj index 7ac6cdc..cbbd6ac 100644 --- a/src/SharpSync/SharpSync.csproj +++ b/src/SharpSync/SharpSync.csproj @@ -52,7 +52,7 @@ - + From 90ce86c383c666b29f6660dee6767942730e18f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Polykanine?= Date: Thu, 26 Mar 2026 12:39:20 +0100 Subject: [PATCH 2/3] Attempt to fix Ubuntu on CI --- .github/workflows/dotnet.yml | 2 ++ docker-compose.test.yml | 4 +--- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/dotnet.yml b/.github/workflows/dotnet.yml index a98a0e1..07f9959 100644 --- a/.github/workflows/dotnet.yml +++ b/.github/workflows/dotnet.yml @@ -36,6 +36,8 @@ jobs: run: | echo "=== Starting test services with docker-compose ===" docker compose -f docker-compose.test.yml up -d + env: + LOCALSTACK_AUTH_TOKEN: ${{ secrets.LOCALSTACK_AUTH_TOKEN }} - name: Wait for services to be ready if: matrix.os == 'ubuntu-latest' diff --git a/docker-compose.test.yml b/docker-compose.test.yml index 08a0bf7..841c9fb 100644 --- a/docker-compose.test.yml +++ b/docker-compose.test.yml @@ -36,9 +36,7 @@ services: ports: - "4566:4566" environment: - SERVICES: s3 - DEBUG: 0 - EDGE_PORT: 4566 + LOCALSTACK_AUTH_TOKEN: ${LOCALSTACK_AUTH_TOKEN:-} healthcheck: test: ["CMD", "curl", "-f", "http://localhost:4566/_localstack/health"] interval: 15s From c8464423ab04ecd80abfd5c6b4202981d91d98b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Andr=C3=A9=20Polykanine?= Date: Thu, 26 Mar 2026 13:00:00 +0100 Subject: [PATCH 3/3] Fix race conditions in sync engine --- src/SharpSync/Sync/SyncEngine.cs | 3 ++ src/SharpSync/Sync/ThreadSafeSyncResult.cs | 33 +++++++++++----------- 2 files changed, 20 insertions(+), 16 deletions(-) diff --git a/src/SharpSync/Sync/SyncEngine.cs b/src/SharpSync/Sync/SyncEngine.cs index b067f65..33d1e8b 100644 --- a/src/SharpSync/Sync/SyncEngine.cs +++ b/src/SharpSync/Sync/SyncEngine.cs @@ -752,6 +752,9 @@ private async Task ProcessChangesAsync(ChangeSet changes, SyncOptions? options, await ProcessPhase1_DirectoriesAndSmallFilesAsync(actionGroups, threadSafeResult, progressCounter, totalChanges, cancellationToken).ConfigureAwait(false); await ProcessPhase2_LargeFilesAsync(actionGroups, threadSafeResult, progressCounter, totalChanges, cancellationToken).ConfigureAwait(false); await ProcessPhase3_DeletesAndConflictsAsync(actionGroups, threadSafeResult, progressCounter, totalChanges, cancellationToken).ConfigureAwait(false); + + // Write final atomic counter values to the result + threadSafeResult.Flush(); } /// diff --git a/src/SharpSync/Sync/ThreadSafeSyncResult.cs b/src/SharpSync/Sync/ThreadSafeSyncResult.cs index 23bc585..d477f45 100644 --- a/src/SharpSync/Sync/ThreadSafeSyncResult.cs +++ b/src/SharpSync/Sync/ThreadSafeSyncResult.cs @@ -3,7 +3,9 @@ namespace Oire.SharpSync.Sync; /// -/// Thread-safe wrapper for SyncResult counters +/// Thread-safe wrapper for SyncResult counters. +/// Call after all parallel processing completes +/// to write the final values to the underlying . /// internal sealed class ThreadSafeSyncResult { private readonly SyncResult _result; @@ -16,23 +18,22 @@ public ThreadSafeSyncResult(SyncResult result) { _result = result; } - public void IncrementFilesSynchronized() { - var newValue = Interlocked.Increment(ref _filesSynchronized); - _result.FilesSynchronized = newValue; - } + public void IncrementFilesSynchronized() => Interlocked.Increment(ref _filesSynchronized); - public void IncrementFilesSkipped() { - var newValue = Interlocked.Increment(ref _filesSkipped); - _result.FilesSkipped = newValue; - } + public void IncrementFilesSkipped() => Interlocked.Increment(ref _filesSkipped); - public void IncrementFilesConflicted() { - var newValue = Interlocked.Increment(ref _filesConflicted); - _result.FilesConflicted = newValue; - } + public void IncrementFilesConflicted() => Interlocked.Increment(ref _filesConflicted); + + public void IncrementFilesDeleted() => Interlocked.Increment(ref _filesDeleted); - public void IncrementFilesDeleted() { - var newValue = Interlocked.Increment(ref _filesDeleted); - _result.FilesDeleted = newValue; + /// + /// Writes the final atomic counter values to the underlying . + /// Must be called after all parallel processing has completed. + /// + public void Flush() { + _result.FilesSynchronized = Interlocked.Read(ref _filesSynchronized); + _result.FilesSkipped = Interlocked.Read(ref _filesSkipped); + _result.FilesConflicted = Interlocked.Read(ref _filesConflicted); + _result.FilesDeleted = Interlocked.Read(ref _filesDeleted); } }