Skip to content

Commit

Permalink
Merge pull request #9 from nemomobile/cleanup-fix
Browse files Browse the repository at this point in the history
Monitoring expired transfers.
  • Loading branch information
Marko Mattila committed Jul 2, 2013
2 parents feaccb0 + f7740f0 commit 536c46e
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 34 deletions.
55 changes: 55 additions & 0 deletions lib/transferengineclient.cpp
Expand Up @@ -339,3 +339,58 @@ void TransferEngineClient::finishTransfer(int transferId, Status status, const Q
d->m_client->finishTransfer(transferId, static_cast<int>(status), reason);
}

/*!
Private method for QML interface to cancel an ongoing transfer using \a transferId.
*/
void TransferEngineClient::cbCancelTransfer(int transferId)
{
Q_D(TransferEngineClient);
d->m_client->cancelTransfer(transferId);
}

/*!
Private method for QML interface to restart canceled or interrupted transfer using \a transferId.
*/
void TransferEngineClient::cbRestartTransfer(int transferId)
{
Q_D(TransferEngineClient);
d->m_client->restartTransfer(transferId);
}

/*!
Private method for QML interface to clear all canceled or interrupted events.
*/
void TransferEngineClient::clearTransfers()
{
Q_D(TransferEngineClient);
d->m_client->clearTransfers();
}

/*!
Private method for QML interface to enable notifications.
*/
void TransferEngineClient::enableNotifications(bool enable)
{
Q_D(TransferEngineClient);
d->m_client->enableNotifications(enable);
}


/*!
Private method for QML interface.
\returns true if notifications are enabled, otherwise false is returned.
*/
bool TransferEngineClient::notificationsEnabled() const
{
Q_D(const TransferEngineClient);
QDBusPendingReply<bool> reply = d->m_client->notificationsEnabled();
reply.waitForFinished();

if (reply.isError()) {
qWarning() << Q_FUNC_INFO << "failed to get notifications!";
return false;
}

return reply.value();
}
9 changes: 9 additions & 0 deletions lib/transferengineclient.h
Expand Up @@ -81,9 +81,18 @@ class TransferEngineClient : public QObject
void updateTransferProgress(int transferId, qreal progress);
void finishTransfer(int transferId, Status status, const QString &reason = QString());

private:
void cbCancelTransfer(int transferId);
void cbRestartTransfer(int transferId);
void clearTransfers();
void enableNotifications(bool enable);
bool notificationsEnabled() const;

private:
TransferEngineClientPrivate *d_ptr;
Q_DECLARE_PRIVATE(TransferEngineClient)

friend class DeclarativeTransferInterface;
};

#endif // TRANSFERENGINECLIENT_H
2 changes: 1 addition & 1 deletion rpm/transfer-engine-qt5.spec
@@ -1,5 +1,5 @@
Name: nemo-transferengine-qt5
Version: 0.0.17
Version: 0.0.23
Release: 0
Summary: Transfer Engine for uploading media content and tracking transfers.
Group: System Environment/Daemon
Expand Down
133 changes: 103 additions & 30 deletions src/transferengine.cpp
Expand Up @@ -48,7 +48,8 @@
#define SHARE_PLUGINS_PATH "/usr/lib/nemo-transferengine/plugins"
#define CONFIG_PATH "/usr/share/nemo-transferengine/nemo-transfer-engine.conf"
#define FILE_WATCHER_TIMEOUT 5000

#define ACTIVITY_MONITOR_TIMEOUT 1*60*1000 // 1 minute in ms
#define TRANSFER_EXPIRATION_THRESHOLD 3*60 // 3 minutes in seconds

TransferEngineSignalHandler * TransferEngineSignalHandler::instance()
{
Expand All @@ -70,6 +71,68 @@ TransferEngineSignalHandler::TransferEngineSignalHandler()
signal(SIGUSR1, TransferEngineSignalHandler::signalHandler);
}

// ---------------------------

