Leaked source code of windows server 2003
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.

156 lines
5.4 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 2002 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: dvtimer.pp
  6. * Content: Implementation of DvTimer class.
  7. *
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 05-06-02 simonpow Created
  12. *
  13. ***************************************************************************/
  14. #include "dxvutilspch.h"
  15. #undef DPF_SUBCOMP
  16. #define DPF_SUBCOMP DN_SUBCOMP_VOICE
  17. #undef DPF_MODNAME
  18. #define DPF_MODNAME "DvTimer::DvTimer"
  19. DvTimer::DvTimer()
  20. {
  21. DPFX(DPFPREP, DVF_TRACELEVEL, "Entry");
  22. m_pfnUserCallback=NULL;
  23. m_pvUserData=NULL;
  24. m_dwPeriod=0;
  25. m_pvTimerData=NULL;
  26. m_uiTimerUnique=0;
  27. DPFX(DPFPREP, DVF_INFOLEVEL, "DvTimer object create at 0x%p", this);
  28. DPFX(DPFPREP, DVF_TRACELEVEL, "Exit");
  29. }
  30. #undef DPF_MODNAME
  31. #define DPF_MODNAME "DvTimer::~DvTimer"
  32. DvTimer::~DvTimer()
  33. {
  34. DPFX(DPFPREP, DVF_TRACELEVEL, "Entry");
  35. //if we've actually created a timer and got a thread pool interface
  36. if (m_pThreadPool)
  37. {
  38. HRESULT hr;
  39. //If we're in the middle of the callback we'll not be able to cancel the timer
  40. //hence spin until we do (since its rescheduled at the end of every callback)
  41. DPFX(DPFPREP, DVF_INFOLEVEL, "Starting cancel loop");
  42. DNASSERT(m_pvTimerData);
  43. //we don't want to be in the situation where the timer keeps getting rescheduled and we keep
  44. //missing it. i.e. Constantly waking up in the period its active rather than the period its scheduled
  45. //hence, set the period to a high value to ensure the next time it fires (if at all) it will be 24hrs away
  46. m_dwPeriod=1000*60*60*24;
  47. while (1)
  48. {
  49. hr=IDirectPlay8ThreadPoolWork_CancelTimer(m_pThreadPool, m_pvTimerData, m_uiTimerUnique, 0);
  50. if (hr==DPN_OK)
  51. break;
  52. DNASSERT(hr==DPNERR_CANNOTCANCEL);
  53. Sleep(DvTimer_SleepPeriodInCancelSpin);
  54. }
  55. IDirectPlay8ThreadPoolWork_Release(m_pThreadPool);
  56. }
  57. DPFX(DPFPREP, DVF_INFOLEVEL, "DvTimer destroyed at 0x%p", this);
  58. DPFX(DPFPREP, DVF_TRACELEVEL, "Exit");
  59. }
  60. #undef DPF_MODNAME
  61. #define DPF_MODNAME "DvTimer::Create"
  62. BOOL DvTimer::Create (DWORD dwPeriod, void * pvUserData, DvTimerCallback pfnCallback)
  63. {
  64. DPFX(DPFPREP, DVF_TRACELEVEL, "Entry dwPeriod %u pvUserData 0x%p pfnCallback 0x%p",
  65. dwPeriod, pvUserData, pfnCallback);
  66. //sanity checks
  67. DNASSERT(pfnCallback);
  68. DNASSERT(dwPeriod);
  69. //store state user specifies for timer
  70. m_pfnUserCallback=pfnCallback;
  71. m_pvUserData=pvUserData;
  72. m_dwPeriod=dwPeriod;
  73. //get a thread pool interface. Since the thread pool is a singleton object, this probably won't
  74. //actually do the creation
  75. HRESULT hr=CoCreateInstance(CLSID_DirectPlay8ThreadPool, NULL, CLSCTX_INPROC_SERVER,
  76. IID_IDirectPlay8ThreadPoolWork, (void **) &m_pThreadPool);
  77. if (FAILED(hr))
  78. {
  79. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to CoCreate CLSID_DirectPlay8ThreadPool hr 0x%x", hr);
  80. return FALSE;
  81. }
  82. //schedule the first timer
  83. hr=IDirectPlay8ThreadPoolWork_ScheduleTimer(m_pThreadPool, -1,
  84. dwPeriod, ThreadpoolTimerCallbackStatic, this, &m_pvTimerData, (UINT* ) &m_uiTimerUnique, 0);
  85. if (FAILED(hr))
  86. {
  87. //need to return state to 'uncreated', so we don't do any clean up in the d'tor
  88. IDirectPlay8ThreadPoolWork_Release(m_pThreadPool);
  89. m_pThreadPool=NULL;
  90. DPFX(DPFPREP, DVF_ERRORLEVEL, "Failed to schedule timer hr 0x%x", hr);
  91. return FALSE;
  92. }
  93. DPFX(DPFPREP, DVF_INFOLEVEL, "DvTimer create success m_pvTimerData 0x%p m_uiTimerUnique 0x%p",
  94. m_pvTimerData, m_uiTimerUnique);
  95. //need to ensure that at least one thread is around to service our timer
  96. IDirectPlay8ThreadPoolWork_RequestTotalThreadCount(m_pThreadPool, 1, 0);
  97. DPFX(DPFPREP, DVF_TRACELEVEL, "Exit");
  98. return TRUE;
  99. }
  100. #undef DPF_MODNAME
  101. #define DPF_MODNAME "DvTimer::ThreadpoolTimerCallbackStatic"
  102. void DvTimer::ThreadpoolTimerCallbackStatic(void * const pvContext,
  103. void * const pvTimerData, const UINT uiTimerUnique)
  104. {
  105. DPFX(DPFPREP, DVF_TRACELEVEL, "Entry pvContext 0x%p pvTimerData 0x%p uiTimerUnique %u",
  106. pvContext, pvTimerData, uiTimerUnique);
  107. //extract the timer object from the context
  108. DvTimer * pTimer=(DvTimer * ) pvContext;
  109. //and store the time we started the callback
  110. DWORD dwStartTime=GETTIMESTAMP();
  111. //generate the callback to the user
  112. (*pTimer->m_pfnUserCallback)(pTimer->m_pvUserData);
  113. //compute the period for the next timer, based on the required period minus
  114. //the time that elasped doing the actual work
  115. //This ensures that the user is called at periods as close to m_dwPeriod as possible
  116. DWORD dwPeriod=pTimer->m_dwPeriod-(GETTIMESTAMP()-dwStartTime);
  117. //if the new period is in the past (i.e. we spent so long in the callback we are due another one
  118. //immediately) then set the minimum period for the next callback
  119. if (((int ) dwPeriod)<0)
  120. dwPeriod=1;
  121. //N.B. We don't pass m_dwTimerUnique direct to the Reset timer function, since we could be using
  122. //the value in a cancel spin. Hence, we wait until the timer has definitely been rescheduled
  123. //before storing the new unique value for it.
  124. UINT uiNextTimerUnique;
  125. IDirectPlay8ThreadPoolWork_ResetCompletingTimer(pTimer->m_pThreadPool, pvTimerData, dwPeriod,
  126. ThreadpoolTimerCallbackStatic, pTimer, &uiNextTimerUnique, 0);
  127. pTimer->m_uiTimerUnique=uiNextTimerUnique;
  128. DPFX(DPFPREP, DVF_TRACELEVEL, "Exit");
  129. }