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
9 changes: 7 additions & 2 deletions src/Couchbase.Lite.Shared/API/Sync/Replicator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -674,8 +674,8 @@ private void OnDocEndedWithConflict(List<ReplicatedDocument> replications)

t.ContinueWith(task =>
{
_conflictTasks.TryRemove(task, out var dummy);
}, cancelToken);
_conflictTasks.TryRemove(task, out _);
}, CancellationToken.None);

_conflictTasks.TryAdd(t, 0);
}
Expand Down Expand Up @@ -781,6 +781,11 @@ private void WaitPendingConflictTasks(C4ReplicatorStatus status)
Task.WaitAll(array);
} catch (TaskCanceledException) {
WriteLog.To.Sync.I(Tag, "Conflict tasks were cancelled");
} catch (AggregateException e) when (e.InnerException is TaskCanceledException) {
// This happens on iOS on .NET 10 at least
WriteLog.To.Sync.I(Tag, "Conflict tasks were cancelled");
} catch (Exception e) {
WriteLog.To.Sync.W(Tag, "Exception waiting for conflict tasks", e);
}
}

Expand Down
57 changes: 46 additions & 11 deletions src/Couchbase.Lite.Shared/Support/iOS/iOSReachability.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,6 @@
using Couchbase.Lite.Internal.Logging;
using Couchbase.Lite.Sync;

using Foundation;

using Network;

namespace Couchbase.Lite.Support;
Expand Down Expand Up @@ -101,20 +99,57 @@ private void ValidateHostReachability(NetworkReachabilityFlags baseFlags)
return;
}

using var session = NSUrlSession.SharedSession;
var request = new NSUrlRequest(new NSUrl(Url.AbsoluteUri));

var task = session.CreateDataTask(request, (_, response, error) =>
var parameters = NWParameters.CreateTcp();
using var endpoint = NWEndpoint.Create(Url.Host, Url.Port.ToString());
if (endpoint == null) {
WriteLog.To.Sync.E(Tag, $"{Url} is not parseable for reachability check");
return;
}

var connection = new NWConnection(endpoint, parameters);
var flags = baseFlags;
var completed = false; // prevent re-entrancy / duplicate notifications after Cancel

connection.SetQueue(_queue);
connection.SetStateChangeHandler((state, error) =>
{
var flags = baseFlags;
if (error.Code != 0 || response is NSHttpUrlResponse { StatusCode: >= 400 }) {
flags &= ~NetworkReachabilityFlags.Reachable;
// Ignore any subsequent callbacks once we've made a reachability decision
if (completed) {
WriteLog.To.Sync.V(Tag, $"Ignoring state after completion: {state}");
return;
}

if (error != null) {
WriteLog.To.Sync.W(Tag, $"Connection error to {Url}: {error}");
}

NotifyFlagsChanged(flags);
switch (state) {
case NWConnectionState.Ready:
flags |= NetworkReachabilityFlags.Reachable;
NotifyFlagsChanged(flags);
completed = true;
// Replace handler with no-op before cancel to avoid processing Cancelled state
connection.SetStateChangeHandler((_, _) => { });
connection.Cancel();
break;
case NWConnectionState.Failed:
flags &= ~NetworkReachabilityFlags.Reachable;
NotifyFlagsChanged(flags);
completed = true;
connection.SetStateChangeHandler((_, _) => { });
connection.Cancel();
break;
case NWConnectionState.Cancelled:
case NWConnectionState.Invalid:
case NWConnectionState.Waiting:
case NWConnectionState.Preparing:
default:
WriteLog.To.Sync.V(Tag, $"Ignoring connection state: {state}");
break;
}
});

task.Resume();
connection.Start();
}

public void Start()
Expand Down
Loading