// ClientActivityMonitor runs periodic checks if there are transfers which are expired.
// A transfer can be expired e.g. when a client has been crashed in the middle of Sync,
// Download or Upload operation or the client API isn't used properly.
//
// NOTE: This class only monitors if there are expired transfers and emit signal to indicate
// that it's cleaning time. It is up to Transfer Engine to remoce expired ids from the
// ClientActivityMonitor instance.
ClientActivityMonitor::ClientActivityMonitor(QObject *parent)
: QObject(parent)
, m_timer(new QTimer(this))
{
connect(m_timer, SIGNAL(timeout()), this, SLOT(checkActivity()));
m_timer->start(ACTIVITY_MONITOR_TIMEOUT);
}

ClientActivityMonitor::~ClientActivityMonitor()
{
m_activityMap.clear();
}

void ClientActivityMonitor::newActivity(int transferId)
{
// Update or add a new timestamp
m_activityMap.insert(transferId, QDateTime::currentDateTimeUtc().toTime_t());
}

void ClientActivityMonitor::activityFinished(int transferId)
{
if (!m_activityMap.contains(transferId)) {
qWarning() << Q_FUNC_INFO << "Could not find matching TransferId. This is probably an error!";
return;
}

m_activityMap.remove(transferId);
}

bool ClientActivityMonitor::activeTransfers() const
{
return !m_activityMap.isEmpty();
}

void ClientActivityMonitor::checkActivity()
{
// Check if there are existing transfers which are not yet finished and
// they've been around too long. Notify TransferEngine about these transfers.
QList<int> ids;
quint32 currTime = QDateTime::currentDateTimeUtc().toTime_t();
QMap<int, quint32>::const_iterator i = m_activityMap.constBegin();
while (i != m_activityMap.constEnd()) {
if ((currTime - i.value()) >= TRANSFER_EXPIRATION_THRESHOLD) {
ids << i.key();
}
i++;
}

if (!ids.isEmpty()) {
emit transfersExpired(ids);
}
}


// ----------------------------

Expand All @@ -92,10 +155,14 @@ TransferEnginePrivate::TransferEnginePrivate(TransferEngine *parent):
connect(m_accountManager, SIGNAL(accountUpdated(Accounts::AccountId)), this, SLOT(enabledPluginsCheck()));
connect(m_accountManager, SIGNAL(enabledEvent(Accounts::AccountId)), this, SLOT(enabledPluginsCheck()));

m_transfersInProgressCount = 0;
connect(TransferEngineSignalHandler::instance(), SIGNAL(exitSafely()), this, SLOT(exitSafely()));
// Exit safely stuff if we recieve certain signal or there are no active transfers
Q_Q(TransferEngine);
connect(q, SIGNAL(statusChanged(int,int)), this, SLOT(inProgressTransfersCheck()));
connect(TransferEngineSignalHandler::instance(), SIGNAL(exitSafely()), this, SLOT(exitSafely()));
connect(q, SIGNAL(statusChanged(int,int)), this, SLOT(exitSafely()));

// Monitor expired transfers and cleanup them if required
m_activityMonitor = new ClientActivityMonitor(this);
connect(m_activityMonitor, SIGNAL(transfersExpired(QList<int>)), this, SLOT(cleanupExpiredTransfers(QList<int>)));
}

void TransferEnginePrivate::pluginDirChanged()
Expand All @@ -108,29 +175,12 @@ void TransferEnginePrivate::pluginDirChanged()

