Index: src/ch/task.cpp =================================================================== diff -u -rc435ab507c8b8280264188b49e9ada56d46c0261 -r249b191b71fb9dde75cf47ff3a04eddbab822698 --- src/ch/task.cpp (.../task.cpp) (revision c435ab507c8b8280264188b49e9ada56d46c0261) +++ src/ch/task.cpp (.../task.cpp) (revision 249b191b71fb9dde75cf47ff3a04eddbab822698) @@ -1,5 +1,5 @@ /*************************************************************************** -* Copyright (C) 2001-2008 by Jozef Starosczyk * +* Copyright (C) 2001-2010 by Jozef Starosczyk * * ixen@copyhandler.com * * * * This program is free software; you can redistribute it and/or modify * @@ -18,785 +18,251 @@ ***************************************************************************/ #include "Stdafx.h" #include "task.h" -#include "StringHelpers.h" -#include "../common/FileSupport.h" -#include "ch.h" -#include "FeedbackHandler.h" + #include #include #include -#include #include -// assume max sectors of 4kB (for rounding) -#define MAXSECTORSIZE 4096 +#include "StringHelpers.h" +#include "FeedbackHandler.h" -/////////////////////////////////////////////////////////////////////// -// CProcessingException +#include "../libchcore/TTaskConfiguration.h" +#include "../libchcore/TSubTaskContext.h" -CProcessingException::CProcessingException(int iType, UINT uiFmtID, DWORD dwError, ...) -{ - // std values - m_iType=iType; - m_dwError=dwError; +#include "../libchcore/TLocalFilesystem.h" +#include "../libchcore/TSubTaskScanDirectory.h" +#include "../libchcore/TSubTaskCopyMove.h" +#include "../libchcore/TSubTaskDelete.h" +#include "FileSupport.h" +#include "../libchcore/TBinarySerializer.h" +#include "../libchcore/SerializationHelpers.h" - // format some text - CString strFormat = GetResManager().LoadString(uiFmtID); - ExpandFormatString(&strFormat, dwError); - - // get param list - va_list marker; - va_start(marker, dwError); - m_strErrorDesc.FormatV(strFormat, marker); - va_end(marker); -} - -CProcessingException::CProcessingException(int iType, DWORD dwError, const tchar_t* pszDesc) -{ - // std values - m_iType=iType; - m_dwError=dwError; - - // format some text - m_strErrorDesc = pszDesc; -} - -//////////////////////////////////////////////////////////////////////////////// -// TTasksGlobalStats members - -TTasksGlobalStats::TTasksGlobalStats() : -m_ullGlobalTotalSize(0), -m_ullGlobalProcessedSize(0), -m_stRunningTasks(0) -{ -} - -TTasksGlobalStats::~TTasksGlobalStats() -{ -} - -void TTasksGlobalStats::IncreaseGlobalTotalSize(unsigned long long ullModify) -{ - m_lock.lock(); - m_ullGlobalTotalSize += ullModify; - m_lock.unlock(); -} - -void TTasksGlobalStats::DecreaseGlobalTotalSize(unsigned long long ullModify) -{ - m_lock.lock(); - m_ullGlobalTotalSize -= ullModify; - m_lock.unlock(); -} - -unsigned long long TTasksGlobalStats::GetGlobalTotalSize() const -{ - boost::shared_lock lock(m_lock); - return m_ullGlobalTotalSize; -} - -void TTasksGlobalStats::IncreaseGlobalProcessedSize(unsigned long long ullModify) -{ - m_lock.lock(); - m_ullGlobalProcessedSize += ullModify; - m_lock.unlock(); -} - -void TTasksGlobalStats::DecreaseGlobalProcessedSize(unsigned long long ullModify) -{ - m_lock.lock(); - m_ullGlobalProcessedSize -= ullModify; - m_lock.unlock(); -} - -unsigned long long TTasksGlobalStats::GetGlobalProcessedSize() const -{ - boost::shared_lock lock(m_lock); - return m_ullGlobalProcessedSize; -} - -void TTasksGlobalStats::IncreaseGlobalProgressData(unsigned long long ullTasksPosition, unsigned long long ullTasksSize) -{ - m_lock.lock(); - m_ullGlobalTotalSize += ullTasksSize; - m_ullGlobalProcessedSize += ullTasksPosition; - m_lock.unlock(); - -} - -void TTasksGlobalStats::DecreaseGlobalProgressData(unsigned long long ullTasksPosition, unsigned long long ullTasksSize) -{ - m_lock.lock(); - m_ullGlobalTotalSize -= ullTasksSize; - m_ullGlobalProcessedSize -= ullTasksPosition; - m_lock.unlock(); -} - -int TTasksGlobalStats::GetProgressPercents() const -{ - unsigned long long llPercent = 0; - - boost::shared_lock lock(m_lock); - - if(m_ullGlobalTotalSize != 0) - llPercent = m_ullGlobalProcessedSize * 100 / m_ullGlobalTotalSize; - - return boost::numeric_cast(llPercent); -} - -void TTasksGlobalStats::IncreaseRunningTasks() -{ - m_lock.lock(); - ++m_stRunningTasks; - m_lock.unlock(); -} - -void TTasksGlobalStats::DecreaseRunningTasks() -{ - m_lock.lock(); - --m_stRunningTasks; - m_lock.unlock(); -} - -size_t TTasksGlobalStats::GetRunningTasksCount() const -{ - boost::shared_lock lock(m_lock); - return m_stRunningTasks; -} - -//////////////////////////////////////////////////////////////////////////////// -// TTasksGlobalStats members -TTaskLocalStats::TTaskLocalStats() : - m_prtGlobalStats(NULL), - m_ullProcessedSize(0), - m_ullTotalSize(0), - m_bTaskIsRunning(false), - m_timeElapsed(0), - m_timeLast(-1) -{ -} - -TTaskLocalStats::~TTaskLocalStats() -{ - DisconnectGlobalStats(); -} - -void TTaskLocalStats::ConnectGlobalStats(TTasksGlobalStats& rtGlobalStats) -{ - DisconnectGlobalStats(); - - boost::unique_lock lock(m_lock); - - m_prtGlobalStats = &rtGlobalStats; - m_prtGlobalStats->IncreaseGlobalProgressData(m_ullProcessedSize, m_ullTotalSize); - if(m_bTaskIsRunning) - m_prtGlobalStats->IncreaseRunningTasks(); -} - -void TTaskLocalStats::DisconnectGlobalStats() -{ - boost::unique_lock lock(m_lock); - if(m_prtGlobalStats) - { - m_prtGlobalStats->DecreaseGlobalProgressData(m_ullProcessedSize, m_ullTotalSize); - m_prtGlobalStats = NULL; - if(m_bTaskIsRunning) - m_prtGlobalStats->DecreaseRunningTasks(); - } -} - -void TTaskLocalStats::IncreaseProcessedSize(unsigned long long ullAdd) -{ - boost::unique_lock lock(m_lock); - - if(m_prtGlobalStats) - m_prtGlobalStats->IncreaseGlobalProcessedSize(ullAdd); - - m_ullProcessedSize += ullAdd; -} - -void TTaskLocalStats::DecreaseProcessedSize(unsigned long long ullSub) -{ - boost::unique_lock lock(m_lock); - if(m_prtGlobalStats) - m_prtGlobalStats->DecreaseGlobalProcessedSize(ullSub); - - m_ullProcessedSize -= ullSub; -} - -void TTaskLocalStats::SetProcessedSize(unsigned long long ullSet) -{ - boost::unique_lock lock(m_lock); - - if(m_prtGlobalStats) - { - if(ullSet < m_ullProcessedSize) - m_prtGlobalStats->DecreaseGlobalProcessedSize(m_ullProcessedSize - ullSet); - else - m_prtGlobalStats->IncreaseGlobalProcessedSize(ullSet - m_ullProcessedSize); - } - - m_ullProcessedSize = ullSet; -} - -unsigned long long TTaskLocalStats::GetProcessedSize() const -{ - boost::shared_lock lock(m_lock); - return m_ullProcessedSize; -} - -unsigned long long TTaskLocalStats::GetUnProcessedSize() const -{ - boost::shared_lock lock(m_lock); - return m_ullTotalSize - m_ullProcessedSize; -} - -void TTaskLocalStats::IncreaseTotalSize(unsigned long long ullAdd) -{ - boost::unique_lock lock(m_lock); - - if(m_prtGlobalStats) - m_prtGlobalStats->IncreaseGlobalTotalSize(ullAdd); - m_ullTotalSize += ullAdd; -} - -void TTaskLocalStats::DecreaseTotalSize(unsigned long long ullSub) -{ - boost::unique_lock lock(m_lock); - - if(m_prtGlobalStats) - m_prtGlobalStats->DecreaseGlobalTotalSize(ullSub); - - m_ullTotalSize -= ullSub; -} - -void TTaskLocalStats::SetTotalSize(unsigned long long ullSet) -{ - boost::unique_lock lock(m_lock); - - if(m_prtGlobalStats) - { - if(ullSet < m_ullTotalSize) - m_prtGlobalStats->DecreaseGlobalTotalSize(m_ullTotalSize - ullSet); - else - m_prtGlobalStats->IncreaseGlobalTotalSize(ullSet - m_ullTotalSize); - } - - m_ullTotalSize = ullSet; -} - -unsigned long long TTaskLocalStats::GetTotalSize() const -{ - boost::shared_lock lock(m_lock); - return m_ullTotalSize; -} - -int TTaskLocalStats::GetProgressInPercent() const -{ - boost::shared_lock lock(m_lock); - - unsigned long long ullPercent = 0; - - if(m_ullTotalSize != 0) - ullPercent = m_ullProcessedSize * 100 / m_ullTotalSize; - - return boost::numeric_cast(ullPercent); -} - -void TTaskLocalStats::MarkTaskAsRunning() -{ - boost::unique_lock lock(m_lock); - if(!m_bTaskIsRunning) - { - if(m_prtGlobalStats) - m_prtGlobalStats->IncreaseRunningTasks(); - m_bTaskIsRunning = true; - } -} - -void TTaskLocalStats::MarkTaskAsNotRunning() -{ - boost::unique_lock lock(m_lock); - if(m_bTaskIsRunning) - { - if(m_prtGlobalStats) - m_prtGlobalStats->DecreaseRunningTasks(); - m_bTaskIsRunning = false; - } -} - -bool TTaskLocalStats::IsRunning() const -{ - boost::shared_lock lock(m_lock); - return m_bTaskIsRunning; -} - -void TTaskLocalStats::SetTimeElapsed(time_t timeElapsed) -{ - boost::unique_lock lock(m_lock); - m_timeElapsed = timeElapsed; -} - -time_t TTaskLocalStats::GetTimeElapsed() -{ - UpdateTime(); - - boost::shared_lock lock(m_lock); - return m_timeElapsed; -} - -void TTaskLocalStats::EnableTimeTracking() -{ - boost::upgrade_lock lock(m_lock); - if(m_timeLast == -1) - { - boost::upgrade_to_unique_lock lock_upgraded(lock); - m_timeLast = time(NULL); - } -} - -void TTaskLocalStats::DisableTimeTracking() -{ - UpdateTime(); - - boost::upgrade_lock lock(m_lock); - if(m_timeLast != -1) - { - boost::upgrade_to_unique_lock lock_upgraded(lock); - m_timeLast = -1; - } -} - -void TTaskLocalStats::UpdateTime() -{ - boost::upgrade_lock lock(m_lock); - if(m_timeLast != -1) - { - time_t timeCurrent = time(NULL); - - boost::upgrade_to_unique_lock lock_upgraded(lock); - m_timeElapsed += timeCurrent - m_timeLast; - m_timeLast = timeCurrent; - } -} - //////////////////////////////////////////////////////////////////////////// // CTask members + CTask::CTask(chcore::IFeedbackHandler* piFeedbackHandler, size_t stSessionUniqueID) : - m_log(), - m_piFeedbackHandler(piFeedbackHandler), - m_files(m_clipboard), - m_stCurrentIndex(0), - m_nStatus(ST_NULL_STATUS), - m_nPriority(THREAD_PRIORITY_NORMAL), - m_ucCopies(1), - m_ucCurrentCopy(0), - m_uiResumeInterval(0), - m_bForce(false), - m_bContinue(false), - m_bSaved(false), - m_stSessionUniqueID(stSessionUniqueID), - m_localStats() +m_log(), +m_piFeedbackHandler(piFeedbackHandler), +m_arrSourcePathsInfo(m_tTaskDefinition.GetSourcePaths()), +m_files(m_tTaskDefinition.GetSourcePaths()), +m_bForce(false), +m_bContinue(false), +m_bRareStateModified(false), +m_bOftenStateModified(false), +m_stSessionUniqueID(stSessionUniqueID), +m_localStats(), +m_eCurrentState(eTaskState_None) { BOOST_ASSERT(piFeedbackHandler); - - m_bsSizes.m_uiDefaultSize=65536; - m_bsSizes.m_uiOneDiskSize=4194304; - m_bsSizes.m_uiTwoDisksSize=262144; - m_bsSizes.m_uiCDSize=262144; - m_bsSizes.m_uiLANSize=65536; - - _itot((int)time(NULL), m_strUniqueName.GetBufferSetLength(16), 10); - m_strUniqueName.ReleaseBuffer(); - } - - CTask::~CTask() - { - KillThread(); - if(m_piFeedbackHandler) - m_piFeedbackHandler->Delete(); - } - - void CTask::OnRegisterTask(TTasksGlobalStats& rtGlobalStats) - { - m_localStats.ConnectGlobalStats(rtGlobalStats); - } - - void CTask::OnUnregisterTask() - { - m_localStats.DisconnectGlobalStats(); - } - - // m_clipboard - void CTask::AddClipboardData(const CClipboardEntryPtr& spEntry) - { - m_clipboard.Add(spEntry); - } - - CClipboardEntryPtr CTask::GetClipboardData(size_t stIndex) - { - return m_clipboard.GetAt(stIndex); - } - - size_t CTask::GetClipboardDataSize() - { - return m_clipboard.GetSize(); - } - - int CTask::ReplaceClipboardStrings(CString strOld, CString strNew) -{ - return m_clipboard.ReplacePathsPrefix(strOld, strNew); } -// m_files -int CTask::FilesAddDir(CString strDirName, size_t stSrcIndex, bool bRecurse, bool bIncludeDirs) +CTask::~CTask() { - WIN32_FIND_DATA wfd; - CString strText; - - // append '\\' at the end of path if needed - if(strDirName.Right(1) != _T("\\")) - strDirName += _T("\\"); - - strText = strDirName + _T("*"); - - // Iterate through dirs & files - HANDLE hFind = FindFirstFile(strText, &wfd); - if(hFind != INVALID_HANDLE_VALUE) - { - do - { - if(!(wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) - { - CFileInfoPtr spFileInfo(boost::make_shared()); - spFileInfo->SetClipboard(&m_clipboard); // this is the link table (CClipboardArray) - - spFileInfo->Create(&wfd, strDirName, stSrcIndex); - if(m_afFilters.Match(spFileInfo)) - m_files.AddFileInfo(spFileInfo); - } - else if(wfd.cFileName[0] != _T('.') || (wfd.cFileName[1] != _T('\0') && (wfd.cFileName[1] != _T('.') || wfd.cFileName[2] != _T('\0')))) - { - if(bIncludeDirs) - { - CFileInfoPtr spFileInfo(boost::make_shared()); - spFileInfo->SetClipboard(&m_clipboard); // this is the link table (CClipboardArray) - - // Add directory itself - spFileInfo->Create(&wfd, strDirName, stSrcIndex); - m_files.AddFileInfo(spFileInfo); - } - if(bRecurse) - { - strText = strDirName + wfd.cFileName + _T("\\"); - // Recurse Dirs - FilesAddDir(strText, stSrcIndex, bRecurse, bIncludeDirs); - } - } - - if(m_workerThread.KillRequested()) - break; - } - while(FindNextFile(hFind, &wfd)); - - FindClose(hFind); - } - - return 0; + KillThread(); + if(m_piFeedbackHandler) + m_piFeedbackHandler->Delete(); } -void CTask::FilesAdd(const CFileInfoPtr& spFileInfo) +void CTask::SetTaskDefinition(const chcore::TTaskDefinition& rTaskDefinition) { - if(spFileInfo->IsDirectory() || m_afFilters.Match(spFileInfo)) - m_files.AddFileInfo(spFileInfo); -} + m_tTaskDefinition = rTaskDefinition; -CFileInfoPtr CTask::FilesGetAt(size_t stIndex) -{ - return m_files.GetAt(stIndex); -} - -CFileInfoPtr CTask::FilesGetAtCurrentIndex() -{ - size_t stCurrentIndex = 0; - - m_lock.lock_shared(); - stCurrentIndex = m_stCurrentIndex; - m_lock.unlock_shared(); - - return m_files.GetAt(m_stCurrentIndex); -} - -void CTask::FilesRemoveAll() -{ + m_arrSourcePathsInfo.SetCount(m_tTaskDefinition.GetSourcePathCount()); m_files.Clear(); } -size_t CTask::FilesGetSize() +void CTask::OnRegisterTask(chcore::TTasksGlobalStats& rtGlobalStats) { - return m_files.GetSize(); + m_localStats.ConnectGlobalStats(rtGlobalStats); } -// m_stCurrentIndex -void CTask::IncreaseCurrentIndex() +void CTask::OnUnregisterTask() { - boost::unique_lock lock(m_lock); - ++m_stCurrentIndex; + m_localStats.DisconnectGlobalStats(); } -size_t CTask::GetCurrentIndex() +void CTask::SetTaskState(ETaskCurrentState eTaskState) { - boost::shared_lock lock(m_lock); - return m_stCurrentIndex; -} - -void CTask::SetCurrentIndex(size_t stIndex) -{ + // NOTE: we could check some transition rules here boost::unique_lock lock(m_lock); - m_stCurrentIndex = stIndex; + m_eCurrentState = eTaskState; } -// m_strDestPath - adds '\\' -void CTask::SetDestPath(LPCTSTR lpszPath) +ETaskCurrentState CTask::GetTaskState() const { - boost::unique_lock lock(m_lock); - m_dpDestPath.SetPath(lpszPath); -} - -// guaranteed '\\' -const CDestPath& CTask::GetDestPath() -{ boost::shared_lock lock(m_lock); - return m_dpDestPath; + return m_eCurrentState; } -int CTask::GetDestDriveNumber() +void CTask::SetBufferSizes(const chcore::TBufferSizes& bsSizes) { - boost::shared_lock lock(m_lock); - return m_dpDestPath.GetDriveNumber(); + m_tTaskDefinition.GetConfiguration().DelayNotifications(); + chcore::SetTaskPropValue(m_tTaskDefinition.GetConfiguration(), bsSizes.GetDefaultSize()); + chcore::SetTaskPropValue(m_tTaskDefinition.GetConfiguration(), bsSizes.GetOneDiskSize()); + chcore::SetTaskPropValue(m_tTaskDefinition.GetConfiguration(), bsSizes.GetTwoDisksSize()); + chcore::SetTaskPropValue(m_tTaskDefinition.GetConfiguration(), bsSizes.GetCDSize()); + chcore::SetTaskPropValue(m_tTaskDefinition.GetConfiguration(), bsSizes.GetLANSize()); + chcore::SetTaskPropValue(m_tTaskDefinition.GetConfiguration(), bsSizes.IsOnlyDefault()); + m_tTaskDefinition.GetConfiguration().ResumeNotifications(); } -// m_nStatus -void CTask::SetStatus(UINT nStatus, UINT nMask) +void CTask::GetBufferSizes(chcore::TBufferSizes& bsSizes) { - boost::unique_lock lock(m_lock); - m_nStatus &= ~nMask; - m_nStatus |= nStatus; + bsSizes.SetDefaultSize(chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration())); + bsSizes.SetOneDiskSize(chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration())); + bsSizes.SetTwoDisksSize(chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration())); + bsSizes.SetCDSize(chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration())); + bsSizes.SetLANSize(chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration())); + bsSizes.SetOnlyDefault(chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration())); } -UINT CTask::GetStatus(UINT nMask) +int CTask::GetCurrentBufferIndex() { - boost::shared_lock lock(m_lock); - return m_nStatus & nMask; + return m_localStats.GetCurrentBufferIndex(); } -// m_nBufferSize -void CTask::SetBufferSizes(const BUFFERSIZES* bsSizes) +// thread +void CTask::SetPriority(int nPriority) { - boost::unique_lock lock(m_lock); - m_bsSizes=*bsSizes; - m_bSaved=false; + chcore::SetTaskPropValue(m_tTaskDefinition.GetConfiguration(), nPriority); } -const BUFFERSIZES* CTask::GetBufferSizes() +void CTask::CalculateProcessedSize() { boost::shared_lock lock(m_lock); - return &m_bsSizes; + CalculateProcessedSizeNL(); } -int CTask::GetCurrentBufferIndex() +void CTask::CalculateProcessedSizeNL() { - int rv = 0; - - boost::shared_lock lock(m_lock); - - size_t stSize = m_files.GetSize(); - if(stSize > 0 && m_stCurrentIndex != std::numeric_limits::max()) - rv = m_bsSizes.m_bOnlyDefault ? 0 : m_files.GetAt((m_stCurrentIndex < stSize) ? m_stCurrentIndex : 0)->GetBufferIndex(); - - return rv; + m_localStats.SetProcessedSize(m_files.CalculatePartialSize(m_tTaskBasicProgressInfo.GetCurrentIndex())); } -// m_pThread -// m_nPriority -int CTask::GetPriority() +void CTask::Load(const chcore::TSmartPath& strPath) { - boost::shared_lock lock(m_lock); + using chcore::Serializers::Serialize; - return m_nPriority; -} - -void CTask::SetPriority(int nPriority) -{ boost::unique_lock lock(m_lock); - SetPriorityNL(nPriority); -} -void CTask::CalculateTotalSize() -{ - unsigned long long ullTotalSize = 0; + //////////////////////////////// + // First load task description + m_tTaskDefinition.Load(strPath); + m_strFilePath = strPath; - boost::shared_lock lock(m_lock); - size_t nSize = m_files.GetSize(); - for(size_t i = 0; i < nSize; i++) - { - ullTotalSize += m_files.GetAt(i)->GetLength64(); - } + // update members according to the task definition + // make sure to resize paths info array size to match source paths count + m_arrSourcePathsInfo.SetCount(m_tTaskDefinition.GetSourcePathCount()); + chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration(), m_afFilters); - ullTotalSize *= m_ucCopies; + //////////////////////////////// + // now rarely changing task progress data + chcore::TSmartPath pathRarelyChangingPath = GetRelatedPathNL(ePathType_TaskRarelyChangingState); + chcore::TReadBinarySerializer readSerializer; + readSerializer.Init(pathRarelyChangingPath); - m_localStats.SetTotalSize(ullTotalSize); -} + m_arrSourcePathsInfo.Serialize(readSerializer, true); + m_files.Serialize(readSerializer, false); -void CTask::CalculateProcessedSize() -{ - unsigned long long ullProcessedSize = 0; + CalculateTotalSizeNL(); - // count all from previous passes - boost::shared_lock lock(m_lock); - if(m_ucCopies) - ullProcessedSize += m_ucCurrentCopy * (m_localStats.GetTotalSize() / m_ucCopies); - else - ullProcessedSize += m_ucCurrentCopy * m_localStats.GetTotalSize(); + /////////////////////////////////// + // and often changing data + chcore::TSmartPath pathOftenChangingPath = GetRelatedPathNL(ePathType_TaskOftenChangingState); + readSerializer.Init(pathOftenChangingPath); - for(size_t stIndex = 0; stIndex < m_stCurrentIndex; ++stIndex) - { - ullProcessedSize += m_files.GetAt(stIndex)->GetLength64(); - } + Serialize(readSerializer, m_tTaskBasicProgressInfo); - m_localStats.SetProcessedSize(ullProcessedSize); -} + CalculateProcessedSizeNL(); -// m_strUniqueName - -CString CTask::GetUniqueName() -{ - boost::shared_lock lock(m_lock); - return m_strUniqueName; -} - -void CTask::Load(const CString& strPath, bool bData) -{ - std::ifstream ifs(strPath, ios_base::in | ios_base::binary); - boost::archive::binary_iarchive ar(ifs); - - boost::unique_lock lock(m_lock); - if(bData) + // load task state, convert "waiting" state to "processing" + int iState = eTaskState_None; + Serialize(readSerializer, iState); + if(iState >= eTaskState_None && iState < eTaskState_Max) { - m_clipboard.Load(ar, 0, bData); - - m_files.Load(ar, 0, false); - - ar >> m_dpDestPath; - - ar >> m_strUniqueName; - ar >> m_afFilters; - ar >> m_ucCopies; + if(iState == eTaskState_Waiting) + iState = eTaskState_Processing; + m_eCurrentState = (ETaskCurrentState)iState; } else { - size_t stData = 0; - UINT uiData = 0; + BOOST_ASSERT(false); + THROW(_T("Wrong data read from stream"), 0, 0, 0); + } - ar >> stData; - m_stCurrentIndex = stData; - ar >> uiData; - m_nStatus = uiData; + time_t timeElapsed = 0; + Serialize(readSerializer, timeElapsed); + m_localStats.SetTimeElapsed(timeElapsed); - ar >> m_bsSizes; - ar >> m_nPriority; - - // this info could be calculated on load (low cost) - unsigned long long ullTotalSize = 0; - ar >> ullTotalSize; - m_localStats.SetTotalSize(ullTotalSize); - - time_t timeElapsed = 0; - ar >> timeElapsed; - m_localStats.SetTimeElapsed(timeElapsed); - - // this info could be calculated on load (low cost) - unsigned long long ullProcessedSize = 0; - ar >> ullProcessedSize; - m_localStats.SetProcessedSize(ullProcessedSize); - - ar >> m_ucCurrentCopy; - - m_clipboard.Load(ar, 0, bData); - m_files.Load(ar, 0, true); - - ar >> m_bSaved; - } + m_arrSourcePathsInfo.Serialize(readSerializer, false); + m_files.Serialize(readSerializer, true); } -void CTask::Store(bool bData) +void CTask::Store() { - boost::shared_lock lock(m_lock); - BOOST_ASSERT(!m_strTaskBasePath.empty()); - if(m_strTaskBasePath.empty()) - THROW(_t("Missing task path."), 0, 0, 0); + using chcore::Serializers::Serialize; - if(!bData && m_bSaved) - return; + boost::upgrade_lock lock(m_lock); - if(!bData && !m_bSaved && ( (m_nStatus & ST_STEP_MASK) == ST_FINISHED || (m_nStatus & ST_STEP_MASK) == ST_CANCELLED - || (m_nStatus & ST_WORKING_MASK) == ST_PAUSED )) + BOOST_ASSERT(!m_strTaskDirectory.IsEmpty()); + if(m_strTaskDirectory.IsEmpty()) + THROW(_t("Missing task path."), 0, 0, 0); + + // generate file path if not available yet + if(m_strFilePath.IsEmpty()) { - m_bSaved = true; + boost::upgrade_to_unique_lock upgraded_lock(lock); + m_strFilePath = m_strTaskDirectory + chcore::PathFromWString(m_tTaskDefinition.GetTaskUniqueID() + _T(".cht")); } - CString strPath = m_strTaskBasePath.c_str() + GetUniqueNameNL() + (bData ? _T(".atd") : _T(".atp")); + // store task definition only if changed + m_tTaskDefinition.Store(GetRelatedPathNL(ePathType_TaskDefinition), true); - std::ofstream ofs(strPath, ios_base::out | ios_base::binary); - boost::archive::binary_oarchive ar(ofs); - - if(bData) + // rarely changing data + if(m_bRareStateModified) { - m_clipboard.Store(ar, 0, bData); + chcore::TWriteBinarySerializer writeSerializer; + writeSerializer.Init(GetRelatedPathNL(ePathType_TaskRarelyChangingState)); - if(GetStatusNL(ST_STEP_MASK) > ST_SEARCHING) - m_files.Store(ar, 0, false); + m_arrSourcePathsInfo.Serialize(writeSerializer, true); + + chcore::ESubOperationType eSubOperation = m_tTaskDefinition.GetOperationPlan().GetSubOperationAt(m_tTaskBasicProgressInfo.GetSubOperationIndex()); + if(eSubOperation != chcore::eSubOperation_Scanning) + m_files.Serialize(writeSerializer, false); else { - size_t st(0); - ar << st; + size_t stFakeSize(0); + Serialize(writeSerializer, stFakeSize); } - - ar << m_dpDestPath; - ar << m_strUniqueName; - ar << m_afFilters; - ar << m_ucCopies; } - else + + if(m_bOftenStateModified) { - size_t stCurrentIndex = m_stCurrentIndex; - ar << stCurrentIndex; - UINT uiStatus = (m_nStatus & ST_WRITE_MASK); - ar << uiStatus; + chcore::TWriteBinarySerializer writeSerializer; + writeSerializer.Init(GetRelatedPathNL(ePathType_TaskOftenChangingState)); - ar << m_bsSizes; - ar << m_nPriority; + Serialize(writeSerializer, m_tTaskBasicProgressInfo); - unsigned long long ullTotalSize = m_localStats.GetTotalSize(); - ar << ullTotalSize; + // store current state (convert from waiting to processing state before storing) + int iState = m_eCurrentState; + if(iState == eTaskState_Waiting) + iState = eTaskState_Processing; + Serialize(writeSerializer, iState); + time_t timeElapsed = m_localStats.GetTimeElapsed(); - ar << timeElapsed; + Serialize(writeSerializer, timeElapsed); - unsigned long long ullProcessedSize = m_localStats.GetProcessedSize(); - ar << ullProcessedSize; + m_arrSourcePathsInfo.Serialize(writeSerializer, false); - ar << m_ucCurrentCopy; - - m_clipboard.Store(ar, 0, bData); - if(GetStatusNL(ST_STEP_MASK) > ST_SEARCHING) - m_files.Store(ar, 0, true); + chcore::ESubOperationType eSubOperation = m_tTaskDefinition.GetOperationPlan().GetSubOperationAt(m_tTaskBasicProgressInfo.GetSubOperationIndex()); + if(eSubOperation != chcore::eSubOperation_Scanning) + m_files.Serialize(writeSerializer, true); else { - size_t st(0); - ar << st; + size_t stFakeSize(0); + Serialize(writeSerializer, stFakeSize); } - ar << m_bSaved; } } @@ -809,39 +275,27 @@ { boost::unique_lock lock(m_lock); - m_uiResumeInterval = 0; // just in case - m_bSaved = false; // save + m_bRareStateModified = true; + m_bOftenStateModified = true; - m_workerThread.StartThread(ThrdProc, this, m_nPriority); + m_workerThread.StartThread(DelegateThreadProc, this, chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration())); } void CTask::ResumeProcessing() { // the same as retry but less demanding - if( (GetStatus(ST_WORKING_MASK) & ST_PAUSED) && GetStatus(ST_STEP_MASK) != ST_FINISHED - && GetStatus(ST_STEP_MASK) != ST_CANCELLED) + if(GetTaskState() == eTaskState_Paused) { - SetStatus(0, ST_ERROR); + SetTaskState(eTaskState_Processing); BeginProcessing(); } } -bool CTask::RetryProcessing(bool bOnlyErrors, UINT uiInterval) +bool CTask::RetryProcessing() { // retry used to auto-resume, after loading - if( (GetStatus(ST_WORKING_MASK) == ST_ERROR || (!bOnlyErrors && GetStatus(ST_WORKING_MASK) != ST_PAUSED)) - && GetStatus(ST_STEP_MASK) != ST_FINISHED && GetStatus(ST_STEP_MASK) != ST_CANCELLED) + if(GetTaskState() != eTaskState_Paused && GetTaskState() != eTaskState_Finished && GetTaskState() != eTaskState_Cancelled) { - if(uiInterval != 0) - { - m_uiResumeInterval+=uiInterval; - if(m_uiResumeInterval < (UINT)GetConfig().get_signed_num(PP_CMAUTORETRYINTERVAL)) - return false; - else - m_uiResumeInterval=0; - } - - SetStatus(0, ST_ERROR); BeginProcessing(); return true; } @@ -851,55 +305,58 @@ void CTask::RestartProcessing() { KillThread(); - SetStatus(0, ST_ERROR); - SetStatus(ST_NULL_STATUS, ST_STEP_MASK); + + SetTaskState(eTaskState_None); + m_localStats.SetTimeElapsed(0); - SetCurrentIndex(0); - SetCurrentCopy(0); + m_tTaskBasicProgressInfo.SetCurrentIndex(0); + BeginProcessing(); } void CTask::PauseProcessing() { - if(GetStatus(ST_STEP_MASK) != ST_FINISHED && GetStatus(ST_STEP_MASK) != ST_CANCELLED) + if(GetTaskState() != eTaskState_Finished && GetTaskState() != eTaskState_Cancelled) { KillThread(); - SetStatus(ST_PAUSED, ST_WORKING_MASK); - m_bSaved=false; + SetTaskState(eTaskState_Paused); + + m_bOftenStateModified = true; } } void CTask::CancelProcessing() { // change to ST_CANCELLED - if(GetStatus(ST_STEP_MASK) != ST_FINISHED) + if(GetTaskState() != eTaskState_Finished) { KillThread(); - SetStatus(ST_CANCELLED, ST_STEP_MASK); - SetStatus(0, ST_ERROR); - m_bSaved=false; + SetTaskState(eTaskState_Cancelled); + m_bOftenStateModified = true; } } void CTask::GetMiniSnapshot(TASK_MINI_DISPLAY_DATA *pData) { boost::shared_lock lock(m_lock); - if(m_stCurrentIndex >= 0 && m_stCurrentIndex < m_files.GetSize()) - pData->m_strPath = m_files.GetAt(m_stCurrentIndex)->GetFileName(); + size_t stCurrentIndex = m_tTaskBasicProgressInfo.GetCurrentIndex(); + + if(stCurrentIndex < m_files.GetSize()) + pData->m_strPath = m_files.GetAt(stCurrentIndex)->GetFullFilePath().GetFileName().ToString(); else { if(m_files.GetSize() > 0) - pData->m_strPath = m_files.GetAt(0)->GetFileName(); + pData->m_strPath = m_files.GetAt(0)->GetFullFilePath().GetFileName().ToString(); else { - if(m_clipboard.GetSize() > 0) - pData->m_strPath = m_clipboard.GetAt(0)->GetFileName(); + if(m_tTaskDefinition.GetSourcePathCount() > 0) + pData->m_strPath = m_tTaskDefinition.GetSourcePathAt(0).GetFileName().ToString(); else - pData->m_strPath = GetResManager().LoadString(IDS_NONEINPUTFILE_STRING); + pData->m_strPath.Clear(); } } - pData->m_uiStatus=m_nStatus; + pData->m_eTaskState = m_eCurrentState; // percents pData->m_nPercent = m_localStats.GetProgressInPercent(); @@ -909,157 +366,104 @@ { boost::unique_lock lock(m_lock); - if(m_stCurrentIndex >= 0 && m_stCurrentIndex < m_files.GetSize()) + size_t stCurrentIndex = m_tTaskBasicProgressInfo.GetCurrentIndex(); + if(stCurrentIndex < m_files.GetSize()) { - pData->m_strFullFilePath = m_files.GetAt(m_stCurrentIndex)->GetFullFilePath(); - pData->m_strFileName = m_files.GetAt(m_stCurrentIndex)->GetFileName(); + pData->m_strFullFilePath = m_files.GetAt(stCurrentIndex)->GetFullFilePath().ToString(); + pData->m_strFileName = m_files.GetAt(stCurrentIndex)->GetFullFilePath().GetFileName().ToString(); } else { if(m_files.GetSize() > 0) { - pData->m_strFullFilePath = m_files.GetAt(0)->GetFullFilePath(); - pData->m_strFileName = m_files.GetAt(0)->GetFileName(); + pData->m_strFullFilePath = m_files.GetAt(0)->GetFullFilePath().ToString(); + pData->m_strFileName = m_files.GetAt(0)->GetFullFilePath().GetFileName().ToString(); } else { - if(m_clipboard.GetSize() > 0) + if(m_tTaskDefinition.GetSourcePathCount() > 0) { - pData->m_strFullFilePath = m_clipboard.GetAt(0)->GetPath(); - pData->m_strFileName = m_clipboard.GetAt(0)->GetFileName(); + pData->m_strFullFilePath = m_tTaskDefinition.GetSourcePathAt(0).ToString(); + pData->m_strFileName = m_tTaskDefinition.GetSourcePathAt(0).GetFileName().ToString(); } else { - pData->m_strFullFilePath = GetResManager().LoadString(IDS_NONEINPUTFILE_STRING); - pData->m_strFileName = pData->m_strFullFilePath; + pData->m_strFullFilePath.Clear(); + pData->m_strFileName.Clear(); } } } - pData->m_pbsSizes=&m_bsSizes; - pData->m_nPriority=m_nPriority; - pData->m_pdpDestPath=&m_dpDestPath; - pData->m_pafFilters=&m_afFilters; - pData->m_uiStatus=m_nStatus; - pData->m_stIndex=m_stCurrentIndex+m_ucCurrentCopy*m_files.GetSize(); + pData->m_nPriority = chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration()); + pData->m_pathDstPath = m_tTaskDefinition.GetDestinationPath(); + pData->m_pafFilters = &m_afFilters; + pData->m_eTaskState = m_eCurrentState; + pData->m_stIndex = stCurrentIndex; pData->m_ullProcessedSize = m_localStats.GetProcessedSize(); - pData->m_stSize=m_files.GetSize()*m_ucCopies; + pData->m_stSize=m_files.GetSize(); pData->m_ullSizeAll = m_localStats.GetTotalSize(); - pData->m_ucCurrentCopy=static_cast(m_ucCurrentCopy+1); // visual aspect - pData->m_ucCopies=m_ucCopies; - pData->m_pstrUniqueName=&m_strUniqueName; + pData->m_strUniqueName = m_tTaskDefinition.GetTaskUniqueID(); + pData->m_eOperationType = m_tTaskDefinition.GetOperationType(); + pData->m_eSubOperationType = m_tTaskDefinition.GetOperationPlan().GetSubOperationAt(m_tTaskBasicProgressInfo.GetSubOperationIndex()); - if(m_files.GetSize() > 0 && m_stCurrentIndex != std::numeric_limits::max()) - pData->m_iCurrentBufferIndex=m_bsSizes.m_bOnlyDefault ? 0 : m_files.GetAt((m_stCurrentIndex < m_files.GetSize()) ? m_stCurrentIndex : 0)->GetBufferIndex(); - else - pData->m_iCurrentBufferIndex=0; + pData->m_bIgnoreDirectories = chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration()); + pData->m_bCreateEmptyFiles = chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration()); - // percents - pData->m_nPercent = m_localStats.GetProgressInPercent(); - - // status string - // first - if( (m_nStatus & ST_WORKING_MASK) == ST_ERROR ) - { - GetResManager().LoadStringCopy(IDS_STATUS0_STRING+4, pData->m_szStatusText, _MAX_PATH); - _tcscat(pData->m_szStatusText, _T("/")); - } - else if( (m_nStatus & ST_WORKING_MASK) == ST_PAUSED ) - { - GetResManager().LoadStringCopy(IDS_STATUS0_STRING+5, pData->m_szStatusText, _MAX_PATH); - _tcscat(pData->m_szStatusText, _T("/")); - } - else if( (m_nStatus & ST_STEP_MASK) == ST_FINISHED ) - { - GetResManager().LoadStringCopy(IDS_STATUS0_STRING+3, pData->m_szStatusText, _MAX_PATH); - _tcscat(pData->m_szStatusText, _T("/")); - } - else if( (m_nStatus & ST_WAITING_MASK) == ST_WAITING ) - { - GetResManager().LoadStringCopy(IDS_STATUS0_STRING+9, pData->m_szStatusText, _MAX_PATH); - _tcscat(pData->m_szStatusText, _T("/")); - } - else if( (m_nStatus & ST_STEP_MASK) == ST_CANCELLED ) - { - GetResManager().LoadStringCopy(IDS_STATUS0_STRING+8, pData->m_szStatusText, _MAX_PATH); - _tcscat(pData->m_szStatusText, _T("/")); - } + if(m_files.GetSize() > 0) + pData->m_iCurrentBufferIndex = m_localStats.GetCurrentBufferIndex(); else - _tcscpy(pData->m_szStatusText, _T("")); + pData->m_iCurrentBufferIndex = chcore::TBufferSizes::eBuffer_Default; - // second part - if( (m_nStatus & ST_STEP_MASK) == ST_DELETING ) - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_STATUS0_STRING+6)); - else if( (m_nStatus & ST_STEP_MASK) == ST_SEARCHING ) - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_STATUS0_STRING+0)); - else if((m_nStatus & ST_OPERATION_MASK) == ST_COPY ) + switch(pData->m_iCurrentBufferIndex) { - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_STATUS0_STRING+1)); - if(!m_afFilters.IsEmpty()) - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_FILTERING_STRING)); + case chcore::TBufferSizes::eBuffer_Default: + pData->m_iCurrentBufferSize = chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration()); + break; + case chcore::TBufferSizes::eBuffer_OneDisk: + pData->m_iCurrentBufferSize = chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration()); + break; + case chcore::TBufferSizes::eBuffer_TwoDisks: + pData->m_iCurrentBufferSize = chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration()); + break; + case chcore::TBufferSizes::eBuffer_CD: + pData->m_iCurrentBufferSize = chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration()); + break; + case chcore::TBufferSizes::eBuffer_LAN: + pData->m_iCurrentBufferSize = chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration()); + break; + default: + THROW(_T("Unhandled case"), 0, 0, 0); + //BOOST_ASSERT(false); // assertions are dangerous here, because we're inside critical section + // (and there could be conflict with Get(Mini)Snapshot called OnTimer in several places. } - else if( (m_nStatus & ST_OPERATION_MASK) == ST_MOVE ) - { - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_STATUS0_STRING+2)); - if(!m_afFilters.IsEmpty()) - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_FILTERING_STRING)); - } - else - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_STATUS0_STRING+7)); - // third part - if( (m_nStatus & ST_SPECIAL_MASK) & ST_IGNORE_DIRS ) - { - _tcscat(pData->m_szStatusText, _T("/")); - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_STATUS0_STRING+10)); - } - if( (m_nStatus & ST_SPECIAL_MASK) & ST_IGNORE_CONTENT ) - { - _tcscat(pData->m_szStatusText, _T("/")); - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_STATUS0_STRING+11)); - } + // percents + pData->m_nPercent = m_localStats.GetProgressInPercent(); - // count of copies - if(m_ucCopies > 1) - { - _tcscat(pData->m_szStatusText, _T("/")); - TCHAR xx[4]; - _tcscat(pData->m_szStatusText, _itot(m_ucCopies, xx, 10)); - if(m_ucCopies < 5) - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_COPYWORDLESSFIVE_STRING)); - else - _tcscat(pData->m_szStatusText, GetResManager().LoadString(IDS_COPYWORDMOREFOUR_STRING)); - } - // time pData->m_timeElapsed = m_localStats.GetTimeElapsed(); } -void CTask::DeleteProgress(LPCTSTR lpszDirectory) +void CTask::DeleteProgress() { - m_lock.lock_shared(); + chcore::TPathContainer vFilesToRemove; - CString strDel1 = lpszDirectory+m_strUniqueName+_T(".atd"); - CString strDel2 = lpszDirectory+m_strUniqueName+_T(".atp"); - CString strDel3 = lpszDirectory+m_strUniqueName+_T(".log"); + // separate scope for shared locking + { + boost::shared_lock lock(m_lock); - m_lock.unlock_shared(); + vFilesToRemove.Add(GetRelatedPath(ePathType_TaskDefinition)); + vFilesToRemove.Add(GetRelatedPath(ePathType_TaskRarelyChangingState)); + vFilesToRemove.Add(GetRelatedPath(ePathType_TaskOftenChangingState)); + vFilesToRemove.Add(GetRelatedPath(ePathType_TaskLogFile)); + } - DeleteFile(strDel1); - DeleteFile(strDel2); - DeleteFile(strDel3); + for(size_t stIndex = 0; stIndex < vFilesToRemove.GetCount(); ++stIndex) + { + DeleteFile(vFilesToRemove.GetAt(stIndex).ToString()); + } } -void CTask::SetFilters(const CFiltersArray* pFilters) -{ - BOOST_ASSERT(pFilters); - if(!pFilters) - THROW(_T("Invalid argument"), 0, 0, 0); - - boost::unique_lock lock(m_lock); - m_afFilters = *pFilters; -} - bool CTask::CanBegin() { bool bRet=true; @@ -1077,54 +481,30 @@ return bRet; } -void CTask::SetCopies(unsigned char ucCopies) +void CTask::SetTaskDirectory(const chcore::TSmartPath& strDir) { boost::unique_lock lock(m_lock); - m_ucCopies=ucCopies; + m_strTaskDirectory = strDir; } -unsigned char CTask::GetCopies() +chcore::TSmartPath CTask::GetTaskDirectory() const { boost::shared_lock lock(m_lock); - return m_ucCopies; + return m_strTaskDirectory; } -void CTask::SetCurrentCopy(unsigned char ucCopy) +void CTask::SetTaskFilePath(const chcore::TSmartPath& strFilePath) { boost::unique_lock lock(m_lock); - m_ucCurrentCopy=ucCopy; + m_strFilePath = strFilePath; } -unsigned char CTask::GetCurrentCopy() +chcore::TSmartPath CTask::GetTaskFilePath() const { boost::shared_lock lock(m_lock); - return m_ucCurrentCopy; + return m_strFilePath; } -bool CTask::GetRequiredFreeSpace(ull_t *pullNeeded, ull_t *pullAvailable) -{ - *pullNeeded = m_localStats.GetUnProcessedSize(); // it'd be nice to round up to take cluster size into consideration, - // but GetDiskFreeSpace returns flase values - - // get free space - if(!GetDynamicFreeSpace(GetDestPath().GetPath(), pullAvailable, NULL)) - return true; - - return (*pullNeeded <= *pullAvailable); -} - -void CTask::SetTaskPath(const tchar_t* pszDir) -{ - boost::unique_lock lock(m_lock); - m_strTaskBasePath = pszDir; -} - -const tchar_t* CTask::GetTaskPath() const -{ - boost::shared_lock lock(m_lock); - return m_strTaskBasePath.c_str(); -} - void CTask::SetForceFlag(bool bFlag) { boost::unique_lock lock(m_lock); @@ -1150,123 +530,12 @@ } ///////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool CTask::SetFileDirectoryTime(LPCTSTR lpszName, const CFileInfoPtr& spFileInfo) -{ - TAutoFileHandle hFile = CreateFile(lpszName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | (spFileInfo->IsDirectory() ? FILE_FLAG_BACKUP_SEMANTICS : 0), NULL); - if(hFile == INVALID_HANDLE_VALUE) - return false; - BOOL bResult = (!SetFileTime(hFile, &spFileInfo->GetCreationTime(), &spFileInfo->GetLastAccessTime(), &spFileInfo->GetLastWriteTime())); - - if(!hFile.Close()) - return false; - - return bResult != 0; -} - -// m_stCurrentIndex -void CTask::IncreaseCurrentIndexNL() -{ - ++m_stCurrentIndex; -} - -size_t CTask::GetCurrentIndexNL() -{ - return m_stCurrentIndex; -} - -void CTask::SetCurrentIndexNL(size_t stIndex) -{ - m_stCurrentIndex = stIndex; -} - -// m_strDestPath - adds '\\' -void CTask::SetDestPathNL(LPCTSTR lpszPath) -{ - m_dpDestPath.SetPath(lpszPath); -} - -// guaranteed '\\' -const CDestPath& CTask::GetDestPathNL() -{ - return m_dpDestPath; -} - -int CTask::GetDestDriveNumberNL() -{ - return m_dpDestPath.GetDriveNumber(); -} - -// m_nStatus -void CTask::SetStatusNL(UINT nStatus, UINT nMask) -{ - m_nStatus &= ~nMask; - m_nStatus |= nStatus; -} - -UINT CTask::GetStatusNL(UINT nMask) -{ - return m_nStatus & nMask; -} - -// m_nBufferSize -void CTask::SetBufferSizesNL(const BUFFERSIZES* bsSizes) -{ - m_bsSizes = *bsSizes; - m_bSaved = false; -} - -const BUFFERSIZES* CTask::GetBufferSizesNL() -{ - return &m_bsSizes; -} - -int CTask::GetCurrentBufferIndexNL() -{ - int rv = 0; - - size_t stSize = m_files.GetSize(); - if(stSize > 0 && m_stCurrentIndex != std::numeric_limits::max()) - rv = m_bsSizes.m_bOnlyDefault ? 0 : m_files.GetAt((m_stCurrentIndex < stSize) ? m_stCurrentIndex : 0)->GetBufferIndex(); - - return rv; -} - -// m_pThread -// m_nPriority -int CTask::GetPriorityNL() -{ - return m_nPriority; -} - -void CTask::SetPriorityNL(int nPriority) -{ - m_workerThread.ChangePriority(nPriority); - - m_nPriority = nPriority; - m_bSaved = false; -} - void CTask::CalculateTotalSizeNL() { - unsigned long long ullTotalSize = 0; - - size_t nSize = m_files.GetSize(); - for(size_t i = 0; i < nSize; i++) - { - ullTotalSize += m_files.GetAt(i)->GetLength64(); - } - - ullTotalSize *= m_ucCopies; - - m_localStats.SetTotalSize(ullTotalSize); + m_localStats.SetTotalSize(m_files.CalculateTotalSize()); } -CString CTask::GetUniqueNameNL() -{ - return m_strUniqueName; -} - void CTask::SetForceFlagNL(bool bFlag) { m_bForce=bFlag; @@ -1287,1508 +556,221 @@ return m_bContinue; } -// searching for files -void CTask::RecurseDirectories() +chcore::TSubTaskBase::ESubOperationResult CTask::CheckForWaitState() { - // log - m_log.logi(_T("Searching for files...")); - - // update status - SetStatus(ST_SEARCHING, ST_STEP_MASK); - - // delete the content of m_files - FilesRemoveAll(); - - // enter some data to m_files - size_t stSize = GetClipboardDataSize(); // size of m_clipboard - int iDestDrvNumber = GetDestDriveNumber(); - bool bIgnoreDirs = (GetStatus(ST_SPECIAL_MASK) & ST_IGNORE_DIRS) != 0; - bool bForceDirectories = (GetStatus(ST_SPECIAL_MASK) & ST_FORCE_DIRS) != 0; - bool bMove = GetStatus(ST_OPERATION_MASK) == ST_MOVE; - - // add everything - ictranslate::CFormat fmt; - bool bRetry = true; - bool bSkipInputPath = false; - - for(size_t stIndex = 0; stIndex < stSize ; stIndex++) + // limiting operation count + SetTaskState(eTaskState_Waiting); + bool bContinue = false; + while(!bContinue) { - CFileInfoPtr spFileInfo; - - bSkipInputPath = false; - bRetry = false; - - // try to get some info about the input path; let user know if the path does not exist. - do + if(CanBegin()) { - // read attributes of src file/folder - spFileInfo.reset(new CFileInfo()); - spFileInfo->SetClipboard(GetClipboard()); + SetTaskState(eTaskState_Processing); + bContinue = true; - bool bExists = spFileInfo->Create(GetClipboardData(stIndex)->GetPath(), stIndex); - if(!bExists) - { - chcore::IFeedbackHandler* piFeedbackHandler = GetFeedbackHandler(); - BOOST_ASSERT(piFeedbackHandler); + m_log.logi(_T("Finished waiting for begin permission")); - CString strSrcFile = GetClipboardData(stIndex)->GetPath(); - FEEDBACK_FILEERROR ferr = { (PCTSTR)strSrcFile, NULL, eFastMoveError, ERROR_FILE_NOT_FOUND }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); - switch(frResult) - { - case CFeedbackHandler::eResult_Cancel: - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Retry: - bRetry = true; - continue; - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - case CFeedbackHandler::eResult_Skip: - bSkipInputPath = true; - bRetry = false; - break; // just do nothing - default: - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - } - } + // return; // skips sleep and kill flag checking } - while(bRetry); - // if we have chosen to skip the input path then there's nothing to do - if(bSkipInputPath) - continue; + Sleep(50); // not to make it too hard for processor - // log - fmt.SetFormat(_T("Adding file/folder (clipboard) : %path ...")); - fmt.SetParam(_t("%path"), GetClipboardData(stIndex)->GetPath()); - m_log.logi(fmt); - - // found file/folder - check if the dest name has been generated - if(GetClipboardData(stIndex)->GetDestinationPathsCount() == 0) + if(m_workerThread.KillRequested()) { - // generate something - if dest folder == src folder - search for copy - if(GetDestPath().GetPath() == spFileInfo->GetFileRoot()) - { - CString strSubst; - FindFreeSubstituteName(spFileInfo->GetFullFilePath(), GetDestPath().GetPath(), &strSubst); - GetClipboardData(stIndex)->AddDestinationPath(strSubst); - } - else - GetClipboardData(stIndex)->AddDestinationPath(spFileInfo->GetFileName()); - } - - // add if needed - if(spFileInfo->IsDirectory()) - { - // add if folder's aren't ignored - if(!bIgnoreDirs && !bForceDirectories) - { - FilesAdd(spFileInfo); - - // log - fmt.SetFormat(_T("Added folder %path")); - fmt.SetParam(_t("%path"), spFileInfo->GetFullFilePath()); - m_log.logi(fmt); - } - - // don't add folder contents when moving inside one disk boundary - if(bIgnoreDirs || !bMove || GetCopies() > 1 || iDestDrvNumber == -1 - || iDestDrvNumber != spFileInfo->GetDriveNumber() || CFileInfo::Exist(spFileInfo->GetDestinationPath(GetDestPath().GetPath(), 0, ((int)bForceDirectories) << 1)) ) - { - // log - fmt.SetFormat(_T("Recursing folder %path")); - fmt.SetParam(_t("%path"), spFileInfo->GetFullFilePath()); - m_log.logi(fmt); - - // no movefile possibility - use CustomCopyFile - GetClipboardData(stIndex)->SetMove(false); - - FilesAddDir(spFileInfo->GetFullFilePath(), stIndex, true, !bIgnoreDirs || bForceDirectories); - } - - // check for kill need - if(m_workerThread.KillRequested()) - { - // log - m_log.logi(_T("Kill request while adding data to files array (RecurseDirectories)")); - throw new CProcessingException(E_KILL_REQUEST); - } - } - else - { - if(bMove && GetCopies() == 1 && iDestDrvNumber != -1 && iDestDrvNumber == spFileInfo->GetDriveNumber() && - !CFileInfo::Exist(spFileInfo->GetDestinationPath(GetDestPath().GetPath(), 0, ((int)bForceDirectories) << 1)) ) - { - // if moving within one partition boundary set the file size to 0 so the overall size will - // be ok - spFileInfo->SetLength64(0); - } - else - GetClipboardData(stIndex)->SetMove(false); // no MoveFile - - FilesAdd(spFileInfo); // file - add - // log - fmt.SetFormat(_T("Added file %path")); - fmt.SetParam(_t("%path"), spFileInfo->GetFullFilePath()); - m_log.logi(fmt); + m_log.logi(_T("Kill request while waiting for begin permission (wait state)")); + return chcore::TSubTaskBase::eSubResult_KillRequest; } } - // calc size of all files - CalculateTotalSize(); - - // update *m_pnTasksAll; -// m_rtGlobalStats.IncreaseGlobalTotalSize(GetAllSize()); - - // change state to ST_COPYING - finished searching for files - SetStatus(ST_COPYING, ST_STEP_MASK); - - // save task status - Store(true); - Store(false); - - // log - m_log.logi(_T("Searching for files finished")); + return chcore::TSubTaskBase::eSubResult_Continue; } -// delete files - after copying -void CTask::DeleteFiles() +DWORD WINAPI CTask::DelegateThreadProc(LPVOID pParam) { - // log - m_log.logi(_T("Deleting files (DeleteFiles)...")); + BOOST_ASSERT(pParam); + if(!pParam) + return 1; - chcore::IFeedbackHandler* piFeedbackHandler = GetFeedbackHandler(); - BOOST_ASSERT(piFeedbackHandler); - - // current processed path - BOOL bSuccess; - CFileInfoPtr spFileInfo; - ictranslate::CFormat fmt; - - // index points to 0 or next item to process - size_t stIndex = GetCurrentIndex(); - while(stIndex < FilesGetSize()) - { - // set index in pTask to currently deleted element - SetCurrentIndex(stIndex); - - // check for kill flag - if(m_workerThread.KillRequested()) - { - // log - m_log.logi(_T("Kill request while deleting files (Delete Files)")); - throw new CProcessingException(E_KILL_REQUEST); - } - - // current processed element - spFileInfo = FilesGetAt(FilesGetSize() - stIndex - 1); - if(!(spFileInfo->GetFlags() & FIF_PROCESSED)) - { - ++stIndex; - continue; - } - - // delete data - if(spFileInfo->IsDirectory()) - { - if(!GetConfig().get_bool(PP_CMPROTECTROFILES)) - SetFileAttributes(spFileInfo->GetFullFilePath(), FILE_ATTRIBUTE_NORMAL | FILE_ATTRIBUTE_DIRECTORY); - bSuccess=RemoveDirectory(spFileInfo->GetFullFilePath()); - } - else - { - // set files attributes to normal - it'd slow processing a bit, but it's better. - if(!GetConfig().get_bool(PP_CMPROTECTROFILES)) - SetFileAttributes(spFileInfo->GetFullFilePath(), FILE_ATTRIBUTE_NORMAL); - bSuccess=DeleteFile(spFileInfo->GetFullFilePath()); - } - - // operation failed - DWORD dwLastError=GetLastError(); - if(!bSuccess && dwLastError != ERROR_PATH_NOT_FOUND && dwLastError != ERROR_FILE_NOT_FOUND) - { - // log - fmt.SetFormat(_T("Error #%errno while deleting file/folder %path")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%path"), spFileInfo->GetFullFilePath()); - m_log.loge(fmt); - - CString strFile = spFileInfo->GetFullFilePath(); - FEEDBACK_FILEERROR ferr = { (PCTSTR)strFile, NULL, eDeleteError, dwLastError }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); - switch(frResult) - { - case CFeedbackHandler::eResult_Cancel: - m_log.logi(_T("Cancel request while deleting file.")); - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Retry: - continue; // no stIndex bump, since we are trying again - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - case CFeedbackHandler::eResult_Skip: - break; // just do nothing - default: - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - } - } - - ++stIndex; - }//while - - // change status to finished - SetStatus(ST_FINISHED, ST_STEP_MASK); - - // add 1 to current index - looks better - IncreaseCurrentIndex(); - - // log - m_log.logi(_T("Deleting files finished")); + CTask* pTask = (CTask*)pParam; + return pTask->ThrdProc(); } -void CTask::CustomCopyFile(CUSTOM_COPY_PARAMS* pData) +DWORD CTask::ThrdProc() { - TAutoFileHandle hSrc = INVALID_HANDLE_VALUE, - hDst = INVALID_HANDLE_VALUE; - ictranslate::CFormat fmt; - bool bRetry = false; - try { - // do we copy rest or recopy ? - bool bCopyRest = false; + chcore::TSubTaskBase::ESubOperationResult eResult = chcore::TSubTaskBase::eSubResult_Continue; - // Data regarding dest file - CFileInfoPtr spDestFileInfo(boost::make_shared()); - bool bExist = spDestFileInfo->Create(pData->strDstFile, std::numeric_limits::max()); + // initialize log file + chcore::TSmartPath pathLogFile = GetRelatedPath(ePathType_TaskLogFile); - chcore::IFeedbackHandler* piFeedbackHandler = GetFeedbackHandler(); - BOOST_ASSERT(piFeedbackHandler); + m_log.init(pathLogFile.ToString(), 262144, icpf::log_file::level_debug, false, false); - // if dest file size >0 - we can do something more than usual - if(bExist) - { - // src and dst files are the same - FEEDBACK_ALREADYEXISTS feedStruct = { pData->spSrcFile, spDestFileInfo }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileAlreadyExists, &feedStruct); - // check for dialog result - switch(frResult) - { - case CFeedbackHandler::eResult_Overwrite: - { - bCopyRest = false; - break; - } - case CFeedbackHandler::eResult_CopyRest: - { - bCopyRest = true; - break; - } - case CFeedbackHandler::eResult_Skip: - { - m_localStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64()); - pData->bProcessed = false; - return; - } - case CFeedbackHandler::eResult_Cancel: - { - // log - if(GetConfig().get_bool(PP_CMCREATELOG)) - { - fmt.SetFormat(_T("Cancel request while checking result of dialog before opening source file %path (CustomCopyFile)")); - fmt.SetParam(_t("%path"), pData->spSrcFile->GetFullFilePath()); - m_log.logi(fmt); - } - throw new CProcessingException(E_CANCEL); - break; - } - case CFeedbackHandler::eResult_Pause: - { - throw new CProcessingException(E_PAUSE); - break; - } - default: - { - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - break; - } - } - }// bExist + // start operation + OnBeginOperation(); - // change attributes of a dest file - if(!GetConfig().get_bool(PP_CMPROTECTROFILES)) - SetFileAttributes(pData->strDstFile, FILE_ATTRIBUTE_NORMAL); + // enable configuration changes tracking + m_tTaskDefinition.GetConfiguration().ConnectToNotifier(chcore::TTaskConfigTracker::NotificationProc, &m_cfgTracker); + m_tTaskDefinition.GetConfiguration().ConnectToNotifier(CTask::OnCfgOptionChanged, this); - // first or second pass ? only for FFNB - bool bFirstPass = true; + // set thread options + HANDLE hThread = GetCurrentThread(); + ::SetThreadPriorityBoost(hThread, chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration())); - // check size of src file to know whether use flag FILE_FLAG_NOBUFFERING -l_start: - bool bNoBuffer=(bFirstPass && GetConfig().get_bool(PP_BFUSENOBUFFERING) && pData->spSrcFile->GetLength64() >= (unsigned long long)GetConfig().get_signed_num(PP_BFBOUNDARYLIMIT)); + // determine when to scan directories + bool bReadTasksSize = chcore::GetTaskPropValue(m_tTaskDefinition.GetConfiguration()); - // refresh data about file - if(!bFirstPass) - bExist = spDestFileInfo->Create(pData->strDstFile, std::numeric_limits::max()); + // wait for permission to really start (but only if search for files is not allowed to start regardless of the lock) + size_t stSubOperationIndex = m_tTaskBasicProgressInfo.GetSubOperationIndex(); + if(!bReadTasksSize || stSubOperationIndex != 0 || m_tTaskDefinition.GetOperationPlan().GetSubOperationsCount() == 0 || m_tTaskDefinition.GetOperationPlan().GetSubOperationAt(0) != chcore::eSubOperation_Scanning) + eResult = CheckForWaitState(); // operation limiting - // open src - do - { - bRetry = false; + // start tracking time for this thread + m_localStats.EnableTimeTracking(); - hSrc = CreateFile(pData->spSrcFile->GetFullFilePath(), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | (bNoBuffer ? FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH : 0), NULL); - if(hSrc == INVALID_HANDLE_VALUE) - { - DWORD dwLastError=GetLastError(); - CString strFile = pData->spSrcFile->GetFullFilePath(); - FEEDBACK_FILEERROR feedStruct = { (PCTSTR)strFile, NULL, eCreateError, dwLastError }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &feedStruct); + // prepare context for subtasks + chcore::TSubTaskContext tSubTaskContext(m_tTaskDefinition, m_arrSourcePathsInfo, m_files, m_localStats, m_tTaskBasicProgressInfo, m_cfgTracker, m_log, m_piFeedbackHandler, m_workerThread, m_fsLocal); - switch (frResult) - { - case CFeedbackHandler::eResult_Skip: - m_localStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64()); - pData->bProcessed = false; - return; - break; - case CFeedbackHandler::eResult_Cancel: - // log - fmt.SetFormat(_T("Cancel request [error %errno] while opening source file %path (CustomCopyFile)")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%path"), pData->spSrcFile->GetFullFilePath()); - m_log.loge(fmt); - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - case CFeedbackHandler::eResult_Retry: - // log - fmt.SetFormat(_T("Retrying [error %errno] to open source file %path (CustomCopyFile)")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%path"), pData->spSrcFile->GetFullFilePath()); - m_log.loge(fmt); - bRetry = true; - break; - default: - { - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - break; - } - } - } - } - while(bRetry); - - // open dest - do + for(; stSubOperationIndex < m_tTaskDefinition.GetOperationPlan().GetSubOperationsCount() && eResult == chcore::TSubTaskBase::eSubResult_Continue; ++stSubOperationIndex) { - bRetry = false; + // set current sub-operation index to allow resuming + m_tTaskBasicProgressInfo.SetSubOperationIndex(stSubOperationIndex); - hDst = CreateFile(pData->strDstFile, GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_SEQUENTIAL_SCAN | (bNoBuffer ? FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH : 0), NULL); - if(hDst == INVALID_HANDLE_VALUE) + chcore::ESubOperationType eSubOperation = m_tTaskDefinition.GetOperationPlan().GetSubOperationAt(stSubOperationIndex); + switch(eSubOperation) { - DWORD dwLastError=GetLastError(); - CString strFile = pData->strDstFile; - - FEEDBACK_FILEERROR feedStruct = { (PCTSTR)strFile, NULL, eCreateError, dwLastError }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &feedStruct); - switch (frResult) + case chcore::eSubOperation_Scanning: { - case CFeedbackHandler::eResult_Retry: - // change attributes - if(!GetConfig().get_bool(PP_CMPROTECTROFILES)) - SetFileAttributes(pData->strDstFile, FILE_ATTRIBUTE_NORMAL); + // start searching + chcore::TSubTaskScanDirectories tSubTaskScanDir(tSubTaskContext); + eResult = tSubTaskScanDir.Exec(); - // log - fmt.SetFormat(_T("Retrying [error %errno] to open destination file %path (CustomCopyFile)")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%path"), pData->strDstFile); - m_log.loge(fmt); - bRetry = true; - break; - case CFeedbackHandler::eResult_Cancel: - // log - fmt.SetFormat(_T("Cancel request [error %errno] while opening destination file %path (CustomCopyFile)")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%path"), pData->strDstFile); - m_log.loge(fmt); - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Skip: - m_localStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64()); - pData->bProcessed = false; - return; - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - default: + // if we didn't wait for permission to start earlier, then ask now (but only in case this is the first search) + if(eResult == chcore::TSubTaskBase::eSubResult_Continue && bReadTasksSize && stSubOperationIndex == 0) { - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - break; - } - } - } - } - while(bRetry); + m_localStats.DisableTimeTracking(); - // seeking - DWORD dwLastError = 0; - if(!pData->bOnlyCreate) - { - if(bCopyRest) // if copy rest - { - if(!bFirstPass || (bExist && spDestFileInfo->GetLength64() > 0)) - { - // try to move file pointers to the end - ULONGLONG ullMove = (bNoBuffer ? ROUNDDOWN(spDestFileInfo->GetLength64(), MAXSECTORSIZE) : spDestFileInfo->GetLength64()); - bool bRetry = true; - while(bRetry) - { - if(SetFilePointer64(hSrc, ullMove, FILE_BEGIN) == -1 || SetFilePointer64(hDst, ullMove, FILE_BEGIN) == -1) - { - dwLastError = GetLastError(); - // log - fmt.SetFormat(_T("Error %errno while moving file pointers of %srcpath and %dstpath to %pos")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%srcpath"), pData->spSrcFile->GetFullFilePath()); - fmt.SetParam(_t("%dstpath"), pData->strDstFile); - fmt.SetParam(_t("%pos"), ullMove); - m_log.loge(fmt); + eResult = CheckForWaitState(); - CString strSrcFile = pData->spSrcFile->GetFullFilePath(); - CString strDstFile = pData->strDstFile; - FEEDBACK_FILEERROR ferr = { (PCTSTR)strSrcFile, (PCTSTR)strDstFile, eSeekError, dwLastError }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); - switch(frResult) - { - case CFeedbackHandler::eResult_Cancel: - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Retry: - continue; - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - case CFeedbackHandler::eResult_Skip: - bRetry = false; - m_localStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64()); - pData->bProcessed = false; - return; - default: - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - } - } - else - { - bRetry = false; - // file pointers moved - so we have skipped some work - update positions - if(bFirstPass) - m_localStats.IncreaseProcessedSize(ullMove); - } + m_localStats.EnableTimeTracking(); } - } - } - else - { - bool bRetry = true; - while(bRetry && !SetEndOfFile(hDst)) - { - // log - dwLastError=GetLastError(); - fmt.SetFormat(_T("Error %errno while setting size of file %path to 0")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%path"), pData->strDstFile); - m_log.loge(fmt); - FEEDBACK_FILEERROR ferr = { (PCTSTR)pData->strDstFile, NULL, eResizeError, dwLastError }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); - switch(frResult) - { - case CFeedbackHandler::eResult_Cancel: - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Retry: - continue; - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - case CFeedbackHandler::eResult_Skip: - bRetry = false; - break; // just do nothing - default: - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - } + break; } - } - // copying - unsigned long ulToRead = 0, ulRead = 0, ulWritten = 0; - int iBufferIndex = 0; - do - { - // kill flag checks - if(m_workerThread.KillRequested()) + case chcore::eSubOperation_Copying: { - // log - fmt.SetFormat(_T("Kill request while main copying file %srcpath -> %dstpath")); - fmt.SetParam(_t("%srcpath"), pData->spSrcFile->GetFullFilePath()); - fmt.SetParam(_t("%dstpath"), pData->strDstFile); - m_log.logi(fmt); - throw new CProcessingException(E_KILL_REQUEST); - } + chcore::TSubTaskCopyMove tSubTaskCopyMove(tSubTaskContext); - // recreate buffer if needed - if(!(*pData->dbBuffer.GetSizes() == *GetBufferSizes())) - { - // log - const BUFFERSIZES *pbs1=pData->dbBuffer.GetSizes(), *pbs2=GetBufferSizes(); - - fmt.SetFormat(_T("Changing buffer size from [Def:%defsize, One:%onesize, Two:%twosize, CD:%cdsize, LAN:%lansize] to [Def:%defsize2, One:%onesize2, Two:%twosize2, CD:%cdsize2, LAN:%lansize2] wile copying %srcfile -> %dstfile (CustomCopyFile)")); - - fmt.SetParam(_t("%defsize"), pbs1->m_uiDefaultSize); - fmt.SetParam(_t("%onesize"), pbs1->m_uiOneDiskSize); - fmt.SetParam(_t("%twosize"), pbs1->m_uiTwoDisksSize); - fmt.SetParam(_t("%cdsize"), pbs1->m_uiCDSize); - fmt.SetParam(_t("%lansize"), pbs1->m_uiLANSize); - fmt.SetParam(_t("%defsize2"), pbs2->m_uiDefaultSize); - fmt.SetParam(_t("%onesize2"), pbs2->m_uiOneDiskSize); - fmt.SetParam(_t("%twosize2"), pbs2->m_uiTwoDisksSize); - fmt.SetParam(_t("%cdsize2"), pbs2->m_uiCDSize); - fmt.SetParam(_t("%lansize2"), pbs2->m_uiLANSize); - fmt.SetParam(_t("%srcfile"), pData->spSrcFile->GetFullFilePath()); - fmt.SetParam(_t("%dstfile"), pData->strDstFile); - - m_log.logi(fmt); - SetBufferSizes(pData->dbBuffer.Create(GetBufferSizes())); + eResult = tSubTaskCopyMove.Exec(); + break; } - // establish count of data to read - iBufferIndex=GetBufferSizes()->m_bOnlyDefault ? 0 : pData->spSrcFile->GetBufferIndex(); - ulToRead=bNoBuffer ? ROUNDUP(pData->dbBuffer.GetSizes()->m_auiSizes[iBufferIndex], MAXSECTORSIZE) : pData->dbBuffer.GetSizes()->m_auiSizes[iBufferIndex]; - - // read - bool bRetry = true; - while(bRetry && !ReadFile(hSrc, pData->dbBuffer, ulToRead, &ulRead, NULL)) + case chcore::eSubOperation_Deleting: { - // log - dwLastError=GetLastError(); - fmt.SetFormat(_T("Error %errno while trying to read %count bytes from source file %path (CustomCopyFile)")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%count"), ulToRead); - fmt.SetParam(_t("%path"), pData->spSrcFile->GetFullFilePath()); - m_log.loge(fmt); - - CString strFile = pData->spSrcFile->GetFullFilePath(); - FEEDBACK_FILEERROR ferr = { (PCTSTR)strFile, NULL, eReadError, dwLastError }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); - switch(frResult) - { - case CFeedbackHandler::eResult_Cancel: - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Retry: - continue; - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - case CFeedbackHandler::eResult_Skip: - bRetry = false; - // TODO: correct the skip length handling - m_localStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64()); - pData->bProcessed = false; - return; - default: - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - } + chcore::TSubTaskDelete tSubTaskDelete(tSubTaskContext); + eResult = tSubTaskDelete.Exec(); + break; } - // change count of stored data - if(bNoBuffer && (ROUNDUP(ulRead, MAXSECTORSIZE)) != ulRead) - { - // we need to copy rest so do the second pass - // close files - hSrc.Close(); - hDst.Close(); - - // second pass - bFirstPass=false; - bCopyRest=true; // nedd to copy rest - - goto l_start; - } - - // write - bRetry = true; - while(bRetry && !WriteFile(hDst, pData->dbBuffer, ulRead, &ulWritten, NULL) || ulWritten != ulRead) - { - // log - dwLastError=GetLastError(); - fmt.SetFormat(_T("Error %errno while trying to write %count bytes to destination file %path (CustomCopyFile)")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%count"), ulRead); - fmt.SetParam(_t("%path"), pData->strDstFile); - m_log.loge(fmt); - - CString strFile = pData->strDstFile; - FEEDBACK_FILEERROR ferr = { (PCTSTR)strFile, NULL, eWriteError, dwLastError }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); - switch(frResult) - { - case CFeedbackHandler::eResult_Cancel: - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Retry: - continue; - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - case CFeedbackHandler::eResult_Skip: - bRetry = false; - // TODO: correct the skip length handling - m_localStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64()); - pData->bProcessed = false; - return; - default: - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - } - } - - // increase count of processed data - m_localStats.IncreaseProcessedSize(ulRead); + default: + BOOST_ASSERT(false); + THROW(_T("Unhandled case"), 0, 0, 0); } - while(ulRead != 0); } - else - { - // we don't copy contents, but need to increase processed size - m_localStats.IncreaseProcessedSize(pData->spSrcFile->GetLength64()); - } - // close files - hSrc.Close(); - hDst.Close(); + // change status to finished + if(eResult == chcore::TSubTaskBase::eSubResult_Continue) + SetTaskState(eTaskState_Finished); - pData->bProcessed = true; - } - catch(...) - { - // close handles - hSrc.Close(); - hDst.Close(); + // refresh time + m_localStats.DisableTimeTracking(); - throw; - } -} -/* - -void CTask::CustomCopyFile2(CUSTOM_COPY_PARAMS* / *pData* /) -{ - // 1. DetermineStartupData: - bool bNoBuffering = false; // TODO: read from config file - - // additional variables - bool bRetry = false; - - // 2. Open source file - HANDLE hFile = INVALID_HANDLE_VALUE; - do - { - bRetry = false; - - hFile = CreateFile(_T("source_path"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, bNoBuffering ? FILE_FLAG_NO_BUFFERING : 0, NULL); - if(hFile == INVALID_HANDLE_VALUE) + // finishing processing + // change task status + switch(eResult) { - int iResponse = 0; // Ask user what to do - switch(iResponse) - { - case 0://eSkip: - return; - break; - case 1://eRetry: - bRetry = true; - break; - case 2://eCancel: - break; - case 3: //ePause: - break; - default: - throw; - } - } - } - while(bRetry); + case chcore::TSubTaskBase::eSubResult_Error: + m_piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_OperationError, NULL); + SetTaskState(eTaskState_Error); + break; - // 3. Open destination file - a more complex part - do - { - bRetry = false; + case chcore::TSubTaskBase::eSubResult_CancelRequest: + SetTaskState(eTaskState_Cancelled); + break; - hFile = CreateFile(_T("destination_path"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, bNoBuffering ? FILE_FLAG_NO_BUFFERING : 0, NULL); - if(hFile == INVALID_HANDLE_VALUE) - { - // creating new destination file have failed - either file exists or some other problem occurred - DWORD dwLastError = GetLastError(); - if(dwLastError == ERROR_FILE_EXISTS) - { - // destination file already exists - try to open existing file - do - { - hFile = CreateFile(_T("destination_path"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, bNoBuffering ? FILE_FLAG_NO_BUFFERING : 0, NULL); - if(hFile == INVALID_HANDLE_VALUE) - { - // opening existing destination file failed... - dwLastError = GetLastError(); - if(dwLastError == ERROR_ACCESS_DENIED) - { - // access to the file was denied - is it read only? - // ask user what to do (reset read-only attr, skip, pause, cancel, retry) - } - else - { - // some other error occurred when trying to open existing destination file - // ask user what todo - } - } - else - { - // existing destination file was successfully opened - gather information about it - // and ask user if he want to overwrite/append/skip/... - } - } - while(bRetry); - } - else - { - // there is other reason for file creation failure - } - } - } - while(bRetry); - - / * - - - - switch ERROR - { - * File already exists: - { - loop - { - ReOpenDestinationFile (OPEN_EXISTING, NoBuffering ? NO_BUFFER : NORMAL) - if FAILED - { - switch ERROR - { - * Access denied = - { - GetFileAttributes - RESPONSE = Ask user - switch RESPONSE - { - * Skip / Retry / Cancel / Pause = - Standard + case chcore::TSubTaskBase::eSubResult_PauseRequest: + SetTaskState(eTaskState_Paused); + break; - ???????????????????????? - - problem with read only -> Delete ReadOnly attr - - standard options - skip/retry/cancel/pause - - rename option - - - ???????????????????????? - } - } + case chcore::TSubTaskBase::eSubResult_KillRequest: + // the only operation + if(GetTaskState() == eTaskState_Waiting) + SetTaskState(eTaskState_Processing); + break; - * Other errors = - { + case chcore::TSubTaskBase::eSubResult_Continue: + m_piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_OperationFinished, NULL); + SetTaskState(eTaskState_Finished); + break; - } - } - else if SUCCEEDED - { - // append/overwrite? - // skip/cancel/pause (no retry?) - } - } - while RETRY - - RESPONSE = Ask user - switch RESPONSE - { - * Skip / Retry / Cancel / Pause = - Standard - - * Replace = - { - loop - { - ReOpenDestinationFile (OPEN_EXISTING, NoBuffering ? NO_BUFFER : NORMAL) - if FAILED - { - - RESPONSE = Ask user - switch RESPONSE - { - * Skip / Retry / Cancel / Pause = - Standard - - - ???????????????????????? - - problem with read only -> Delete ReadOnly attr - - standard options - skip/retry/cancel/pause - - rename option - - - ???????????????????????? - } - } - } - while RETRY - - loop - { - SetEndOfFile at current file position (0) - if FAILED - { - * Skip = - exit proc -> continue with next src file - * Retry = - RETRY LOOP - * Cancel = - stop processing completely with cancelled status - * Pause = - stop processing completely with paused status - } - } - while RETRY - } - - * Append = - { - loop - { - ReOpen dst file (OPEN_EXISTING, NoBuffering ? NO_BUFFER : NORMAL) - if FAILED - { - RESPONSE = Ask user - switch RESPONSE - { - ???????????????????????? - } - } - } - while RETRY - - loop - { - SeekToEnd (possibly round down when NoBuffering) - if FAILED - { - * Skip = - exit proc -> continue with next src file - * Retry = - RETRY LOOP - * Cancel = - stop processing completely with cancelled status - * Pause = - stop processing completely with paused status - } - } - while RETRY - - loop - { - SeekToPosition for source file opened at pt 1 to position at which dst file has been set - if FAILED - { - * Skip = - exit proc -> continue with next src file - * Retry = - RETRY LOOP - * Cancel = - stop processing completely with cancelled status - * Pause = - stop processing completely with paused status - } - } - while RETRY - } - * Rename = - { - Store new filename + goto 2 - } - } - } - - * Other problems: - { - - Skip - - Retry - - Cancel - - Pause - - Rename = - Store new filename + goto 2 - } - } - } - } - while RETRY - } - while(bRetry); - - - 3. Copy contents of source file to destination file - - loop: - - Read xxx bytes from source file (take NoBuffer requirements into consideration) - - If bytes read are equal to those requested: - - write bytes to destination filename - - else - - - - - - - ////////////////////////////////////// - Standard options when asking user for feedback: - * Skip = - exit proc -> continue with next src file - * Retry = - RETRY LOOP - * Cancel = - stop processing completely with cancelled status - * Pause = - stop processing completely with paused status -* / -} -*/ -/* - -HANDLE CTask::OpenSourceFile(const CString& strPath, bool bNoBuffering/ *, FeedbackSettings* /) -{ - bool bRetry = false; - HANDLE hFile = INVALID_HANDLE_VALUE; - do - { - bRetry = false; - - hFile = CreateFile(_T("source_path"), GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, bNoBuffering ? FILE_FLAG_NO_BUFFERING : 0, NULL); - if(hFile == INVALID_HANDLE_VALUE) - { - int iResponse = 0; // Ask user what to do - switch(iResponse) - { - case 0://eSkip: - return; - break; - case 1://eRetry: - bRetry = true; - break; - case 2://eCancel: - break; - case 3: //ePause: - break; - default: - throw; - } + default: + BOOST_ASSERT(false); + THROW(_T("Unhandled case"), 0, 0, 0); } - } - while(bRetry); - return hFile; -} - -HANDLE CTask::CreateNewDestinationFile(const CString& strPath, bool bNoBuffering/ *, FeedbackSettings* /) -{ - bool bRetry = false; - HANDLE hFile = INVALID_HANDLE_VALUE; - do - { - bRetry = false; - - hFile = CreateFile(_T("destination_path"), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_NEW, bNoBuffering ? FILE_FLAG_NO_BUFFERING : 0, NULL); - if(hFile == INVALID_HANDLE_VALUE) + // perform cleanup dependent on currently executing subtask + switch(m_tTaskDefinition.GetOperationPlan().GetSubOperationAt(m_tTaskBasicProgressInfo.GetSubOperationIndex())) { - // creating new destination file have failed - either file exists or some other problem occurred - DWORD dwLastError = GetLastError(); - if(dwLastError == ERROR_FILE_EXISTS) - { - // destination file already exists - try to open existing file - return OpenExistingDestinationFile(strPath, bNoBuffering); - } - else - { - // there is other reason for file creation failure - int iResponse = 0; // Ask user what to do - switch(iResponse) - { - case 0://eSkip: - return; - break; - case 1://eRetry: - bRetry = true; - break; - case 2://eCancel: - break; - case 3: //ePause: - break; - default: - throw; - } + case chcore::eSubOperation_Scanning: + m_files.Clear(); // get rid of m_files contents + m_bRareStateModified = true; + break; } - } - while(bRetry); - return hFile; -} + // save progress before killed + m_bOftenStateModified = true; + Store(); -HANDLE CTask::OpenExistingDestinationFile(const CString& strPath, bool bNoBuffering/ *, FeedbackSettings* /) -{ + // reset flags + SetContinueFlag(false); + SetForceFlag(false); -} + // mark this task as dead, so other can start + m_localStats.MarkTaskAsNotRunning(); -void CTask::SetEndOfFile(HANDLE hFile) -{ + m_tTaskDefinition.GetConfiguration().DisconnectFromNotifier(chcore::TTaskConfigTracker::NotificationProc); + m_tTaskDefinition.GetConfiguration().DisconnectFromNotifier(CTask::OnCfgOptionChanged); -} - -void CTask::SeekToPosition(HANDLE hFile, unsigned long long ullPos) -{ - -} -*/ - -// function processes files/folders -void CTask::ProcessFiles() -{ - chcore::IFeedbackHandler* piFeedbackHandler = GetFeedbackHandler(); - BOOST_ASSERT(piFeedbackHandler); - - // log - m_log.logi(_T("Processing files/folders (ProcessFiles)")); - - // count how much has been done (updates also a member in CTaskArray) - CalculateProcessedSize(); - - // create a buffer of size m_nBufferSize - CUSTOM_COPY_PARAMS ccp; - ccp.bProcessed = false; - ccp.bOnlyCreate=(GetStatus(ST_SPECIAL_MASK) & ST_IGNORE_CONTENT) != 0; - ccp.dbBuffer.Create(GetBufferSizes()); - - // helpers - DWORD dwLastError = 0; - - // begin at index which wasn't processed previously - size_t stSize = FilesGetSize(); - int iCopiesCount = GetCopies(); - bool bIgnoreFolders = (GetStatus(ST_SPECIAL_MASK) & ST_IGNORE_DIRS) != 0; - bool bForceDirectories = (GetStatus(ST_SPECIAL_MASK) & ST_FORCE_DIRS) != 0; - const CDestPath& dpDestPath = GetDestPath(); - - // log - const BUFFERSIZES* pbs = ccp.dbBuffer.GetSizes(); - - ictranslate::CFormat fmt; - fmt.SetFormat(_T("Processing files/folders (ProcessFiles):\r\n\tOnlyCreate: %create\r\n\tBufferSize: [Def:%defsize, One:%onesize, Two:%twosize, CD:%cdsize, LAN:%lansize]\r\n\tFiles/folders count: %filecount\r\n\tCopies count: %copycount\r\n\tIgnore Folders: %ignorefolders\r\n\tDest path: %dstpath\r\n\tCurrent pass (0-based): %currpass\r\n\tCurrent index (0-based): %currindex")); - fmt.SetParam(_t("%create"), ccp.bOnlyCreate); - fmt.SetParam(_t("%defsize"), pbs->m_uiDefaultSize); - fmt.SetParam(_t("%onesize"), pbs->m_uiOneDiskSize); - fmt.SetParam(_t("%twosize"), pbs->m_uiTwoDisksSize); - fmt.SetParam(_t("%cdsize"), pbs->m_uiCDSize); - fmt.SetParam(_t("%lansize"), pbs->m_uiLANSize); - fmt.SetParam(_t("%filecount"), stSize); - fmt.SetParam(_t("%copycount"), iCopiesCount); - fmt.SetParam(_t("%ignorefolders"), bIgnoreFolders); - fmt.SetParam(_t("%dstpath"), dpDestPath.GetPath()); - fmt.SetParam(_t("%currpass"), GetCurrentCopy()); - fmt.SetParam(_t("%currindex"), GetCurrentIndex()); - - m_log.logi(fmt); - - for (unsigned char j=GetCurrentCopy();jGetDestinationPath(dpDestPath.GetPath(), j, ((int)bForceDirectories) << 1 | (int)bIgnoreFolders); - - // are the files/folders lie on the same partition ? - bool bMove=GetStatus(ST_OPERATION_MASK) == ST_MOVE; - if(bMove && dpDestPath.GetDriveNumber() != -1 && dpDestPath.GetDriveNumber() == spFileInfo->GetDriveNumber() && iCopiesCount == 1 && spFileInfo->GetMove()) - { - bool bRetry = true; - if(bRetry && !MoveFile(spFileInfo->GetFullFilePath(), ccp.strDstFile)) - { - dwLastError=GetLastError(); - //log - fmt.SetFormat(_T("Error %errno while calling MoveFile %srcpath -> %dstpath (ProcessFiles)")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%srcpath"), spFileInfo->GetFullFilePath()); - fmt.SetParam(_t("%dstpath"), ccp.strDstFile); - m_log.loge(fmt); - - CString strSrcFile = spFileInfo->GetFullFilePath(); - CString strDstFile = ccp.strDstFile; - FEEDBACK_FILEERROR ferr = { (PCTSTR)strSrcFile, (PCTSTR)strDstFile, eFastMoveError, dwLastError }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); - switch(frResult) - { - case CFeedbackHandler::eResult_Cancel: - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Retry: - continue; - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - case CFeedbackHandler::eResult_Skip: - bRetry = false; - break; // just do nothing - default: - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - } - } - else - spFileInfo->SetFlags(FIF_PROCESSED, FIF_PROCESSED); - } - else - { - // if folder - create it - if(spFileInfo->IsDirectory()) - { - bool bRetry = true; - if(bRetry && !CreateDirectory(ccp.strDstFile, NULL) && (dwLastError=GetLastError()) != ERROR_ALREADY_EXISTS ) - { - // log - fmt.SetFormat(_T("Error %errno while calling CreateDirectory %path (ProcessFiles)")); - fmt.SetParam(_t("%errno"), dwLastError); - fmt.SetParam(_t("%path"), ccp.strDstFile); - m_log.loge(fmt); - - CString strFile = ccp.strDstFile; - FEEDBACK_FILEERROR ferr = { (PCTSTR)strFile, NULL, eCreateError, dwLastError }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_FileError, &ferr); - switch(frResult) - { - case CFeedbackHandler::eResult_Cancel: - throw new CProcessingException(E_CANCEL); - break; - case CFeedbackHandler::eResult_Retry: - continue; - break; - case CFeedbackHandler::eResult_Pause: - throw new CProcessingException(E_PAUSE); - break; - case CFeedbackHandler::eResult_Skip: - bRetry = false; - break; // just do nothing - default: - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - } - } - - m_localStats.IncreaseProcessedSize(spFileInfo->GetLength64()); - spFileInfo->SetFlags(FIF_PROCESSED, FIF_PROCESSED); - } - else - { - // start copying/moving file - ccp.spSrcFile = spFileInfo; - ccp.bProcessed = false; - - // kopiuj dane - CustomCopyFile(&ccp); - spFileInfo->SetFlags(ccp.bProcessed ? FIF_PROCESSED : 0, FIF_PROCESSED); - - // if moving - delete file (only if config flag is set) - if(bMove && spFileInfo->GetFlags() & FIF_PROCESSED && !GetConfig().get_bool(PP_CMDELETEAFTERFINISHED) && j == iCopiesCount-1) - { - if(!GetConfig().get_bool(PP_CMPROTECTROFILES)) - SetFileAttributes(spFileInfo->GetFullFilePath(), FILE_ATTRIBUTE_NORMAL); - DeleteFile(spFileInfo->GetFullFilePath()); // there will be another try later, so I don't check - // if succeeded - } - } - - // set a time - if(GetConfig().get_bool(PP_CMSETDESTDATE)) - SetFileDirectoryTime(ccp.strDstFile, spFileInfo); // no error checking (but most probably it should be checked) - - // attributes - if(GetConfig().get_bool(PP_CMSETDESTATTRIBUTES)) - SetFileAttributes(ccp.strDstFile, spFileInfo->GetAttributes()); // as above - } - } - - // current copy finished - SetCurrentIndex(0); + // and the real end + OnEndOperation(); } - - // delete buffer - it's not needed - ccp.dbBuffer.Delete(); - - // change status - if(GetStatus(ST_OPERATION_MASK) == ST_MOVE) + catch(...) { - SetStatus(ST_DELETING, ST_STEP_MASK); - // set the index to 0 before deleting - SetCurrentIndex(0); - } - else - { - SetStatus(ST_FINISHED, ST_STEP_MASK); + m_tTaskDefinition.GetConfiguration().DisconnectFromNotifier(chcore::TTaskConfigTracker::NotificationProc); + m_tTaskDefinition.GetConfiguration().DisconnectFromNotifier(CTask::OnCfgOptionChanged); - // to look better - increase current index by 1 - SetCurrentIndex(stSize); - } - // log - m_log.logi(_T("Finished processing in ProcessFiles")); -} - -void CTask::CheckForWaitState() -{ - // limiting operation count - SetStatus(ST_WAITING, ST_WAITING_MASK); - bool bContinue = false; - while(!bContinue) - { - if(CanBegin()) - { - SetStatus(0, ST_WAITING); - bContinue=true; - - m_log.logi(_T("Finished waiting for begin permission")); - - // return; // skips sleep and kill flag checking - } - - Sleep(50); // not to make it too hard for processor - - if(m_workerThread.KillRequested()) - { - // log - m_log.logi(_T("Kill request while waiting for begin permission (wait state)")); - throw new CProcessingException(E_KILL_REQUEST); - } - } -} - -DWORD CTask::ThrdProc(LPVOID pParam) -{ - CTask* pTask = static_cast(pParam); - - chcore::IFeedbackHandler* piFeedbackHandler = pTask->GetFeedbackHandler(); - - tstring_t strPath = pTask->GetTaskPath(); - strPath += pTask->GetUniqueName()+_T(".log"); - - pTask->m_log.init(strPath.c_str(), 262144, icpf::log_file::level_debug, false, false); - - pTask->OnBeginOperation(); - - // set thread boost - HANDLE hThread=GetCurrentThread(); - ::SetThreadPriorityBoost(hThread, GetConfig().get_bool(PP_CMDISABLEPRIORITYBOOST)); - - CTime tm=CTime::GetCurrentTime(); - - ictranslate::CFormat fmt; - try - { - // to make the value stable - bool bReadTasksSize = GetConfig().get_bool(PP_CMREADSIZEBEFOREBLOCKING); - - if(!bReadTasksSize) - pTask->CheckForWaitState(); // operation limiting - - // start tracking time - pTask->m_localStats.EnableTimeTracking(); - - // search for files if needed - if((pTask->GetStatus(ST_STEP_MASK) == ST_NULL_STATUS - || pTask->GetStatus(ST_STEP_MASK) == ST_SEARCHING)) - { - // get rid of info about processed sizes - pTask->m_localStats.SetProcessedSize(0); - pTask->m_localStats.SetTotalSize(0); - - // start searching - pTask->RecurseDirectories(); - } - - // check for free space - ull_t ullNeededSize = 0, ullAvailableSize = 0; -l_showfeedback: - pTask->m_log.logi(_T("Checking for free space on destination disk...")); - - if(!pTask->GetRequiredFreeSpace(&ullNeededSize, &ullAvailableSize)) - { - fmt.SetFormat(_T("Not enough free space on disk - needed %needsize bytes for data, available: %availablesize bytes.")); - fmt.SetParam(_t("%needsize"), ullNeededSize); - fmt.SetParam(_t("%availablesize"), ullAvailableSize); - pTask->m_log.logw(fmt); - - BOOST_ASSERT(piFeedbackHandler); - - if(pTask->GetClipboardDataSize() > 0) - { - CString strSrcPath = pTask->GetClipboardData(0)->GetPath(); - CString strDstPath = pTask->GetDestPath().GetPath(); - FEEDBACK_NOTENOUGHSPACE feedStruct = { ullNeededSize, (PCTSTR)strSrcPath, (PCTSTR)strDstPath }; - CFeedbackHandler::EFeedbackResult frResult = (CFeedbackHandler::EFeedbackResult)piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_NotEnoughSpace, &feedStruct); - - // default - switch (frResult) - { - case CFeedbackHandler::eResult_Cancel: - { - pTask->m_log.logi(_T("Cancel request while checking for free space on disk.")); - throw new CProcessingException(E_CANCEL); - break; - } - case CFeedbackHandler::eResult_Retry: - pTask->m_log.logi(_T("Retrying to read drive's free space...")); - goto l_showfeedback; - break; - case CFeedbackHandler::eResult_Skip: - pTask->m_log.logi(_T("Ignored warning about not enough place on disk to copy data.")); - break; - default: - BOOST_ASSERT(FALSE); // unknown result - throw new CProcessingException(E_ERROR, 0, _t("Unknown feedback result type")); - break; - } - } - } - - if(bReadTasksSize) - { - pTask->m_localStats.DisableTimeTracking(); - - pTask->CheckForWaitState(); - - pTask->m_localStats.EnableTimeTracking(); - } - - // Phase II - copying/moving - if(pTask->GetStatus(ST_STEP_MASK) == ST_COPYING) - { - // decrease processed in ctaskarray - the rest will be done in ProcessFiles - //pTask->m_rtGlobalStats.DecreaseGlobalProcessedSize(pTask->GetProcessedSize()); - pTask->ProcessFiles(); - } - - // deleting data - III phase - if(pTask->GetStatus(ST_STEP_MASK) == ST_DELETING) - pTask->DeleteFiles(); - // refresh time - pTask->m_localStats.DisableTimeTracking(); + m_localStats.DisableTimeTracking(); - // save progress before killed - pTask->Store(false); - - // we are ending - pTask->m_localStats.MarkTaskAsNotRunning(); - - // play sound - piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_OperationFinished, NULL); - - pTask->OnEndOperation(); - } - catch(CProcessingException* e) - { - // refresh time - pTask->m_localStats.DisableTimeTracking(); - // log - fmt.SetFormat(_T("Caught exception in ThrdProc [last error: %errno, type: %type]")); - fmt.SetParam(_t("%errno"), e->m_dwError); - fmt.SetParam(_t("%type"), e->m_iType); - pTask->m_log.loge(fmt); + ictranslate::CFormat fmt; - if(e->m_iType == E_ERROR) - piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_OperationError, NULL); + fmt.SetFormat(_T("Caught exception in ThrdProc")); + m_log.loge(fmt); - // perform some adjustments depending on exception type - switch(e->m_iType) - { - case E_ERROR: - pTask->SetStatus(ST_ERROR, ST_WORKING_MASK); - break; - case E_CANCEL: - pTask->SetStatus(ST_CANCELLED, ST_STEP_MASK); - break; - case E_PAUSE: - pTask->SetStatus(ST_PAUSED, ST_PAUSED); - break; - } + // let others know some error happened + m_piFeedbackHandler->RequestFeedback(CFeedbackHandler::eFT_OperationError, NULL); + SetTaskState(eTaskState_Error); - // change flags and calls cleanup for a task - switch(pTask->GetStatus(ST_STEP_MASK)) - { - case ST_NULL_STATUS: - case ST_SEARCHING: - // get rid of m_files contents - pTask->FilesRemoveAll(); + m_localStats.MarkTaskAsNotRunning(); - // save state of a task - pTask->Store(true); - pTask->Store(false); + SetContinueFlag(false); + SetForceFlag(false); - break; - case ST_COPYING: - case ST_DELETING: - pTask->Store(false); - break; - } - - if(pTask->GetStatus(ST_WAITING_MASK) & ST_WAITING) - pTask->SetStatus(0, ST_WAITING); - - pTask->m_localStats.MarkTaskAsNotRunning(); - pTask->SetContinueFlag(false); - pTask->SetForceFlag(false); - - pTask->OnEndOperation(); - - delete e; - - return 0xffffffff; // almost like -1 + OnEndOperation(); + return 1; } return 0; @@ -2829,11 +811,60 @@ m_workerThread.SignalThreadToStop(); } +chcore::TSmartPath CTask::GetRelatedPath(EPathType ePathType) +{ + boost::shared_lock lock(m_lock); + + return GetRelatedPathNL(ePathType); +} + +chcore::TSmartPath CTask::GetRelatedPathNL(EPathType ePathType) +{ + BOOST_ASSERT(!m_strTaskDirectory.IsEmpty() || !m_strFilePath.IsEmpty()); + if(m_strTaskDirectory.IsEmpty() && m_strFilePath.IsEmpty()) + THROW(_t("Missing task path."), 0, 0, 0); + + // in all cases we would like to have task definition path defined + chcore::TSmartPath strFilePath = m_strFilePath; + if(strFilePath.IsEmpty()) + strFilePath = m_strTaskDirectory + chcore::PathFromWString(m_tTaskDefinition.GetTaskUniqueID() + _T(".cht")); + + switch(ePathType) + { + case ePathType_TaskDefinition: + return strFilePath; + + case ePathType_TaskRarelyChangingState: + return strFilePath.AppendCopy(chcore::PathFromString(_T(".rstate")), false); + + case ePathType_TaskOftenChangingState: + return strFilePath.AppendCopy(chcore::PathFromString(_T(".ostate")), false); + + case ePathType_TaskLogFile: + return strFilePath.AppendCopy(chcore::PathFromString(_T(".log")), false); + + default: + THROW(_t("Unhandled case"), 0, 0, 0); + } +} + +void CTask::OnCfgOptionChanged(const chcore::TStringSet& rsetChanges, void* pParam) +{ + CTask* pTask = (CTask*)pParam; + if(!pTask) + THROW(_T("Invalid pointer"), 0, 0, 0); + + if(rsetChanges.HasValue(chcore::TaskPropData::GetPropertyName())) + { + pTask->m_workerThread.ChangePriority(chcore::GetTaskPropValue(pTask->GetTaskDefinition().GetConfiguration())); + } +} + //////////////////////////////////////////////////////////////////////////////// // CTaskArray members CTaskArray::CTaskArray() : - m_piFeedbackFactory(NULL), - m_stNextSessionUniqueID(NO_TASK_SESSION_UNIQUE_ID + 1) +m_piFeedbackFactory(NULL), +m_stNextSessionUniqueID(NO_TASK_SESSION_UNIQUE_ID + 1) { } @@ -2845,22 +876,44 @@ void CTaskArray::Create(chcore::IFeedbackHandlerFactory* piFeedbackHandlerFactory) { BOOST_ASSERT(piFeedbackHandlerFactory); - + m_piFeedbackFactory = piFeedbackHandlerFactory; } -CTaskPtr CTaskArray::CreateTask() +CTaskPtr CTaskArray::CreateTask(const chcore::TTaskDefinition& tTaskDefinition) { + CTaskPtr spTask = CreateEmptyTask(); + if(spTask) + { + spTask->SetTaskDefinition(tTaskDefinition); + Add(spTask); + spTask->Store(); + } + + return spTask; +} + +CTaskPtr CTaskArray::ImportTask(const chcore::TSmartPath& strTaskPath) +{ + // load task definition from the new location + chcore::TTaskDefinition tTaskDefinition; + tTaskDefinition.Load(strTaskPath); + + return CreateTask(tTaskDefinition); +} + +CTaskPtr CTaskArray::CreateEmptyTask() +{ BOOST_ASSERT(m_piFeedbackFactory); if(!m_piFeedbackFactory) return CTaskPtr(); - + chcore::IFeedbackHandler* piHandler = m_piFeedbackFactory->Create(); if(!piHandler) return CTaskPtr(); BOOST_ASSERT(m_stNextSessionUniqueID != NO_TASK_SESSION_UNIQUE_ID); - CTaskPtr spTask(boost::make_shared(piHandler, m_stNextSessionUniqueID++)); + CTaskPtr spTask(new CTask(piHandler, m_stNextSessionUniqueID++)); // NO_TASK_SESSION_UNIQUE_ID is a special value so it should not be used to identify tasks if(m_stNextSessionUniqueID == NO_TASK_SESSION_UNIQUE_ID) @@ -2878,11 +931,11 @@ CTaskPtr CTaskArray::GetAt(size_t nIndex) const { boost::shared_lock lock(m_lock); - + _ASSERTE(nIndex >= 0 && nIndex < m_vTasks.size()); if(nIndex >= m_vTasks.size()) THROW(_t("Invalid argument"), 0, 0, 0); - + return m_vTasks.at(nIndex); } @@ -2902,19 +955,19 @@ break; } } - + return spFoundTask; } size_t CTaskArray::Add(const CTaskPtr& spNewTask) { if(!spNewTask) THROW(_t("Invalid argument"), 0, 0, 0); - + boost::unique_lock lock(m_lock); // here we know load succeeded - spNewTask->SetTaskPath(m_strTasksDir.c_str()); - + spNewTask->SetTaskDirectory(m_pathTasksDir); + m_vTasks.push_back(spNewTask); spNewTask->OnRegisterTask(m_globalStats); @@ -2925,15 +978,15 @@ void CTaskArray::RemoveAt(size_t stIndex, size_t stCount) { boost::unique_lock lock(m_lock); - + _ASSERTE(stIndex >= m_vTasks.size() || stIndex + stCount > m_vTasks.size()); if(stIndex >= m_vTasks.size() || stIndex + stCount > m_vTasks.size()) THROW(_t("Invalid argument"), 0, 0, 0); - + for(std::vector::iterator iterTask = m_vTasks.begin() + stIndex; iterTask != m_vTasks.begin() + stIndex + stCount; ++iterTask) { CTaskPtr& spTask = *iterTask; - + // kill task if needed spTask->KillThread(); @@ -2957,53 +1010,54 @@ { std::vector vTasksToRemove; - m_lock.lock(); - - size_t stIndex = m_vTasks.size(); - while(stIndex--) + // separate scope for locking { - CTaskPtr spTask = m_vTasks.at(stIndex); - - // delete only when the thread is finished - if((spTask->GetStatus(ST_STEP_MASK) == ST_FINISHED || spTask->GetStatus(ST_STEP_MASK) == ST_CANCELLED)) + boost::unique_lock lock(m_lock); + + size_t stIndex = m_vTasks.size(); + while(stIndex--) { - spTask->OnUnregisterTask(); + CTaskPtr spTask = m_vTasks.at(stIndex); - vTasksToRemove.push_back(spTask); - m_vTasks.erase(m_vTasks.begin() + stIndex); + // delete only when the thread is finished + if((spTask->GetTaskState() == eTaskState_Finished || spTask->GetTaskState() == eTaskState_Cancelled)) + { + spTask->OnUnregisterTask(); + + vTasksToRemove.push_back(spTask); + m_vTasks.erase(m_vTasks.begin() + stIndex); + } } } - - m_lock.unlock(); BOOST_FOREACH(CTaskPtr& spTask, vTasksToRemove) { // delete associated files - spTask->DeleteProgress(m_strTasksDir.c_str()); + spTask->DeleteProgress(); } } void CTaskArray::RemoveFinished(const CTaskPtr& spSelTask) { boost::unique_lock lock(m_lock); - + // this might be optimized by copying tasks to a local table in critical section, and then deleting progress files outside of the critical section for(std::vector::iterator iterTask = m_vTasks.begin(); iterTask != m_vTasks.end(); ++iterTask) { CTaskPtr& spTask = *iterTask; - - if(spTask == spSelTask && (spTask->GetStatus(ST_STEP_MASK) == ST_FINISHED || spTask->GetStatus(ST_STEP_MASK) == ST_CANCELLED)) + + if(spTask == spSelTask && (spTask->GetTaskState() == eTaskState_Finished || spTask->GetTaskState() == eTaskState_Cancelled)) { // kill task if needed spTask->KillThread(); spTask->OnUnregisterTask(); // delete associated files - spTask->DeleteProgress(m_strTasksDir.c_str()); - + spTask->DeleteProgress(); + m_vTasks.erase(iterTask); - + return; } } @@ -3012,7 +1066,7 @@ void CTaskArray::StopAllTasks() { boost::unique_lock lock(m_lock); - + StopAllTasksNL(); } @@ -3025,7 +1079,7 @@ BOOST_FOREACH(CTaskPtr& spTask, m_vTasks) { // turn on some thread - find something with wait state - if(spTask->GetStatus(ST_WAITING_MASK) & ST_WAITING && (stMaxRunningTasks == 0 || m_globalStats.GetRunningTasksCount() < stMaxRunningTasks)) + if(spTask->GetTaskState() == eTaskState_Waiting && (stMaxRunningTasks == 0 || m_globalStats.GetRunningTasksCount() < stMaxRunningTasks)) { spTask->m_localStats.MarkTaskAsRunning(); spTask->SetContinueFlagNL(true); @@ -3039,60 +1093,47 @@ boost::shared_lock lock(m_lock); BOOST_FOREACH(CTaskPtr& spTask, m_vTasks) { - spTask->Store(true); + spTask->Store(); } } -void CTaskArray::SaveProgress() -{ - boost::shared_lock lock(m_lock); - BOOST_FOREACH(CTaskPtr& spTask, m_vTasks) - { - spTask->Store(false); - } -} - void CTaskArray::LoadDataProgress() { CFileFind finder; CTaskPtr spTask; - CString strPath; - - BOOL bWorking=finder.FindFile(CString(m_strTasksDir.c_str())+_T("*.atd")); + chcore::TSmartPath pathFound; + + // find all CH Task files + chcore::TSmartPath pathToFind = m_pathTasksDir + chcore::PathFromString(_T("*.cht")); + BOOL bWorking = finder.FindFile(pathToFind.ToString()); while(bWorking) { bWorking = finder.FindNextFile(); - + + pathFound = chcore::PathFromString(finder.GetFilePath()); // load data - spTask = CreateTask(); + spTask = CreateEmptyTask(); try { - strPath = finder.GetFilePath(); - - spTask->Load(strPath, true); - - strPath = strPath.Left(strPath.GetLength() - 4); - strPath += _T(".atp"); - - spTask->Load(strPath, false); - + spTask->Load(pathFound); + // add read task to array Add(spTask); } - catch(std::exception& e) + catch(icpf::exception& e) { + CString strMsg; + e.get_info(strMsg.GetBufferSetLength(65536), 65536); + strMsg.ReleaseBuffer(); + CString strFmt; - strFmt.Format(_T("Cannot load task data: %s (reason: %S)"), strPath, e.what()); + strFmt.Format(_T("Cannot load task data: %s (reason: %s)"), pathFound.ToString(), (PCTSTR)strMsg); LOG_ERROR(strFmt); } - catch(icpf::exception& e) + catch(std::exception& e) { - tchar_t szBuffer[65536]; - e.get_info(szBuffer, 65536); - szBuffer[65535] = _T('\0'); - CString strFmt; - strFmt.Format(_T("Cannot load task data: %s (reason: %s)"), strPath, szBuffer); + strFmt.Format(_T("Cannot load task data: %s (reason: %S)"), pathFound.ToString(), e.what()); LOG_ERROR(strFmt); } } @@ -3135,16 +1176,16 @@ } } -bool CTaskArray::TasksRetryProcessing(bool bOnlyErrors, UINT uiInterval) +bool CTaskArray::TasksRetryProcessing() { boost::shared_lock lock(m_lock); bool bChanged=false; BOOST_FOREACH(CTaskPtr& spTask, m_vTasks) { - if(spTask->RetryProcessing(bOnlyErrors, uiInterval)) + if(spTask->RetryProcessing()) bChanged = true; } - + return bChanged; } @@ -3175,32 +1216,29 @@ bool CTaskArray::AreAllFinished() { bool bFlag=true; - UINT uiStatus; - + if(m_globalStats.GetRunningTasksCount() != 0) bFlag = false; else { boost::shared_lock lock(m_lock); BOOST_FOREACH(CTaskPtr& spTask, m_vTasks) { - uiStatus = spTask->GetStatus(); - bFlag = ((uiStatus & ST_STEP_MASK) == ST_FINISHED || (uiStatus & ST_STEP_MASK) == ST_CANCELLED - || (uiStatus & ST_WORKING_MASK) == ST_PAUSED - || ((uiStatus & ST_WORKING_MASK) == ST_ERROR && !GetConfig().get_bool(PP_CMAUTORETRYONERROR))); + ETaskCurrentState eState = spTask->GetTaskState(); + bFlag = (eState == eTaskState_Finished || eState == eTaskState_Cancelled || eState == eTaskState_Paused || eState == eTaskState_Error); if(!bFlag) break; } } - + return bFlag; } -void CTaskArray::SetTasksDir(const tchar_t* pszPath) +void CTaskArray::SetTasksDir(const chcore::TSmartPath& pathDir) { boost::unique_lock lock(m_lock); - m_strTasksDir = pszPath; + m_pathTasksDir = pathDir; } void CTaskArray::StopAllTasksNL() @@ -3210,7 +1248,7 @@ { spTask->RequestStopThread(); } - + // wait for finishing BOOST_FOREACH(CTaskPtr& spTask, m_vTasks) {