// Must be called from within the ThreadSafety private void StatusChangedCallback(C4ReplicatorStatus status) { if (_disposed) { return; } if (status.level == C4ReplicatorActivityLevel.Stopped) { if (HandleError(status.error)) { status.level = C4ReplicatorActivityLevel.Offline; } } else if (status.level > C4ReplicatorActivityLevel.Connecting) { _retryCount = 0; _reachability?.Stop(); _reachability = null; } UpdateStateProperties(status); if (status.level == C4ReplicatorActivityLevel.Stopped) { ClearRepl(); Config.Database.ActiveReplications.Remove(this); } try { _statusChanged.Fire(this, new ReplicatorStatusChangedEventArgs(Status)); } catch (Exception e) { Log.To.Sync.W(Tag, "Exception during StatusChanged callback", e); } }
private static void StatusChangedCallback(C4Replicator *repl, C4ReplicatorStatus status, void *context) { var replicator = GCHandle.FromIntPtr((IntPtr)context).Target as Replicator; replicator?.DispatchQueue.DispatchSync(() => { replicator.StatusChangedCallback(status); }); }
private static void StatusChangedCallback(C4ReplicatorStatus status, object context) { var repl = context as Replicator; repl?._threadSafetyQueue.DispatchSync(() => { repl.StatusChangedCallback(status); }); }
private static void StatusChangedCallback(C4Replicator *repl, C4ReplicatorStatus status, void *context) { var replicator = GCHandle.FromIntPtr((IntPtr)context).Target as Replicator; if (replicator == null) { return; } replicator.WaitPendingConflictTasks(status); replicator.DispatchQueue.DispatchSync(() => { replicator.StatusChangedCallback(status); }); }
private void UpdateStateProperties(C4ReplicatorStatus state) { Exception error = null; if (state.error.code > 0) { error = CouchbaseException.Create(state.error); } _rawStatus = state; var level = (ReplicatorActivityLevel)state.level; var progress = new ReplicatorProgress(state.progress.unitsCompleted, state.progress.unitsTotal); Status = new ReplicatorStatus(level, progress, error); WriteLog.To.Sync.I(Tag, $"{this} is {state.level}, progress {state.progress.unitsCompleted}/{state.progress.unitsTotal}"); }
private void WaitPendingConflictTasks(C4ReplicatorStatus status) { if (status.error.code == 0 && status.error.domain == 0) { return; } if (status.level == C4ReplicatorActivityLevel.Stopped || status.level == C4ReplicatorActivityLevel.Idle) { var array = _conflictTasks?.Keys?.ToArray(); if (array != null) { Task.WaitAll(array); } } }
/// <summary> /// Starts the replication with an option to reset the checkpoint. /// </summary> /// <param name="reset">Resets the local checkpoint of the replicator, meaning that it will read all changes since the beginning /// of time from the remote database. /// </param> public void Start(bool reset) { var status = default(C4ReplicatorStatus); DispatchQueue.DispatchSync(() => { if (_disposed) { throw new ObjectDisposedException(CouchbaseLiteErrorMessage.ReplicatorDisposed); } var err = SetupC4Replicator(); if (err.code > 0) { WriteLog.To.Sync.E(Tag, $"Setup replicator {this} failed."); } if (_repl != null) { status = Native.c4repl_getStatus(_repl); if (status.level == C4ReplicatorActivityLevel.Stopped || status.level == C4ReplicatorActivityLevel.Stopping || status.level == C4ReplicatorActivityLevel.Offline) { ServerCertificate = null; WriteLog.To.Sync.I(Tag, $"{this}: Starting"); Native.c4repl_start(_repl, Config.Options.Reset || reset); Config.Options.Reset = false; Config.Database.AddActiveStoppable(this); status = Native.c4repl_getStatus(_repl); } } else { status = new C4ReplicatorStatus { error = err, level = C4ReplicatorActivityLevel.Stopped, progress = new C4Progress() }; } }); UpdateStateProperties(status); DispatchQueue.DispatchSync(() => StatusChangedCallback(status)); }
private void WaitPendingConflictTasks(C4ReplicatorStatus status) { if (status.error.code == 0 && status.error.domain == 0) { return; } bool transient; if (!IsPermanentError(status.error, out transient)) { return; } if (status.level == C4ReplicatorActivityLevel.Stopped) { var array = _conflictTasks?.Keys?.ToArray(); if (array != null) { Task.WaitAll(array); } } }
// Must be called from within the ThreadSafety private void StatusChangedCallback(C4ReplicatorStatus status) { if (_disposed) { return; } // idle or busy if ((status.level > C4ReplicatorActivityLevel.Connecting && status.level != C4ReplicatorActivityLevel.Stopping) && status.error.code == 0) { StopReachabilityObserver(); } UpdateStateProperties(status); // offline if (status.level == C4ReplicatorActivityLevel.Offline) { StartReachabilityObserver(); } // stopped if (status.level == C4ReplicatorActivityLevel.Stopped) { StopReachabilityObserver(); Stopped(); } try { _statusChanged.Fire(this, new ReplicatorStatusChangedEventArgs(Status)); } catch (Exception e) { WriteLog.To.Sync.W(Tag, "Exception during StatusChanged callback", e); } }
// Must be called from within the ThreadSafety private void StartInternal() { _desc = ToString(); // Cache this; it may be called a lot when logging // Target: var addr = new C4Address(); var scheme = new C4String(); var host = new C4String(); var path = new C4String(); Database otherDB = null; var remoteUrl = Config.RemoteUrl; string dbNameStr = null; if (remoteUrl != null) { var pathStr = String.Concat(remoteUrl.Segments.Take(remoteUrl.Segments.Length - 1)); dbNameStr = remoteUrl.Segments.Last().TrimEnd('/'); scheme = new C4String(remoteUrl.Scheme); host = new C4String(remoteUrl.Host); path = new C4String(pathStr); addr.scheme = scheme.AsC4Slice(); addr.hostname = host.AsC4Slice(); addr.port = (ushort)remoteUrl.Port; addr.path = path.AsC4Slice(); } else { otherDB = Config.OtherDB; } var options = Config.Options; var userInfo = remoteUrl?.UserInfo?.Split(':'); if (userInfo?.Length == 2) { throw new ArgumentException( "Embedded credentials in a URL (username:password@url) are not allowed; use the BasicAuthenticator class instead"); } Config.Authenticator?.Authenticate(options); options.Build(); var push = Config.ReplicatorType.HasFlag(ReplicatorType.Push); var pull = Config.ReplicatorType.HasFlag(ReplicatorType.Pull); var continuous = Config.Continuous; // Clear the reset flag, it is a one-time thing Config.Options.Reset = false; var socketFactory = Config.SocketFactory; socketFactory.context = GCHandle.ToIntPtr(GCHandle.Alloc(this)).ToPointer(); _nativeParams = new ReplicatorParameters(options) { Push = Mkmode(push, continuous), Pull = Mkmode(pull, continuous), Context = this, OnDocumentError = OnDocError, OnStatusChanged = StatusChangedCallback, SocketFactory = &socketFactory }; var err = new C4Error(); var status = default(C4ReplicatorStatus); _stopping = false; _databaseThreadSafety.DoLocked(() => { C4Error localErr; _repl = Native.c4repl_new(Config.Database.c4db, addr, dbNameStr, otherDB != null ? otherDB.c4db : null, _nativeParams.C4Params, &localErr); err = localErr; if (_repl != null) { status = Native.c4repl_getStatus(_repl); Config.Database.ActiveReplications.Add(this); } else { status = new C4ReplicatorStatus { error = err, level = C4ReplicatorActivityLevel.Stopped, progress = new C4Progress() }; } }); scheme.Dispose(); path.Dispose(); host.Dispose(); UpdateStateProperties(status); DispatchQueue.DispatchSync(() => StatusChangedCallback(status)); }