void TransferEnginePrivate::exitSafely()
{
if (m_transfersInProgressCount == 0) {
if (!m_activityMonitor->activeTransfers()) {
qDebug() << Q_FUNC_INFO;
qApp->exit();
}
}

void TransferEnginePrivate::inProgressTransfersCheck()
{
int count = 0;
// Just read the statuses from the DB.
QList<TransferDBRecord> records = DbManager::instance()->transfers();
Q_FOREACH(TransferDBRecord record, records) {
if (record.status == TransferEngineData::TransferStarted ||
record.status == TransferEngineData::NotStarted) {
++count;
}
}
m_transfersInProgressCount = count;
if (m_transfersInProgressCount == 0) {
qApp->exit();
}
}

void TransferEnginePrivate::enabledPluginsCheck()
{
Q_Q(TransferEngine);
Expand Down Expand Up @@ -179,19 +229,34 @@ void TransferEnginePrivate::enabledPluginsCheck()
qWarning() << Q_FUNC_INFO << loader.errorString();
}
}
}


void TransferEnginePrivate::cleanupExpiredTransfers(const QList<int> &expiredIds)
{
// Clean up expired items from the database by changing the status to TransferInterrupted. This way
// database doesn't maintain objects with unifinished state and failed items can be cleaned by the
// user manually from the UI.
Q_Q(TransferEngine);
Q_FOREACH(int id, expiredIds) {
if (DbManager::instance()->updateTransferStatus(id, TransferEngineData::TransferInterrupted)) {
m_activityMonitor->activityFinished(id);
emit q->statusChanged(id, TransferEngineData::TransferInterrupted);
}
}
}

void TransferEnginePrivate::recoveryCheck()
{
QList<TransferDBRecord> records = DbManager::instance()->transfers();
// Check all transfer which are not properly finished and mark those as
// interrupted
Q_Q(TransferEngine);
Q_FOREACH(TransferDBRecord record, records) {
if (record.status == TransferEngineData::TransferStarted ||
record.status == TransferEngineData::NotStarted) {
DbManager::instance()->updateTransferStatus(record.transfer_id, TransferEngineData::TransferInterrupted);
if (DbManager::instance()->updateTransferStatus(record.transfer_id, TransferEngineData::TransferInterrupted)) {
emit q->statusChanged(record.transfer_id, TransferEngineData::TransferInterrupted);
}
}
}
}
Expand Down Expand Up @@ -415,6 +480,7 @@ int TransferEnginePrivate::uploadMediaItem(MediaItem *mediaItem,
return key;
}

m_activityMonitor->newActivity(key);
emit q->transfersChanged();
emit q->statusChanged(key, TransferEngineData::NotStarted);

Expand Down Expand Up @@ -485,6 +551,7 @@ void TransferEnginePrivate::uploadItemStatusChanged(MediaTransferInterface::Tran
switch(tStatus) {
case TransferEngineData::TransferStarted:
ok = DbManager::instance()->updateTransferStatus(key, tStatus);
m_activityMonitor->newActivity(key);
break;

case TransferEngineData::TransferInterrupted:
Expand All @@ -502,6 +569,7 @@ void TransferEnginePrivate::uploadItemStatusChanged(MediaTransferInterface::Tran
}
muif->deleteLater();
muif = 0;
m_activityMonitor->activityFinished(key);
} break;

default:
Expand All @@ -528,6 +596,7 @@ void TransferEnginePrivate::updateProgress(qreal progress)
return;
}

m_activityMonitor->newActivity(key);
Q_Q(TransferEngine);
emit q->progressChanged(key, progress);
}
Expand Down Expand Up @@ -603,7 +672,6 @@ void TransferEnginePrivate::callbackCall(int transferId, CallbackMethodType meth
qWarning() << "TransferEnginePrivate::callbackCall: Failed to get callback method name!";
return;
}

remoteInterface.call(methodName, transferId);
}

Expand Down Expand Up @@ -701,15 +769,14 @@ TransferEngine::TransferEngine(QObject *parent) :
qFatal("Could not register object \'/org/nemo/transferengine\'");
}

new TransferEngineAdaptor(this);

// Let's make sure that db is open by creating
// DbManager singleton instance.
DbManager::instance();
Q_D(TransferEngine);
d->recoveryCheck();
d->enabledPluginsCheck();

new TransferEngineAdaptor(this);
}

