From 155c61acbbaf32d5c4996b7b8b53c6204360fb8c Mon Sep 17 00:00:00 2001 From: Matthieu Gallien Date: Fri, 30 Aug 2024 16:51:09 +0200 Subject: [PATCH] let cancel sync and finish sync lambda be reusable methods will enable implementation of other ways to interrupt sync after discovery to get user feedback Signed-off-by: Matthieu Gallien --- src/libsync/syncengine.cpp | 246 ++++++++++++++++++++----------------- src/libsync/syncengine.h | 7 ++ 2 files changed, 138 insertions(+), 115 deletions(-) diff --git a/src/libsync/syncengine.cpp b/src/libsync/syncengine.cpp index 21d6b0c8b..eb114f553 100644 --- a/src/libsync/syncengine.cpp +++ b/src/libsync/syncengine.cpp @@ -801,102 +801,6 @@ void SyncEngine::slotDiscoveryFinished() _progressInfo->_status = ProgressInfo::Reconcile; emit transmissionProgress(*_progressInfo); - // qCInfo(lcEngine) << "Permissions of the root folder: " << _csync_ctx->remote.root_perms.toString(); - auto finish = [this]{ - auto databaseFingerprint = _journal->dataFingerprint(); - // If databaseFingerprint is empty, this means that there was no information in the database - // (for example, upgrading from a previous version, or first sync, or server not supporting fingerprint) - if (!databaseFingerprint.isEmpty() && _discoveryPhase - && _discoveryPhase->_dataFingerprint != databaseFingerprint) { - qCInfo(lcEngine) << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryPhase->_dataFingerprint; - restoreOldFiles(_syncItems); - } - - if (_discoveryPhase->_anotherSyncNeeded && !_discoveryPhase->_filesNeedingScheduledSync.empty()) { - slotScheduleFilesDelayedSync(); - } else if (_discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) { - _anotherSyncNeeded = ImmediateFollowUp; - } - - if (!_discoveryPhase->_filesUnscheduleSync.empty()) { - slotUnscheduleFilesDelayedSync(); - } - - if (_discoveryPhase->_hasDownloadRemovedItems && _discoveryPhase->_hasUploadErrorItems) { - for (const auto &item : qAsConst(_syncItems)) { - if (item->_instruction == CSYNC_INSTRUCTION_ERROR && item->_direction == SyncFileItem::Up) { - item->_instruction = CSYNC_INSTRUCTION_IGNORE; - } - } - _anotherSyncNeeded = ImmediateFollowUp; - } - - Q_ASSERT(std::is_sorted(_syncItems.begin(), _syncItems.end())); - - qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate) #################################################### " << _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate)")) << "ms"; - - _localDiscoveryPaths.clear(); - - // To announce the beginning of the sync - emit aboutToPropagate(_syncItems); - - qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate OK) #################################################### "<< _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate OK)")) << "ms"; - - // it's important to do this before ProgressInfo::start(), to announce start of new sync - _progressInfo->_status = ProgressInfo::Propagation; - emit transmissionProgress(*_progressInfo); - _progressInfo->startEstimateUpdates(); - - // post update phase script: allow to tweak stuff by a custom script in debug mode. - if (!qEnvironmentVariableIsEmpty("OWNCLOUD_POST_UPDATE_SCRIPT")) { - #ifndef NDEBUG - const QString script = qEnvironmentVariable("OWNCLOUD_POST_UPDATE_SCRIPT"); - - qCDebug(lcEngine) << "Post Update Script: " << script; - auto scriptArgs = script.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); - if (scriptArgs.size() > 0) { - const auto scriptExecutable = scriptArgs.takeFirst(); - QProcess::execute(scriptExecutable, scriptArgs); - } -#else - qCWarning(lcEngine) << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG"; - #endif - } - - // do a database commit - _journal->commit(QStringLiteral("post treewalk")); - - _propagator = QSharedPointer( - new OwncloudPropagator(_account, _localPath, _remotePath, _journal, _bulkUploadBlackList)); - _propagator->setSyncOptions(_syncOptions); - connect(_propagator.data(), &OwncloudPropagator::itemCompleted, - this, &SyncEngine::slotItemCompleted); - connect(_propagator.data(), &OwncloudPropagator::progress, - this, &SyncEngine::slotProgress); - connect(_propagator.data(), &OwncloudPropagator::finished, this, &SyncEngine::slotPropagationFinished, Qt::QueuedConnection); - connect(_propagator.data(), &OwncloudPropagator::seenLockedFile, this, &SyncEngine::seenLockedFile); - connect(_propagator.data(), &OwncloudPropagator::touchedFile, this, &SyncEngine::slotAddTouchedFile); - connect(_propagator.data(), &OwncloudPropagator::insufficientLocalStorage, this, &SyncEngine::slotInsufficientLocalStorage); - connect(_propagator.data(), &OwncloudPropagator::insufficientRemoteStorage, this, &SyncEngine::slotInsufficientRemoteStorage); - connect(_propagator.data(), &OwncloudPropagator::newItem, this, &SyncEngine::slotNewItem); - - // apply the network limits to the propagator - setNetworkLimits(_uploadLimit, _downloadLimit); - - deleteStaleDownloadInfos(_syncItems); - deleteStaleUploadInfos(_syncItems); - deleteStaleErrorBlacklistEntries(_syncItems); - _journal->commit(QStringLiteral("post stale entry removal")); - - // Emit the started signal only after the propagator has been set up. - if (_needsUpdate) - Q_EMIT started(); - - _propagator->start(std::move(_syncItems)); - - qCInfo(lcEngine) << "#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QStringLiteral("Post-Reconcile Finished")) << "ms"; - }; - const auto displayDialog = ConfigFile().promptDeleteFiles() && !_syncOptions.isCmd(); if (!_hasNoneFiles && _hasRemoveFile && displayDialog) { qCInfo(lcEngine) << "All the files are going to be changed, asking the user"; @@ -907,27 +811,14 @@ void SyncEngine::slotDiscoveryFinished() } } - QPointer guard = new QObject(); - QPointer self = this; - auto callback = [this, self, finish, guard](bool cancel) -> void { - // use a guard to ensure its only called once... - // qpointer to self to ensure we still exist - if (!guard || !self) { - return; - } - guard->deleteLater(); - if (cancel) { - qCInfo(lcEngine) << "User aborted sync"; - finalize(false); - return; - } else { - finish(); - } - }; - emit aboutToRemoveAllFiles(side >= 0 ? SyncFileItem::Down : SyncFileItem::Up, callback); + promptUserBeforePropagation([this, side](auto &&callback){ + emit aboutToRemoveAllFiles(side >= 0 ? SyncFileItem::Down : SyncFileItem::Up, callback); + }); return; } - finish(); + + + finishSync(); } void SyncEngine::slotCleanPollsJobAborted(const QString &error, const ErrorCategory errorCategory) @@ -1104,6 +995,131 @@ void SyncEngine::restoreOldFiles(SyncFileItemVector &syncItems) } } +void SyncEngine::cancelSyncOrContinue(bool cancel) +{ + if (cancel) { + qCInfo(lcEngine) << "User aborted sync"; + finalize(false); + } else { + finishSync(); + } +} + +void SyncEngine::finishSync() +{ + auto databaseFingerprint = _journal->dataFingerprint(); + // If databaseFingerprint is empty, this means that there was no information in the database + // (for example, upgrading from a previous version, or first sync, or server not supporting fingerprint) + if (!databaseFingerprint.isEmpty() && _discoveryPhase + && _discoveryPhase->_dataFingerprint != databaseFingerprint) { + qCInfo(lcEngine) << "data fingerprint changed, assume restore from backup" << databaseFingerprint << _discoveryPhase->_dataFingerprint; + restoreOldFiles(_syncItems); + } + + if (_discoveryPhase && _discoveryPhase->_anotherSyncNeeded && !_discoveryPhase->_filesNeedingScheduledSync.empty()) { + slotScheduleFilesDelayedSync(); + } else if (_discoveryPhase && _discoveryPhase->_anotherSyncNeeded && _anotherSyncNeeded == NoFollowUpSync) { + _anotherSyncNeeded = ImmediateFollowUp; + } + + if (_discoveryPhase && !_discoveryPhase->_filesUnscheduleSync.empty()) { + slotUnscheduleFilesDelayedSync(); + } + + if (_discoveryPhase && _discoveryPhase->_hasDownloadRemovedItems && _discoveryPhase->_hasUploadErrorItems) { + for (const auto &item : qAsConst(_syncItems)) { + if (item->_instruction == CSYNC_INSTRUCTION_ERROR && item->_direction == SyncFileItem::Up) { + // item->_instruction = CSYNC_INSTRUCTION_IGNORE; + } + } + _anotherSyncNeeded = ImmediateFollowUp; + } + + Q_ASSERT(std::is_sorted(_syncItems.begin(), _syncItems.end())); + + qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate) #################################################### " << _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate)")) << "ms"; + + _localDiscoveryPaths.clear(); + + // To announce the beginning of the sync + emit aboutToPropagate(_syncItems); + + qCInfo(lcEngine) << "#### Reconcile (aboutToPropagate OK) #################################################### "<< _stopWatch.addLapTime(QStringLiteral("Reconcile (aboutToPropagate OK)")) << "ms"; + + // it's important to do this before ProgressInfo::start(), to announce start of new sync + _progressInfo->_status = ProgressInfo::Propagation; + emit transmissionProgress(*_progressInfo); + _progressInfo->startEstimateUpdates(); + + // post update phase script: allow to tweak stuff by a custom script in debug mode. + if (!qEnvironmentVariableIsEmpty("OWNCLOUD_POST_UPDATE_SCRIPT")) { +#ifndef NDEBUG + const QString script = qEnvironmentVariable("OWNCLOUD_POST_UPDATE_SCRIPT"); + + qCDebug(lcEngine) << "Post Update Script: " << script; + auto scriptArgs = script.split(QRegularExpression("\\s+"), Qt::SkipEmptyParts); + if (scriptArgs.size() > 0) { + const auto scriptExecutable = scriptArgs.takeFirst(); + QProcess::execute(scriptExecutable, scriptArgs); + } +#else + qCWarning(lcEngine) << "**** Attention: POST_UPDATE_SCRIPT installed, but not executed because compiled with NDEBUG"; +#endif + } + + // do a database commit + _journal->commit(QStringLiteral("post treewalk")); + + _propagator = QSharedPointer( + new OwncloudPropagator(_account, _localPath, _remotePath, _journal, _bulkUploadBlackList)); + _propagator->setSyncOptions(_syncOptions); + connect(_propagator.data(), &OwncloudPropagator::itemCompleted, + this, &SyncEngine::slotItemCompleted); + connect(_propagator.data(), &OwncloudPropagator::progress, + this, &SyncEngine::slotProgress); + connect(_propagator.data(), &OwncloudPropagator::finished, this, &SyncEngine::slotPropagationFinished, Qt::QueuedConnection); + connect(_propagator.data(), &OwncloudPropagator::seenLockedFile, this, &SyncEngine::seenLockedFile); + connect(_propagator.data(), &OwncloudPropagator::touchedFile, this, &SyncEngine::slotAddTouchedFile); + connect(_propagator.data(), &OwncloudPropagator::insufficientLocalStorage, this, &SyncEngine::slotInsufficientLocalStorage); + connect(_propagator.data(), &OwncloudPropagator::insufficientRemoteStorage, this, &SyncEngine::slotInsufficientRemoteStorage); + connect(_propagator.data(), &OwncloudPropagator::newItem, this, &SyncEngine::slotNewItem); + + // apply the network limits to the propagator + setNetworkLimits(_uploadLimit, _downloadLimit); + + deleteStaleDownloadInfos(_syncItems); + deleteStaleUploadInfos(_syncItems); + deleteStaleErrorBlacklistEntries(_syncItems); + _journal->commit(QStringLiteral("post stale entry removal")); + + // Emit the started signal only after the propagator has been set up. + if (_needsUpdate) + Q_EMIT started(); + + _propagator->start(std::move(_syncItems)); + + qCInfo(lcEngine) << "#### Post-Reconcile end #################################################### " << _stopWatch.addLapTime(QStringLiteral("Post-Reconcile Finished")) << "ms"; +} + +template +void SyncEngine::promptUserBeforePropagation(T &&lambda) +{ + QPointer guard = new QObject(); + QPointer self = this; + auto callback = [this, self, guard](bool cancel) -> void { + // use a guard to ensure its only called once... + // qpointer to self to ensure we still exist + if (!guard || !self) { + return; + } + guard->deleteLater(); + + cancelSyncOrContinue(cancel); + }; + + lambda(callback); +} + void SyncEngine::slotAddTouchedFile(const QString &fn) { QElapsedTimer now; diff --git a/src/libsync/syncengine.h b/src/libsync/syncengine.h index 2887c4bc1..278c5569d 100644 --- a/src/libsync/syncengine.h +++ b/src/libsync/syncengine.h @@ -361,6 +361,13 @@ private: */ void restoreOldFiles(SyncFileItemVector &syncItems); + void cancelSyncOrContinue(bool cancel); + + void finishSync(); + + template + void promptUserBeforePropagation(T &&lambda); + // true if there is at least one file which was not changed on the server bool _hasNoneFiles = false;