1 /*++ 2 3 Copyright (c) Microsoft Corporation 4 5 ModuleName: 6 7 MxTimerUm.h 8 9 Abstract: 10 11 User mode implementation of timer defined in 12 MxTimer.h 13 14 Author: 15 16 17 Revision History: 18 19 20 21 --*/ 22 23 #pragma once 24 25 typedef struct _MdTimer { 26 // 27 // Callback function to be invoked upon timer expiration and the context to 28 // be passed in to the callback function 29 // 30 MdDeferredRoutine m_TimerCallback; 31 PVOID m_TimerContext; 32 33 // 34 // The timer period 35 // 36 LONG m_Period; 37 38 // 39 // Handle to the timer object 40 // 41 PTP_TIMER m_TimerHandle; 42 43 // 44 // Work object to be executed by threadpool upon timer expiration 45 // 46 PTP_WORK m_WorkObject; 47 48 // 49 // Flag to indicate that the timer callback has started running 50 // 51 BOOL m_CallbackStartedRunning; 52 53 // 54 // Flag to indicate that the timer was started 55 // since it was created or since it was last stopped. 56 // 57 BOOL m_TimerWasStarted; 58 59 _MdTimer( 60 VOID 61 ) 62 { 63 m_TimerHandle = NULL; 64 m_WorkObject = NULL; 65 m_TimerCallback = NULL; 66 m_TimerContext = NULL; 67 m_Period = 0; 68 m_CallbackStartedRunning = FALSE; 69 m_TimerWasStarted = FALSE; 70 } 71 72 ~_MdTimer( 73 VOID 74 ) 75 { 76 // 77 // Release the timer object 78 // 79 if (m_TimerHandle) 80 { 81 CloseThreadpoolTimer(m_TimerHandle); 82 m_TimerHandle = NULL; 83 } 84 85 // 86 // Release the work object 87 // 88 if (m_WorkObject) 89 { 90 CloseThreadpoolWork(m_WorkObject); 91 m_WorkObject = NULL; 92 } 93 } 94 95 BOOLEAN 96 IsInSystemQueue( 97 VOID 98 ) 99 { 100 // 101 // Timer was not started since it was created or since 102 // it was last stopped, so it can't be in the system timer queue. 103 // 104 if (!m_TimerWasStarted) { 105 return FALSE; 106 } 107 108 // 109 // Periodic timers are always in the system timer queue. 110 // 111 if (m_Period != 0) { 112 return TRUE; 113 } 114 115 // 116 // Non-periodic timer: 117 // 118 // At this point, the timer callback function has either been canceled or 119 // has finished running. Examine the m_CallbackStartedRunning value to see 120 // which one of these happened. 121 // 122 if (m_CallbackStartedRunning) 123 { 124 // 125 // Timer cancellation was too late. Timer callback already started 126 // running and the timer was removed from the system timer queue. 127 // 128 return FALSE; 129 } 130 else 131 { 132 // 133 // Timer cancellation happened on time and prevented the timer callback 134 // from running. 135 // 136 return TRUE; 137 } 138 } 139 140 _Must_inspect_result_ 141 NTSTATUS 142 Initialize( 143 __in_opt PVOID TimerContext, 144 __in MdDeferredRoutine TimerCallback, 145 __in LONG Period 146 ) 147 { 148 NTSTATUS ntStatus = STATUS_SUCCESS; 149 150 m_TimerCallback = TimerCallback; 151 m_TimerContext = TimerContext; 152 m_Period = Period; 153 154 // 155 // Create the timer object 156 // 157 m_TimerHandle = CreateThreadpoolTimer(_MdTimer::s_MdTimerCallback, 158 this, 159 NULL); 160 if (NULL == m_TimerHandle) 161 { 162 ntStatus = WinErrorToNtStatus(GetLastError()); 163 } 164 165 // 166 // Create the work object 167 // 168 if (NT_SUCCESS(ntStatus)) 169 { 170 m_WorkObject = CreateThreadpoolWork(_MdTimer::s_MdWorkCallback, 171 this, 172 NULL); 173 if (NULL == m_WorkObject) 174 { 175 ntStatus = WinErrorToNtStatus(GetLastError()); 176 } 177 } 178 179 return ntStatus; 180 } 181 182 BOOLEAN 183 Start( 184 __in LARGE_INTEGER DueTime, 185 __in ULONG TolerableDelay 186 ) 187 { 188 BOOLEAN bRetVal; 189 FILETIME dueFileTime; 190 191 if (m_TimerWasStarted) { 192 // 193 // Cancel the previously pended timer callback, 194 // we want it to execute after a full period elapsed. 195 // 196 WaitForThreadpoolTimerCallbacks(m_TimerHandle, TRUE); 197 } 198 199 // 200 // Return TRUE if the timer is in the system timer queue. 201 // 202 bRetVal = IsInSystemQueue(); 203 204 // 205 // This is a fresh start for the timer, so clear the flag that the 206 // timer callback function may have previously set. 207 // 208 m_CallbackStartedRunning = FALSE; 209 210 // 211 // Set the timer started flag. 212 // 213 m_TimerWasStarted = TRUE; 214 215 // 216 // Copy the due time into a FILETIME structure 217 // 218 dueFileTime.dwLowDateTime = DueTime.LowPart; 219 dueFileTime.dwHighDateTime = (DWORD) DueTime.HighPart; 220 221 // 222 // Start the timer 223 // 224 SetThreadpoolTimer(m_TimerHandle, 225 &dueFileTime, 226 (DWORD) m_Period, 227 TolerableDelay); 228 229 return bRetVal; 230 } 231 232 _Must_inspect_result_ 233 BOOLEAN 234 Stop( 235 VOID 236 ) 237 { 238 BOOLEAN bRetVal; 239 240 bRetVal = IsInSystemQueue(); 241 242 // 243 // Stop the timer 244 // 245 SetThreadpoolTimer(m_TimerHandle, 246 NULL, // pftDueTime 247 0, // msPeriod 248 0 // msWindowLength 249 ); 250 251 // 252 // Cancel pending callbacks that have not yet started to execute and wait 253 // for outstanding callbacks to complete. 254 // 255 WaitForThreadpoolTimerCallbacks(m_TimerHandle, 256 TRUE // cancel pending callbacks 257 ); 258 259 // 260 // Reset the timer started flag. 261 // 262 m_TimerWasStarted = FALSE; 263 264 return bRetVal; 265 } 266 267 VOID 268 TimerCallback( 269 VOID 270 ) 271 { 272 // 273 // Invoke the user's callback function 274 // 275 m_TimerCallback(NULL, /* Reserved1 */ 276 m_TimerContext, 277 NULL, /* Reserved2 */ 278 NULL /* Reserved3 */ 279 ); 280 281 return; 282 } 283 284 static 285 VOID CALLBACK 286 s_MdWorkCallback( 287 __inout PTP_CALLBACK_INSTANCE Instance, 288 __inout_opt PVOID Context, 289 __inout PTP_WORK Work 290 ) 291 { 292 struct _MdTimer *pThis = NULL; 293 294 UNREFERENCED_PARAMETER(Instance); 295 UNREFERENCED_PARAMETER(Work); 296 297 pThis = (struct _MdTimer*) Context; 298 pThis->TimerCallback(); 299 300 return; 301 } 302 303 static 304 VOID CALLBACK 305 s_MdTimerCallback( 306 __inout PTP_CALLBACK_INSTANCE Instance, 307 __inout_opt PVOID Context, 308 __inout PTP_TIMER Timer 309 ) 310 { 311 struct _MdTimer *pThis = NULL; 312 313 UNREFERENCED_PARAMETER(Instance); 314 UNREFERENCED_PARAMETER(Timer); 315 316 pThis = (struct _MdTimer*) Context; 317 318 // 319 // First, indicate that the callback has started running 320 // 321 pThis->m_CallbackStartedRunning = TRUE; 322 323 // 324 // Post a work object to execute the callback function supplied by the 325 // user of MxTimer. 326 // 327 // We do not execute the user-supplied callback here because we could 328 // run into a deadlock if the user is trying to cancel the timer by 329 // calling MxTimer::Stop. MxTimer::Stop actually blocks waiting for 330 // MdTimer::s_MdTimerCallback to finish executing, so that it can know 331 // where its attempt to cancel the timer was successful. If we were to 332 // execute the user's callback in MdTimer::s_MdTimerCallback, the user 333 // would have to be careful not to call MxTimer::Stop while holding a 334 // lock that the user's callback tries to acquire. In order to avoid 335 // imposing such a restriction on the user, we allow 336 // MdTimer::s_MdTimerCallback to return immediately after posting a 337 // work object to run the user's callback. 338 // 339 SubmitThreadpoolWork(pThis->m_WorkObject); 340 341 return; 342 } 343 344 BOOLEAN 345 StartWithReturn( 346 __in LARGE_INTEGER DueTime, 347 __in ULONG TolerableDelay 348 ) 349 { 350 return Start(DueTime, TolerableDelay); 351 } 352 } MdTimer; 353 354 #include "MxTimer.h" 355 356 // 357 // Implementation of MxTimer functions 358 // 359 MxTimer::MxTimer( 360 VOID 361 ) 362 { 363 } 364 365 MxTimer::~MxTimer( 366 VOID 367 ) 368 { 369 } 370 371 _Must_inspect_result_ 372 NTSTATUS 373 MxTimer::Initialize( 374 __in_opt PVOID TimerContext, 375 __in MdDeferredRoutine TimerCallback, 376 __in LONG Period 377 ) 378 /*++ 379 Routine description: 380 Initializes the MxTimer object. 381 382 Arguments: 383 TimerContext - Context information that will be passed in to the timer 384 callback function. 385 386 TimerCallback - The timer callback function. 387 388 *** IMPORTANT NOTE *** 389 MxTimer object must not be freed inside the timer callback function 390 because in the pre-Vista, user mode implementation of MxTimer, the 391 destructor blocks waiting for all callback functions to finish 392 executing. Hence freeing the MxTimer object inside the callback 393 function will result in a deadlock. 394 395 Period - The period of the timer in milliseconds. 396 397 Return value: 398 An NTSTATUS value that indicates whether or not we succeeded in 399 initializing the MxTimer 400 --*/ 401 { 402 NTSTATUS ntStatus; 403 404 ntStatus = m_Timer.Initialize(TimerContext, 405 TimerCallback, 406 Period); 407 408 return ntStatus; 409 } 410 411 _Must_inspect_result_ 412 NTSTATUS 413 MxTimer::InitializeEx( 414 __in_opt PVOID TimerContext, 415 __in MdExtCallback TimerCallback, 416 __in LONG Period, 417 __in ULONG TolerableDelay, 418 __in BOOLEAN UseHighResolutionTimer 419 ) 420 { 421 422 UNREFERENCED_PARAMETER(TolerableDelay); 423 UNREFERENCED_PARAMETER(UseHighResolutionTimer); 424 UNREFERENCED_PARAMETER(TimerCallback); 425 UNREFERENCED_PARAMETER(TimerContext); 426 UNREFERENCED_PARAMETER(Period); 427 ASSERTMSG("Not implemented for UMDF\n", FALSE); 428 return STATUS_NOT_IMPLEMENTED; 429 430 } 431 432 VOID 433 MxTimer::Start( 434 __in LARGE_INTEGER DueTime, 435 __in ULONG TolerableDelay 436 ) 437 { 438 m_Timer.Start(DueTime, TolerableDelay); 439 440 return; 441 } 442 443 _Must_inspect_result_ 444 BOOLEAN 445 MxTimer::Stop( 446 VOID 447 ) 448 { 449 BOOLEAN bRetVal; 450 451 bRetVal = m_Timer.Stop(); 452 453 return bRetVal; 454 } 455 456 _Must_inspect_result_ 457 BOOLEAN 458 MxTimer::StartWithReturn( 459 __in LARGE_INTEGER DueTime, 460 __in ULONG TolerableDelay 461 ) 462 { 463 BOOLEAN bRetVal = TRUE; 464 465 bRetVal = m_Timer.StartWithReturn(DueTime, TolerableDelay); 466 467 return bRetVal; 468 } 469 470 VOID 471 MxTimer::FlushQueuedDpcs( 472 VOID 473 ) 474 { 475 WaitForThreadpoolWorkCallbacks(m_Timer.m_WorkObject, 476 TRUE // cancel pending callbacks 477 ); 478 } 479 480