syncevolution/src/dbus/server/progress-data.cpp
Patrick Ohly 5214e0052a D-Bus server: ensure progress percentage is 0-100
For example in the new TestLocalCache.testItemDelete100, the
percentage value in the ProgressChanged signal become larger
than 100 and then revert to 100 at the end of the sync.

Seems the underlying calculation is faulty or simply inaccurate.
This patch does not fix that. Instead it just clips the final
result to the valid range. It also cleans up ownership of the
actual int32_t progress value.
2012-08-29 11:01:29 +02:00

232 lines
6.9 KiB
C++

/*
* Copyright (C) 2011 Intel Corporation
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) version 3.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301 USA
*/
#include "progress-data.h"
#include <cmath>
#include <limits>
SE_BEGIN_CXX
const float ProgressData::PRO_SYNC_PREPARE_RATIO = 0.2;
const float ProgressData::DATA_PREPARE_RATIO = 0.10;
const float ProgressData::ONEITEM_SEND_RATIO = 0.05;
const float ProgressData::ONEITEM_RECEIVE_RATIO = 0.05;
const float ProgressData::CONN_SETUP_RATIO = 0.5;
ProgressData::ProgressData()
: m_progress(0),
m_step(PRO_SYNC_INVALID),
m_sendCounts(0),
m_internalMode(INTERNAL_NONE)
{
/**
* init default units of each step
*/
float totalUnits = 0.0;
for(int i = 0; i < PRO_SYNC_TOTAL; i++) {
float units = getDefaultUnits((ProgressStep)i);
m_syncUnits[i] = units;
totalUnits += units;
}
m_propOfUnit = 1.0 / totalUnits;
/**
* init default sync step proportions. each step stores proportions of
* its previous steps and itself.
*/
m_syncProp[0] = 0;
for(int i = 1; i < PRO_SYNC_TOTAL - 1; i++) {
m_syncProp[i] = m_syncProp[i - 1] + m_syncUnits[i] / totalUnits;
}
m_syncProp[PRO_SYNC_TOTAL - 1] = 1.0;
}
void ProgressData::setProgress(int progress)
{
m_progress = progress > 100 ? 100 :
progress < 0 ? 0 :
progress;
}
void ProgressData::setStep(ProgressStep step)
{
if(m_step != step) {
/** if state is changed, progress is set as the end of current step*/
setProgress(100.0 * m_syncProp[(int)m_step]);
m_step = step; ///< change to new state
m_sendCounts = 0; ///< clear send/receive counts
m_source = ""; ///< clear source
}
}
void ProgressData::sendStart()
{
checkInternalMode();
m_sendCounts++;
/* self adapts. If a new send and not default, we need re-calculate proportions */
if(m_sendCounts > MSG_SEND_RECEIVE_TIMES) {
m_syncUnits[(int)m_step] += 1;
recalc();
}
/**
* If in the send operation of PRO_SYNC_UNINIT, it often takes extra time
* to send message due to items handling
*/
if(m_step == PRO_SYNC_UNINIT && m_syncUnits[(int)m_step] != MSG_SEND_RECEIVE_TIMES) {
updateProg(DATA_PREPARE_RATIO);
}
}
void ProgressData::receiveEnd()
{
/**
* often receiveEnd is the last operation of each step by default.
* If more send/receive, then we need expand proportion of current
* step and re-calc them
*/
updateProg(m_syncUnits[(int)m_step]);
}
void ProgressData::addSyncMode(SyncMode mode)
{
switch(mode) {
case SYNC_TWO_WAY:
case SYNC_SLOW:
m_internalMode |= INTERNAL_TWO_WAY;
break;
case SYNC_ONE_WAY_FROM_CLIENT:
case SYNC_REFRESH_FROM_CLIENT:
m_internalMode |= INTERNAL_ONLY_TO_CLIENT;
break;
case SYNC_ONE_WAY_FROM_SERVER:
case SYNC_REFRESH_FROM_SERVER:
m_internalMode |= INTERNAL_ONLY_TO_SERVER;
break;
default:
;
};
}
void ProgressData::itemPrepare()
{
checkInternalMode();
/**
* only the first PEV_ITEMPREPARE event takes some time
* due to data access, other events don't according to
* profiling data
*/
if(m_source.empty()) {
m_source = "source"; ///< use this to check whether itemPrepare occurs
updateProg(DATA_PREPARE_RATIO);
}
}
void ProgressData::itemReceive(const std::string &source, int count, int total)
{
/**
* source is used to check whether a new source is received
* If the first source, we compare its total number and default number
* then re-calc sync units
*/
if(m_source.empty()) {
m_source = source;
if(total != 0) {
m_syncUnits[PRO_SYNC_UNINIT] += ONEITEM_RECEIVE_RATIO * (total - DEFAULT_ITEMS);
recalc();
}
/** if another new source, add them into sync units */
} else if(m_source != source){
m_source = source;
if(total != 0) {
m_syncUnits[PRO_SYNC_UNINIT] += ONEITEM_RECEIVE_RATIO * total;
recalc();
}
}
updateProg(ONEITEM_RECEIVE_RATIO);
}
void ProgressData::updateProg(float ratio)
{
setProgress(m_progress + m_propOfUnit * 100 * ratio);
m_syncUnits[(int)m_step] -= ratio;
}
/** dynamically adapt the proportion of each step by their current units */
void ProgressData::recalc()
{
float units = getRemainTotalUnits();
if(std::abs(units) < std::numeric_limits<float>::epsilon()) {
m_propOfUnit = 0.0;
} else {
m_propOfUnit = ( 100.0 - m_progress ) / (100.0 * units);
}
if(m_step != PRO_SYNC_TOTAL -1 ) {
m_syncProp[(int)m_step] = m_progress / 100.0 + m_syncUnits[(int)m_step] * m_propOfUnit;
for(int i = ((int)m_step) + 1; i < PRO_SYNC_TOTAL - 1; i++) {
m_syncProp[i] = m_syncProp[i - 1] + m_syncUnits[i] * m_propOfUnit;
}
}
}
void ProgressData::checkInternalMode()
{
if(!m_internalMode) {
return;
} else if(m_internalMode & INTERNAL_TWO_WAY) {
// don't adjust
} else if(m_internalMode & INTERNAL_ONLY_TO_CLIENT) {
// only to client, remove units of prepare and send
m_syncUnits[PRO_SYNC_DATA] -= (ONEITEM_RECEIVE_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO);
recalc();
} else if(m_internalMode & INTERNAL_ONLY_TO_SERVER) {
// only to server, remove units of receive
m_syncUnits[PRO_SYNC_UNINIT] -= (ONEITEM_RECEIVE_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO);
recalc();
}
m_internalMode = INTERNAL_NONE;
}
float ProgressData::getRemainTotalUnits()
{
float total = 0.0;
for(int i = (int)m_step; i < PRO_SYNC_TOTAL; i++) {
total += m_syncUnits[i];
}
return total;
}
float ProgressData::getDefaultUnits(ProgressStep step)
{
switch(step) {
case PRO_SYNC_PREPARE:
return PRO_SYNC_PREPARE_RATIO;
case PRO_SYNC_INIT:
return CONN_SETUP_RATIO + MSG_SEND_RECEIVE_TIMES;
case PRO_SYNC_DATA:
return ONEITEM_SEND_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO + MSG_SEND_RECEIVE_TIMES;
case PRO_SYNC_UNINIT:
return ONEITEM_RECEIVE_RATIO * DEFAULT_ITEMS + DATA_PREPARE_RATIO + MSG_SEND_RECEIVE_TIMES;
default:
return 0;
};
}
SE_END_CXX