/*========================================================================== * * Copyright (C) 2002 Microsoft Corporation. All Rights Reserved. * * File: dvtimer.pp * Content: Implementation of DvTimer class. * * History: * Date By Reason * ==== == ====== * 05-06-02 simonpow Created * ***************************************************************************/ #include "dxvutilspch.h" #undef DPF_SUBCOMP #define DPF_SUBCOMP DN_SUBCOMP_VOICE #undef DPF_MODNAME #define DPF_MODNAME "DvTimer::DvTimer" DvTimer::DvTimer() { DPFX(DPFPREP, DVF_TRACELEVEL, "Entry"); m_pfnUserCallback=NULL; m_pvUserData=NULL; m_dwPeriod=0; m_pvTimerData=NULL; m_uiTimerUnique=0; DPFX(DPFPREP, DVF_INFOLEVEL, "DvTimer object create at 0x%p", this); DPFX(DPFPREP, DVF_TRACELEVEL, "Exit"); } #undef DPF_MODNAME #define DPF_MODNAME "DvTimer::~DvTimer" DvTimer::~DvTimer() { DPFX(DPFPREP, DVF_TRACELEVEL, "Entry"); //if we've actually created a timer and got a thread pool interface if (m_pThreadPool) { HRESULT hr; //If we're in the middle of the callback we'll not be able to cancel the timer //hence spin until we do (since its rescheduled at the end of every callback) DPFX(DPFPREP, DVF_INFOLEVEL, "Starting cancel loop"); DNASSERT(m_pvTimerData); //we don't want to be in the situation where the timer keeps getting rescheduled and we keep //missing it. i.e. Constantly waking up in the period its active rather than the period its scheduled //hence, set the period to a high value to ensure the next time it fires (if at all) it will be 24hrs away m_dwPeriod=1000*60*60*24; while (1) { hr=IDirectPlay8ThreadPoolWork_CancelTimer(m_pThreadPool, m_pvTimerData, m_uiTimerUnique, 0); if (hr==DPN_OK) break; DNASSERT(hr==DPNERR_CANNOTCANCEL); Sleep(DvTimer_SleepPeriodInCancelSpin); } IDirectPlay8ThreadPoolWork_Release(m_pThreadPool); } DPFX(DPFPREP, DVF_INFOLEVEL, "DvTimer destroyed at 0x%p", this); DPFX(DPFPREP, DVF_TRACELEVEL, "Exit"); } #undef DPF_MODNAME #define DPF_MODNAME "DvTimer::Create" BOOL DvTimer::Create (DWORD dwPeriod, void * pvUserData, DvTimerCallback pfnCallback) { DPFX(DPFPREP, DVF_TRACELEVEL, "Entry dwPeriod %u pvUserData 0x%p pfnCallback 0x%p", dwPeriod, pvUserData, pfnCallback); //sanity checks DNASSERT(pfnCallback); DNASSERT(dwPeriod); //store state user specifies for timer m_pfnUserCallback=pfnCallback; m_pvUserData=pvUserData; m_dwPeriod=dwPeriod; //get a thread pool interface. Since the thread pool is a singleton object, this probably won't //actually do the creation HRESULT hr=CoCreateInstance(CLSID_DirectPlay8ThreadPool, NULL, CLSCTX_INPROC_SERVER, IID_IDirectPlay8ThreadPoolWork, (void **) &m_pThreadPool); if (FAILED(hr)) { DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to CoCreate CLSID_DirectPlay8ThreadPool hr 0x%x", hr); return FALSE; } //schedule the first timer hr=IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pThreadPool, -1, dwPeriod, ThreadpoolTimerCallbackStatic, this, &m_pvTimerData, (UINT* ) &m_uiTimerUnique, 0); if (FAILED(hr)) { //need to return state to 'uncreated', so we don't do any clean up in the d'tor IDirectPlay8ThreadPoolWork_Release(m_pThreadPool); m_pThreadPool=NULL; DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to schedule timer hr 0x%x", hr); return FALSE; } DPFX(DPFPREP, DVF_INFOLEVEL, "DvTimer create success m_pvTimerData 0x%p m_uiTimerUnique 0x%p", m_pvTimerData, m_uiTimerUnique); //need to ensure that at least one thread is around to service our timer IDirectPlay8ThreadPoolWork_RequestTotalThreadCount(m_pThreadPool, 1, 0); DPFX(DPFPREP, DVF_TRACELEVEL, "Exit"); return TRUE; } #undef DPF_MODNAME #define DPF_MODNAME "DvTimer::ThreadpoolTimerCallbackStatic" void DvTimer::ThreadpoolTimerCallbackStatic(void * const pvContext, void * const pvTimerData, const UINT uiTimerUnique) { DPFX(DPFPREP, DVF_TRACELEVEL, "Entry pvContext 0x%p pvTimerData 0x%p uiTimerUnique %u", pvContext, pvTimerData, uiTimerUnique); //extract the timer object from the context DvTimer * pTimer=(DvTimer * ) pvContext; //and store the time we started the callback DWORD dwStartTime=GETTIMESTAMP(); //generate the callback to the user (*pTimer->m_pfnUserCallback)(pTimer->m_pvUserData); //compute the period for the next timer, based on the required period minus //the time that elasped doing the actual work //This ensures that the user is called at periods as close to m_dwPeriod as possible DWORD dwPeriod=pTimer->m_dwPeriod-(GETTIMESTAMP()-dwStartTime); //if the new period is in the past (i.e. we spent so long in the callback we are due another one //immediately) then set the minimum period for the next callback if (((int ) dwPeriod)<0) dwPeriod=1; //N.B. We don't pass m_dwTimerUnique direct to the Reset timer function, since we could be using //the value in a cancel spin. Hence, we wait until the timer has definitely been rescheduled //before storing the new unique value for it. UINT uiNextTimerUnique; IDirectPlay8ThreadPoolWork_ResetCompletingTimer(pTimer->m_pThreadPool, pvTimerData, dwPeriod, ThreadpoolTimerCallbackStatic, pTimer, &uiNextTimerUnique, 0); pTimer->m_uiTimerUnique=uiNextTimerUnique; DPFX(DPFPREP, DVF_TRACELEVEL, "Exit"); }