/*!
Expand Down Expand Up @@ -871,6 +938,7 @@ int TransferEngine::createDownload(const QString &displayName,
mediaItem->setValue(MediaItem::RestartSupported,!restartMethod.isEmpty());

const int key = DbManager::instance()->createTransferEntry(mediaItem);
d->m_activityMonitor->newActivity(key);
d->m_keyTypeCache.insert(key, TransferEngineData::Download);
emit transfersChanged();
emit statusChanged(key, TransferEngineData::NotStarted);
Expand Down Expand Up @@ -917,6 +985,7 @@ int TransferEngine::createSync(const QString &displayName,

const int key = DbManager::instance()->createTransferEntry(mediaItem);
Q_D(TransferEngine);
d->m_activityMonitor->newActivity(key);
d->m_keyTypeCache.insert(key, TransferEngineData::Sync);
emit transfersChanged();
emit statusChanged(key, TransferEngineData::NotStarted);
Expand Down Expand Up @@ -949,6 +1018,7 @@ void TransferEngine::startTransfer(int transferId)
if (status == TransferEngineData::NotStarted ||
status == TransferEngineData::TransferCanceled ||
status == TransferEngineData::TransferInterrupted) {
d->m_activityMonitor->newActivity(transferId);
DbManager::instance()->updateTransferStatus(transferId, TransferEngineData::TransferStarted);
emit statusChanged(transferId, TransferEngineData::TransferStarted);
} else {
Expand Down Expand Up @@ -991,6 +1061,7 @@ void TransferEngine::restartTransfer(int transferId)
connect(muif, SIGNAL(progressUpdated(qreal)),
d, SLOT(updateProgress(qreal)));

d->m_activityMonitor->newActivity(transferId);
d->m_keyTypeCache.insert(transferId, TransferEngineData::Upload);
d->m_plugins.insert(muif, transferId);
muif->start();
Expand All @@ -1004,6 +1075,7 @@ void TransferEngine::restartTransfer(int transferId)
if (status == TransferEngineData::TransferCanceled ||
status == TransferEngineData::TransferInterrupted) {
DbManager::instance()->updateProgress(transferId, 0);
d->m_activityMonitor->newActivity(transferId);
d->callbackCall(transferId, TransferEnginePrivate::RestartCallback);
}
}
Expand Down Expand Up @@ -1045,7 +1117,7 @@ void TransferEngine::finishTransfer(int transferId, int status, const QString &r
transferStatus == TransferEngineData::TransferCanceled ||
transferStatus == TransferEngineData::TransferInterrupted) {
DbManager::instance()->updateTransferStatus(transferId, transferStatus);

d->m_activityMonitor->activityFinished(transferId);
d->sendNotification(type, transferStatus, filePath);
emit statusChanged(transferId, status);

Expand All @@ -1072,8 +1144,8 @@ void TransferEngine::updateTransferProgress(int transferId, double progress)
return;
}


if (DbManager::instance()->updateProgress(transferId, progress)) {
d->m_activityMonitor->newActivity(transferId);
emit progressChanged(transferId, progress);
} else {
qWarning() << "TransferEngine::updateTransferProgress: Failed to update progress for " << transferId;
Expand Down Expand Up @@ -1129,6 +1201,7 @@ void TransferEngine::cancelTransfer(int transferId)
// Handle canceling of Download or Sync
if (type == TransferEngineData::Download || type == TransferEngineData::Sync) {
d->callbackCall(transferId, TransferEnginePrivate::CancelCallback);
d->m_activityMonitor->activityFinished(transferId);
return;
}

Expand All @@ -1139,7 +1212,7 @@ void TransferEngine::cancelTransfer(int transferId)
qWarning() << "TransferEngine::cancelTransfer: Failed to get MediaTransferInterface!";
return;
}

d->m_activityMonitor->activityFinished(transferId);
muif->cancel();
}
}
Expand Down

0 comments on commit 536c46e

Please sign in to comment.