When the specified amount of time has elapsed for a time-out, the callback procedure is called. For asynchronous time-outs, the callback procedure is called at hardware interrupt time; for other time-outs, the callback procedure is called at unrestricted event time. At hardware interrupt time, only asynchronous services may be called. See Events for restrictions on what can and cannot be done at event time.
All time-out callback procedures receive a tardiness value in the ECX register. This value is the number of milliseconds by which the time-out is late. For example, if you scheduled a time-out for 100 milliseconds, but it does not get dispatched until 115 milliseconds have elapsed, the ECX register will contain the value 15. Time-outs are often delayed by 10 milliseconds or more, because the normal system timer runs at 20 milliseconds or slower. If a virtual device needs more accurate time-outs, it must increase the timer interrupt frequency using virtual timer device (VTD) services.
Note that the value in ECX is accurate only to the last updated resolution. If you need the tardiness to millisecond resolution, you must first ensure that the virtual machine manager originally scheduled the time-out to millisecond resolution (see Timing Query Services), then use the following algorithm to convert the last-updated resolution to system-time resolution:
; On entry, ECX = tardiness in milliseconds relative to ; Last_Updated_System_Time VMMcall Get_Last_Updated_System_Time sub ecx, eax ;ECX = Remove Last_Updated_System_Time bias VMMcall Get_System_Time add ecx, eax ; ECX = Apply System_Time bias ; ECX = true tardiness to millisecond resolution
Alternatively, you could record the system time when the time-out was scheduled, and subtract it from the current system time at the time the callback is made, rather than doing the above.
If you need to convert the tardiness of a thread time-out or virtual machine time-out, you should use _GetLastUpdatedThreadExecTime and _GetThreadExecTime, or Get_Last_Updated_VM_Exec_Time and Get_VM_Exec_Time, respectively. Note that, since _GetLastUpdatedThreadExecTime and _GetThreadExecTime use the C calling convention, you need to preserve the EDX and ECX registers around the calls.