#pragma once class CStressJobManager; class CBarrier { CEvent m_hBarrierEvent; LONG m_lBarrierSize; LONG m_lWaitingThreads; PRIVATIZE_COPY_CONSTRUCTORS(CBarrier) public: CBarrier() : m_lBarrierSize(0), m_lWaitingThreads(0) { } // // Start up, with the given barrier size and name (if any) // BOOL Initialize( DWORD lBarrierSize, PCWSTR pcwszBarrierName = NULL ) { FN_PROLOG_WIN32 m_lBarrierSize = lBarrierSize; m_lWaitingThreads = 0; IFW32FALSE_EXIT(m_hBarrierEvent.Win32CreateEvent(TRUE, FALSE, pcwszBarrierName)); FN_EPILOG } // // This thread is to join the barrier. It's possible that this call will be // the one that "breaks" the barrier.. // BOOL WaitForBarrier() { FN_PROLOG_WIN32 LONG LatestValue = ::InterlockedIncrement(&m_lWaitingThreads); if ( LatestValue >= m_lBarrierSize ) { IFW32FALSE_EXIT(::SetEvent(m_hBarrierEvent)); } else { IFW32FALSE_EXIT(::WaitForSingleObject(m_hBarrierEvent, INFINITE) == WAIT_OBJECT_0); } FN_EPILOG } // // In case someone wants to wait on us without actually joining in to count // for breaking the barrier (why??) // BOOL WaitForBarrierNoJoin() { FN_PROLOG_WIN32 IFW32FALSE_EXIT(::WaitForSingleObject(m_hBarrierEvent, INFINITE) == WAIT_OBJECT_0); FN_EPILOG } // // A thread has a reason to break the barrier early? Fine, let them. // BOOL EarlyRelease() { FN_PROLOG_WIN32 IFW32FALSE_EXIT(::SetEvent(m_hBarrierEvent)); FN_EPILOG } }; class CStressJobEntry { DWORD InternalThreadProc(); BOOL WaitForStartingGun(); PRIVATIZE_COPY_CONSTRUCTORS(CStressJobEntry); public: CDequeLinkage m_dlLinkage; CThread m_hThread; bool m_fStop; ULONG m_ulRuns; ULONG m_ulFailures; DWORD m_dwSleepBetweenRuns; CStringBuffer m_buffTestName; CStringBuffer m_buffTestDirectory; CStressJobManager *m_pManager; // // Override these three to provide functionality // virtual BOOL RunTest( bool &rfTestPasses ) = 0; virtual BOOL SetupSelfForRun() = 0; virtual BOOL Cleanup(); virtual BOOL LoadFromSettingsFile( PCWSTR pcwszSettingsFile ); // // These are not to be overridden! // BOOL Stop( BOOL fWaitForCompletion = TRUE ); BOOL WaitForCompletion(); static DWORD ThreadProc( PVOID pv ); CStressJobEntry( CStressJobManager *pManager ); virtual ~CStressJobEntry(); }; typedef CDeque CStressEntryDeque; typedef CDequeIterator CStressEntryDequeIterator; class CStressJobManager { PRIVATIZE_COPY_CONSTRUCTORS(CStressJobManager); friend CStressJobEntry; CEvent m_hStartingGunEvent; ULONG m_ulThreadsCreated; ULONG m_ulThreadsReady; BOOL SignalAnotherJobReady(); BOOL SignalThreadWorking(); BOOL SignalThreadDone(); BOOL WaitForStartEvent(); public: CStressEntryDeque m_JobsListed; ULONG m_ulThreadsWorking; BOOL LoadFromDirectory( PCWSTR pcwszDirectoryName, PULONG pulJobsFound = NULL ); BOOL CreateWorkerThreads( PULONG pulThreadsCreated = NULL ); BOOL StopJobs( BOOL fWithWaitForComplete = TRUE ); BOOL CleanupJobs(); BOOL StartJobs(); BOOL WaitForAllJobsComplete(); // // This returns the directory name that the manager will use to find // data files/directories. // virtual PCWSTR GetGroupName() = 0; virtual PCWSTR GetIniFileName() = 0; CStressJobManager(); ~CStressJobManager(); protected: virtual BOOL CreateJobEntry( CStressJobEntry* &pJobEntry ) = 0; };