Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

356 lines
8.8 KiB

  1. // powercopy.cpp : Defines the entry point for the console application.
  2. //
  3. #include <istream>
  4. #include <strstream>
  5. #include <sstream>
  6. #include <string>
  7. #include <iostream>
  8. #include <queue>
  9. #include <vector>
  10. #include <utility>
  11. #include "windows.h"
  12. #include "winbase.h"
  13. using namespace std;
  14. typedef __int64 BigInteger;
  15. void AssertionFailed(
  16. PCSTR Function,
  17. int Line,
  18. PCSTR Statement
  19. )
  20. {
  21. stringstream ss;
  22. ss << "Assertion failed: " << Function << " [line " << Line << "]: " << Statement;
  23. OutputDebugStringA( ss.str().c_str() );
  24. }
  25. #define ASSERT( x ) do { bool __x = (x); if ( !__x ) { AssertionFailed( __FUNCTION__, __LINE__, #x ); } } while ( 0 )
  26. class CLockCriticalSection;
  27. class CCriticalSection : private CRITICAL_SECTION
  28. {
  29. CCriticalSection( const CCriticalSection& );
  30. CCriticalSection& operator=( const CCriticalSection& );
  31. friend CLockCriticalSection;
  32. public:
  33. CCriticalSection() { InitializeCriticalSection(this); }
  34. ~CCriticalSection() { DeleteCriticalSection(this); }
  35. };
  36. class CLockCriticalSection
  37. {
  38. bool m_fLocked;
  39. CCriticalSection& m_CriticalSection;
  40. public:
  41. CLockCriticalSection(
  42. CCriticalSection& toLock,
  43. bool bTakeOnConstruction = true
  44. ) : m_fLocked( false ), m_CriticalSection( toLock )
  45. {
  46. if ( bTakeOnConstruction ) Lock();
  47. }
  48. ~CLockCriticalSection() { if ( m_fLocked ) Unlock(); }
  49. bool TryEnter();
  50. void Lock() {
  51. ASSERT( !m_fLocked );
  52. LeaveCriticalSection(&m_CriticalSection);
  53. }
  54. void Unlock() {
  55. ASSERT( m_fLocked );
  56. LeaveCriticalSection(&m_CriticalSection);
  57. }
  58. };
  59. class CopyJobEntry :
  60. public pair<wstring, wstring>,
  61. public SINGLE_LIST_ENTRY
  62. {
  63. public:
  64. CopyJobEntry() : m_FileSize( 0 ), m_bCopied( false ), m_dwCopyStatus( 0 ) { }
  65. BigInteger m_FileSize;
  66. bool m_bCopied;
  67. DWORD m_dwCopyStatus;
  68. private:
  69. CopyJobEntry( const CopyJobEntry& );
  70. CopyJobEntry& operator=(const CopyJobEntry&);
  71. };
  72. class CCopyJobResultHolder : public vector<CopyJobEntry*>
  73. {
  74. private:
  75. typedef vector<CopyJobEntry*> Super;
  76. public:
  77. virtual void clear() {
  78. for ( const_iterator i = begin(); i != end(); i++ ) delete *i;
  79. Super::clear();
  80. }
  81. virtual ~CCopyJobResultHolder() { clear(); }
  82. };
  83. class CopyJobWorker;
  84. class CopyJobManager
  85. {
  86. private:
  87. CopyJobManager( const CopyJobManager& );
  88. CopyJobManager& operator=(const CopyJobManager&);
  89. vector<CopyJobWorker*> m_Workers;
  90. bool m_fFoundEndOfCopyList;
  91. bool m_fValidatingCopies;
  92. int m_iReportGranularity;
  93. wostream m_ErrorStream, m_ReportStream;
  94. CCriticalSection m_ErrorStreamLock, m_ReportStreamLock;
  95. public:
  96. CopyJobManager( bool fValidate, int iReportInGranularity, int iWorkerCount, wostream&, wostream& );
  97. enum PossibleCopyErrors {
  98. eCopyFileFailure,
  99. eGetFileAttributesFailure
  100. };
  101. bool SetStreams( wostream Errors, wostream Reports );
  102. bool NoMoreCopyJobs() { return m_fFoundEndOfCopyList; }
  103. bool ReportError( PossibleCopyErrors Errors, const CopyJobEntry *pEntry );
  104. bool Report( CopyJobWorker& Worker, const CCopyJobResultHolder& holder );
  105. bool ValidateCopies() { return m_fValidatingCopies; }
  106. int ReportGranularity() { return m_iReportGranularity; }
  107. };
  108. class CopyJobWorker
  109. {
  110. private:
  111. SLIST_HEADER m_List;
  112. HANDLE m_hAnotherItemOnTheList;
  113. BigInteger m_TotalFilesCopied, m_TotalBytesCopied;
  114. CopyJobManager &m_Owner;
  115. public:
  116. CopyJobWorker( CopyJobManager& owner );
  117. static DWORD WINAPI StartCopyThread( PVOID pWorkerObject );
  118. bool AddCopyJob( CopyJobEntry* pNewEntry );
  119. void SignalCompleted();
  120. const BigInteger GetFilesCopied() { return m_TotalFilesCopied; }
  121. const BigInteger GetBytesCopied() { return m_TotalBytesCopied; }
  122. protected:
  123. DWORD StartCopyingItems();
  124. bool WaitForAnotherJob( CopyJobEntry *&pNextJob );
  125. private:
  126. CopyJobWorker( const CopyJobWorker& );
  127. CopyJobWorker& operator=(const CopyJobWorker&);
  128. };
  129. /*
  130. wostream& operator<<( const wostream& out, const BigInteger &bi )
  131. {
  132. return out << (double)bi;
  133. }
  134. */
  135. DWORD
  136. CopyJobWorker::StartCopyThread( PVOID pWorkerObject )
  137. {
  138. DWORD dwResult = 0;
  139. __try
  140. {
  141. dwResult = ((CopyJobWorker*)pWorkerObject)->StartCopyingItems();
  142. }
  143. __except( EXCEPTION_EXECUTE_HANDLER )
  144. {
  145. dwResult = 0;
  146. }
  147. return dwResult;
  148. }
  149. DWORD
  150. CopyJobWorker::StartCopyingItems()
  151. {
  152. CopyJobEntry *pNextJob = NULL;
  153. WIN32_FILE_ATTRIBUTE_DATA DestInfo;
  154. CCopyJobResultHolder JobsSoFar;
  155. DWORD dwLastCopyStatus = 0;
  156. BOOL bStatus;
  157. while ( WaitForAnotherJob( pNextJob ) )
  158. {
  159. if ( ( pNextJob == NULL ) && ( m_Owner.NoMoreCopyJobs() ) )
  160. {
  161. break;
  162. }
  163. // We should not get here by the way WaitForAnoterJob works, but ohwell,
  164. // try again anyhow.
  165. else if ( pNextJob == NULL )
  166. {
  167. continue;
  168. }
  169. dwLastCopyStatus = 0;
  170. bStatus = CopyFileExW(
  171. pNextJob->first.c_str(),
  172. pNextJob->second.c_str(),
  173. NULL,
  174. this,
  175. NULL,
  176. 0
  177. );
  178. dwLastCopyStatus = pNextJob->m_dwCopyStatus = ::GetLastError();
  179. // If there was an error, report it to the manager, then continue waiting for
  180. // more copies to be ready.
  181. if ( !bStatus )
  182. {
  183. m_Owner.ReportError( CopyJobManager::eCopyFileFailure, pNextJob );
  184. continue;
  185. }
  186. // This is a little bookkeeping work, as well as being a fallback line of
  187. // defense for knowing whether or not the copy was successful.
  188. bStatus = GetFileAttributesEx( pNextJob->second.c_str(), GetFileExInfoStandard, &DestInfo );
  189. if ( !bStatus )
  190. {
  191. pNextJob->m_dwCopyStatus = ::GetLastError();
  192. m_Owner.ReportError( CopyJobManager::eGetFileAttributesFailure, pNextJob );
  193. continue;
  194. }
  195. LARGE_INTEGER liTemp;
  196. liTemp.HighPart = DestInfo.nFileSizeHigh;
  197. liTemp.LowPart = DestInfo.nFileSizeLow;
  198. m_TotalFilesCopied++;
  199. m_TotalBytesCopied += liTemp.QuadPart;
  200. pNextJob->m_FileSize = liTemp.QuadPart;
  201. pNextJob->m_bCopied = true;
  202. // Add this job to the list of those that we have completed - if we should
  203. // be telling our manager, then go and let him know.
  204. JobsSoFar.push_back( pNextJob );
  205. if ( JobsSoFar.size() > m_Owner.ReportGranularity() )
  206. {
  207. // After reporting, feel free to delete all the copy jobs that were
  208. // so far outstanding on the object.
  209. m_Owner.Report( *this, JobsSoFar );
  210. JobsSoFar.clear();
  211. }
  212. }
  213. return dwLastCopyStatus;
  214. }
  215. CopyJobWorker::CopyJobWorker(CopyJobManager &owner)
  216. : m_Owner( owner ), m_TotalFilesCopied( 0 ), m_TotalBytesCopied( 0 ),
  217. m_hAnotherItemOnTheList( INVALID_HANDLE_VALUE )
  218. {
  219. ::InitializeSListHead(&m_List);
  220. m_hAnotherItemOnTheList = ::CreateEventW( NULL, TRUE, FALSE, NULL );
  221. }
  222. bool
  223. CopyJobWorker::AddCopyJob(
  224. CopyJobEntry *pAnotherJob
  225. )
  226. {
  227. ::InterlockedPushEntrySList(&m_List, pAnotherJob);
  228. return !!SetEvent(m_hAnotherItemOnTheList);
  229. }
  230. void CopyJobWorker::SignalCompleted() { SetEvent(m_hAnotherItemOnTheList); }
  231. bool
  232. CopyJobWorker::WaitForAnotherJob(
  233. CopyJobEntry * & pNextJob
  234. )
  235. {
  236. pNextJob = (CopyJobEntry*)InterlockedPopEntrySList(&m_List);
  237. // Keep looking until we're either out of jobs, or we get something.
  238. while ( !pNextJob && !m_Owner.NoMoreCopyJobs() )
  239. {
  240. WaitForSingleObject( m_hAnotherItemOnTheList, INFINITE );
  241. pNextJob = (CopyJobEntry*)InterlockedPopEntrySList(&m_List);
  242. if ( pNextJob ) ResetEvent( m_hAnotherItemOnTheList );
  243. }
  244. // Return true if we actually got a job... false is returned if there
  245. // were no more items to be gotten from our master.
  246. return pNextJob != NULL;
  247. }
  248. CopyJobManager::CopyJobManager(
  249. bool fValidate,
  250. int iReportInGranularity,
  251. int iWorkerCount,
  252. wostream& ErrorStream,
  253. wostream& ReportStream
  254. ) : m_fFoundEndOfCopyList(false), m_iReportGranularity(iReportInGranularity),
  255. m_fValidatingCopies(fValidate), m_ErrorStream(ErrorStream), m_ReportStream(ReportStream)
  256. {
  257. }
  258. bool
  259. CopyJobManager::ReportError(
  260. CopyJobManager::PossibleCopyErrors pce,
  261. const CopyJobEntry *pEntry
  262. )
  263. {
  264. return true;
  265. }
  266. bool
  267. CopyJobManager::Report(
  268. CopyJobWorker& Worker,
  269. const CCopyJobResultHolder& holder
  270. )
  271. {
  272. wstringstream ss;
  273. bool fResult;
  274. ss << hex << GetCurrentThreadId() << L": "
  275. << (double)Worker.GetBytesCopied()
  276. << L" bytes in "
  277. << (double)Worker.GetFilesCopied()
  278. << L" files" << endl;
  279. {
  280. CLockCriticalSection Locker(m_ReportStreamLock, true);
  281. m_ReportStream << ss.str();
  282. }
  283. return true;
  284. }
  285. void __cdecl wmain( int argc, wchar_t **argv )
  286. {
  287. // TODO: Make this work right
  288. }