How to implement synchronization with timers in C# - c#

I have a scenario where my C# class has two methods say DoThis() and DoThat() that are called independent of each other, in any order, by an external caller. The two methods need to be synchronized in the following way:
After a call to DoThis(), wait at least t1 seconds before proceeding with DoThat() execution
After a call to DoThat(), wait at least t2 seconds before proceeding with DoThis() execution
So essentially in pseudocode:
static SomeCustomTimer Ta, Tb;
static TimeSpan t1, t2;
public static void DoThis()
{
if(Tb.IsRunning())
Tb.WaitForExpiry();
DoStuff();
Ta.Start(t1);
}
public static void DoThat()
{
if(Ta.IsRunning())
Ta.WaitForExpiry();
DoOtherStuff();
Tb.Start(t2);
}
DoStuff() and DoOtherStuff() are not long-running methods and do not share resources otherwise. Typically DoThis() and DoThat() will not be called concurrently. But I still need to protect against potential deadlocks.
How can I best implement DoThis(), DoThat() in C#?
EDIT
My scenario right now is simple in that there aren't an arbitrary number of threads calling these functions. For purpose of simplification, there's a single caller thread calling these functions in an arbitrary sequence. So the two methods will not be called concurrently, instead the caller will call these methods one-by-one in any order. I don't have control over the caller thread's code so I want to enforce the delay between successive calls to DoThis(), DoThat().

This is pretty easy to solve with a timed latch. A latch is synchronization mechanism that is either opened or closed. When open threads are allowed to pass through. When closed threads cannot get through. A timed latch is one that will automatically reopen or reclose after a certain amount of time has elapsed. In this case we want a "normally opened" latch so the behavior is biased towards staying open. That means the latch will reopen automatically after the timeout, but close only if Close is explicitly called. Multiple calls to Close will reset the timer.
static NormallyOpenTimedLatch LatchThis = new NormallyOpenTimedLatch(t2);
static NormallyOpenTimedLatch LatchThat = new NormallyOpenTimedLatch(t1);
static void DoThis()
{
LatchThis.Wait(); // Wait for it open.
DoThisStuff();
LatchThat.Close();
}
static void DoThat()
{
LatchThat.Wait(); // Wait for it open.
DoThatStuff();
LatchThis.Close();
}
And we can implement our timed latch like the following.
public class NormallyOpenTimedLatch
{
private TimeSpan m_Timeout;
private bool m_Open = true;
private object m_LockObject = new object();
private DateTime m_TimeOfLastClose = DateTime.MinValue;
public NormallyOpenTimedLatch(TimeSpan timeout)
{
m_Timeout = timeout;
}
public void Wait()
{
lock (m_LockObject)
{
while (!m_Open)
{
Monitor.Wait(m_LockObject);
}
}
}
public void Open()
{
lock (m_LockObject)
{
m_Open = true;
Monitor.PulseAll(m_LockObject);
}
}
public void Close()
{
lock (m_LockObject)
{
m_TimeOfLastClose = DateTime.UtcNow;
if (m_Open)
{
new Timer(OnTimerCallback, null, (long)m_Timeout.TotalMilliseconds, Timeout.Infinite);
}
m_Open = false;
}
}
private void OnTimerCallback(object state)
{
lock (m_LockObject)
{
TimeSpan span = DateTime.UtcNow - m_TimeOfLastClose;
if (span > m_Timeout)
{
Open();
}
else
{
TimeSpan interval = m_Timeout - span;
new Timer(OnTimerCallback, null, (long)interval.TotalMilliseconds, Timeout.Infinite);
}
}
}
}

Hm..What do you need in that case:
One Thread calls DoThis some time in succession. Another can run DoThat at least t2 seconds after LAST calling of DoThat or the first one after last calling DoThat?
I think, If your target platform is Win then It is better to use WaitableTimer (however, It is not realized in .NET but you can use It through API. You need to define those functions:
[DllImport("kernel32.dll")]
public static extern IntPtr CreateWaitableTimer(IntPtr lpTimerAttributes, bool bManualReset, string lpTimerName);
[DllImport("kernel32.dll")]
public static extern bool SetWaitableTimer(IntPtr hTimer, [In] ref long pDueTime,
int lPeriod, IntPtr pfnCompletionRoutine,
IntPtr lpArgToCompletionRoutine, bool fResume);
[DllImport("kernel32", SetLastError = true, ExactSpelling = true)]
public static extern Int32 WaitForSingleObject(IntPtr handle, int milliseconds);
public static uint INFINITE = 0xFFFFFFFF;
And then using It as follow:
private IntPtr _timer = null;
//Before first call of DoThis or DoThat you need to create timer:
//_timer = CreateWaitableTimer (IntPtr.Zero, true, null);
public static void DoThis()
{
//Waiting until timer signaled
WaitForSingleObject (_timer, INFINITE);
DoStuff();
long dueTime = 10000 * 1000 * seconds; //dueTime is in 100 nanoseconds
//Timer will signal once after expiration of dueTime
SetWaitableTimer (_timer, ref dueTime, 0, IntPtr.Zero, IntPtr.Zero, false);
}
public static void DoThis()
{
//Waiting until timer signaled
WaitForSingleObject (_timer, INFINITE);
DoOtherStuff();
long dueTime = 10000 * 1000 * seconds; //dueTime is in 100 nanoseconds
//Timer will signal once after expiration of dueTime
SetWaitableTimer (_timer, ref dueTime, 0, IntPtr.Zero, IntPtr.Zero, false);
}
And after using you may destroy timer by calling CloseHandle.

Okay I'm trying out a possible solution to this problem using EventWaitHandle. Looking for comments / feedback. Can this work reliably?
// Implementation of a manual event class with a DelayedSet method
// DelayedSet will set the event after a delay period
// TODO: Improve exception handling
public sealed class DelayedManualEvent : EventWaitHandle
{
private SysTimer timer; // using SysTimer = System.Timers.Timer;
public DelayedManualEvent() :
base(true, EventResetMode.ManualReset)
{
timer = new SysTimer();
timer.AutoReset = false;
timer.Elapsed +=new ElapsedEventHandler(OnTimeout);
}
public bool DelayedSet(TimeSpan delay)
{
bool result = false;
try
{
double timeout = delay.TotalMilliseconds;
if (timeout > 0 && timer != null && Reset())
{
timer.Interval = timeout;
timer.Start();
result = true;
Trace.TraceInformation("DelayedManualEvent.DelayedSet Event will be signaled in {0}ms",
delay);
}
}
catch (Exception e)
{
Trace.TraceError("DelayedManualEvent.DelayedSet Exception {0}\n{1}",
e.Message, e.StackTrace);
}
return result;
}
private void OnTimeout(object source, ElapsedEventArgs e)
{
if (timer != null)
{
timer.Stop();
Trace.TraceInformation("DelayedManualEvent.OnTimeout Event signaled at time {0}", e.SignalTime);
}
try
{
if (!Set())
{
Trace.TraceError("DelayedManualEvent.OnTimeout Event set failed");
}
}
catch (Exception ex)
{
Trace.TraceError("DelayedManualEvent.OnTimeout Exception in signaling event\n{0}]\n{1}",
ex.Message, ex.StackTrace);
}
}
protected override void Dispose(bool disposing)
{
if (timer != null)
{
timer.Dispose();
}
base.Dispose(disposing);
}
}
The way I'm planning to use this:
// Pseudocode
static DelayedManualEvent delayedEvent = new DelayedManualEvent();
static TimeSpan t1, t2, maxTimeout;
public static void DoThis()
{
if(!delayedEvent.WaitOne(maxTimeout))
return;
DoStuff();
delayedEvent.DelayedSet(t1);
}
public static void DoThat()
{
if(!delayedEvent.WaitOne(maxTimeout))
return;
DoOtherStuff();
delayedEvent.DelayedSet(t2);
}

Related

How can I avoid crashing when creating a precision timer with WinMM.dll?

I'm trying to create a precision timer. I found an example created with WinMM.dll. The sample works really fine. But it crashes with the first garbage collector.
How can I prevent the garbage collector from blocking the timer?
public class WinMMWrapper : IDisposable
{
[DllImport("WinMM.dll", SetLastError = true)]
public static extern uint timeSetEvent(int msDelay, int msResolution,
TimerEventHandler handler, ref int userCtx, int eventType);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)] // <=== ADD THIS
static extern uint timeKillEvent(uint uTimerID); // <=== ADD THIS
public delegate void TimerEventHandler(uint id, uint msg, ref int userCtx,
int rsv1, int rsv2);
public enum TimerEventType
{
OneTime = 0,
Repeating = 1,
}
private readonly Action _elapsedAction;
private readonly int _elapsedMs;
private readonly int _resolutionMs;
private readonly TimerEventType _timerEventType;
private uint _timerId; // <=== ADD THIS
private bool _disposed; // <=== ADD THIS
public WinMMWrapper(int elapsedMs, int resolutionMs, TimerEventType timerEventType, Action elapsedAction)
{
_elapsedMs = elapsedMs;
_resolutionMs = resolutionMs;
_timerEventType = timerEventType;
_elapsedAction = elapsedAction;
}
public bool StartElapsedTimer() // <=== RETURN bool
{
StopTimer(); // Stop any started timer
int myData = 1;
// === SET _timerId
_timerId = timeSetEvent(_elapsedMs, _resolutionMs / 10, new TimerEventHandler(TickHandler), ref myData, (int)_timerEventType);
return _timerId != 0;
}
public void StopTimer() // <=== ADD THIS
{
if (_timerId != 0)
{
timeKillEvent(_timerId);
_timerId = 0;
}
}
private void TickHandler(uint id, uint msg, ref int userctx, int rsv1, int rsv2)
{
_elapsedAction();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed && disposing)
StopTimer();
_disposed = true;
}
~WinMMWrapper()
{
Dispose(false);
}
}
My Static Class
public static class Global
{
public static WinMMWrapper timer;
}
Create WinMMWrapper
private void TimerStart_Click(object sender, RoutedEventArgs e)
{
Global.timer = new WinMMWrapper(1, 1, WinMMWrapper.TimerEventType.Repeating, Tick);
Global.timer.StartElapsedTimer();
}
Tick Function
private static void Tick()
{
Console.WriteLine("Time : " + DateTime.Now.ToString("hh:mm:ss:ffff"));
}
Error Message
Managed Debugging Assistant 'CallbackOnCollectedDelegate' : A callback was made on the garbage-collected delegate of type 'CanBusRandomDataGenerator!CanBusRandomDataGenerator.WinMMWrapper+TimerEventHandler::Invoke'. This can cause app crashes, corruption, and data loss. When delegating to unmanaged code, it must be kept alive by the managed application until it is guaranteed that the delegates will never be called.'
The code is now exactly the same. It works for about 2 3 seconds, then it crashes to the following error. Error occurs within WinMMWrapper function without falling into Dispose.
You must keep the timer variable alive as long as you are using the timer. If it is a local variable, it will be reclaimed by the GC when you leave the method. Do so by converting this local variable to a class field (possibly static). In a Console application you can still use a local variable, but you must add a Console.ReadKey(); to prevent the application to exit prematurely.
Also, stop the timer before this variable becomes eligible for garbage collection. To do so, let WinMMWrapper implement IDisposable.
Make sure that the object where the callback Action lives stays alive and is not disposed! Probably this is the object where you call new WinMMWrapper(..., theAction).
public class WinMMWrapper : IDisposable
{
[DllImport("WinMM.dll", SetLastError = true)]
public static extern uint timeSetEvent(int msDelay, int msResolution,
TimerEventHandler handler, ref int userCtx, int eventType);
[DllImport("Winmm.dll", CharSet = CharSet.Auto)] // <=== ADD THIS
static extern uint timeKillEvent(uint uTimerID); // <=== ADD THIS
public delegate void TimerEventHandler(uint id, uint msg, ref int userCtx,
int rsv1, int rsv2);
public enum TimerEventType
{
OneTime = 0,
Repeating = 1,
}
private readonly Action _elapsedAction;
private readonly int _elapsedMs;
private readonly int _resolutionMs;
private readonly TimerEventType _timerEventType;
private iuint _timerId; // <=== ADD THIS
private bool _disposed; // <=== ADD THIS
public WinMMWrapper(int elapsedMs, int resolutionMs, TimerEventType timerEventType, Action elapsedAction)
{
_elapsedMs = elapsedMs;
_resolutionMs = resolutionMs;
_timerEventType = timerEventType;
_elapsedAction = elapsedAction;
}
public bool StartElapsedTimer() // <=== RETURN bool
{
Stop(); // Stop any started timer
int myData = 1;
// === SET _timerId
_timerId = timeSetEvent(_elapsedMs, _resolutionMs / 10, new TimerEventHandler(TickHandler), ref myData, (int)_timerEventType);
return _timerId != 0;
}
public void StopTimer() // <=== ADD THIS
{
if (_timerId != 0)
{
timeKillEvent(_timerId);
_timerId = 0;
}
}
private void TickHandler(uint id, uint msg, ref int userctx, int rsv1, int rsv2)
{
_elapsedAction();
}
// === ADD Dispose and finalizer ===
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private void Dispose(bool disposing)
{
if (!_disposed && disposing)
StopTimer();
}
_disposed = true;
}
~MMTimer()
{
Dispose(false);
}
}
Then you can do this in a Console application:
using (var timer = new WinMMWrapper(1, 1, WinMMWrapper.TimerEventType.Repeating,
() => Console.WriteLine("Time : " + DateTime.Now.ToString("hh:mm:ss:fff"))) {
Console.Writeline("Hit a key to stop the timer and quit the application!");
Console.ReadKey();
} // <= Here timer.Dispose() gets automatically called by using.
If you cannot use a using statement because your timer will be stopped at another place in your code, you can also call timer.Dispose(); explicitly.
To make this code thread-safe, enclose your start and stop timer code in a lock(this { ... } statement.

timeBeginPeriod not working on Intel Comet Lake CPU (i5 10400H)

I have some operations in my application which rely on short timers. Using the example code below I have timers firing every ~5ms as required.
On an Intel i5 10400H CPU the timings are observed to be off, and the callback occurs after ~15ms (or a multiple of 15). Using the ClockRes sysinternals tool shows that the machine has a system timer resolution of 15ms even when run after the call to timeBeginPeriod(1) made in the code below.
Using https://cms.lucashale.com/timer-resolution/ to set the resolution to the maximum supported value (0.5ms) does not change the behaviour of the example code.
From what I can see the machine is using the Invariant TSC acpi timer, and forcing it to use HPET (with bcdedit /set useplatformclock true and rebooting) did not change the behaviour.
I can't see anything in the CPU documentation or errata that would explain this.
I don't know where the problem lies and if it is something that is fixable on my end, any ideas?
Edit: Having this program (DPC Latency Checker) open results in the timer queue firing when expected, so it's solveable.
Example code:
using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
namespace ConsoleApp1
{
class Program
{
static void Main(string[] args)
{
using (new TimePeriod(1))
RunTimer();
}
public static void RunTimer()
{
var completionEvent = new ManualResetEvent(false);
var stopwatch = Stopwatch.StartNew();
var i = 0;
var previous = 0L;
using var x = TimerQueue.Default.CreateTimer((s) =>
{
if (i > 100)
completionEvent.Set();
i++;
var now = stopwatch.ElapsedMilliseconds;
var gap = now - previous;
previous = now;
Console.WriteLine($"Gap: {gap}ms");
}, "", 10, 5);
completionEvent.WaitOne();
}
}
public class TimerQueueTimer : IDisposable
{
private TimerQueue MyQueue;
private TimerCallback Callback;
private object UserState;
private IntPtr Handle;
internal TimerQueueTimer(
TimerQueue queue,
TimerCallback cb,
object state,
uint dueTime,
uint period,
TimerQueueTimerFlags flags)
{
MyQueue = queue;
Callback = cb;
UserState = state;
bool rslt = TQTimerWin32.CreateTimerQueueTimer(
out Handle,
MyQueue.Handle,
TimerCallback,
IntPtr.Zero,
dueTime,
period,
flags);
if (!rslt)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error creating timer.");
}
}
~TimerQueueTimer()
{
Dispose(false);
}
public void Change(uint dueTime, uint period)
{
bool rslt = TQTimerWin32.ChangeTimerQueueTimer(MyQueue.Handle, ref Handle, dueTime, period);
if (!rslt)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error changing timer.");
}
}
private void TimerCallback(IntPtr state, bool bExpired)
{
Callback.Invoke(UserState);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private IntPtr completionEventHandle = new IntPtr(-1);
public void Dispose(WaitHandle completionEvent)
{
completionEventHandle = completionEvent.SafeWaitHandle.DangerousGetHandle();
this.Dispose();
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!disposed)
{
bool rslt = TQTimerWin32.DeleteTimerQueueTimer(MyQueue.Handle,
Handle, completionEventHandle);
if (!rslt)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error deleting timer.");
}
disposed = true;
}
}
}
public class TimerQueue : IDisposable
{
public IntPtr Handle { get; private set; }
public static TimerQueue Default { get; private set; }
static TimerQueue()
{
Default = new TimerQueue(IntPtr.Zero);
}
private TimerQueue(IntPtr handle)
{
Handle = handle;
}
public TimerQueue()
{
Handle = TQTimerWin32.CreateTimerQueue();
if (Handle == IntPtr.Zero)
{
throw new Win32Exception(Marshal.GetLastWin32Error(), "Error creating timer queue.");
}
}
~TimerQueue()
{
Dispose(false);
}
public TimerQueueTimer CreateTimer(
TimerCallback callback,
object state,
uint dueTime,
uint period)
{
return CreateTimer(callback, state, dueTime, period, TimerQueueTimerFlags.ExecuteInPersistentThread);
}
public TimerQueueTimer CreateTimer(
TimerCallback callback,
object state,
uint dueTime,
uint period,
TimerQueueTimerFlags flags)
{
return new TimerQueueTimer(this, callback, state, dueTime, period, flags);
}
private IntPtr CompletionEventHandle = new IntPtr(-1);
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public void Dispose(WaitHandle completionEvent)
{
CompletionEventHandle = completionEvent.SafeWaitHandle.DangerousGetHandle();
Dispose();
}
private bool Disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!Disposed)
{
if (Handle != IntPtr.Zero)
{
bool rslt = TQTimerWin32.DeleteTimerQueueEx(Handle, CompletionEventHandle);
if (!rslt)
{
int err = Marshal.GetLastWin32Error();
throw new Win32Exception(err, "Error disposing timer queue");
}
}
Disposed = true;
}
}
}
public enum TimerQueueTimerFlags : uint
{
ExecuteDefault = 0x0000,
ExecuteInTimerThread = 0x0020,
ExecuteInIoThread = 0x0001,
ExecuteInPersistentThread = 0x0080,
ExecuteLongFunction = 0x0010,
ExecuteOnlyOnce = 0x0008,
TransferImpersonation = 0x0100,
}
public delegate void Win32WaitOrTimerCallback(
IntPtr lpParam,
[MarshalAs(UnmanagedType.U1)] bool bTimedOut);
static public class TQTimerWin32
{
[DllImport("kernel32.dll", SetLastError = true)]
public extern static IntPtr CreateTimerQueue();
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool DeleteTimerQueue(IntPtr timerQueue);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool DeleteTimerQueueEx(IntPtr timerQueue, IntPtr completionEvent);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool CreateTimerQueueTimer(
out IntPtr newTimer,
IntPtr timerQueue,
Win32WaitOrTimerCallback callback,
IntPtr userState,
uint dueTime,
uint period,
TimerQueueTimerFlags flags);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool ChangeTimerQueueTimer(
IntPtr timerQueue,
ref IntPtr timer,
uint dueTime,
uint period);
[DllImport("kernel32.dll", SetLastError = true)]
public extern static bool DeleteTimerQueueTimer(
IntPtr timerQueue,
IntPtr timer,
IntPtr completionEvent);
}
public sealed class TimePeriod : IDisposable
{
private const string WINMM = "winmm.dll";
private static TIMECAPS timeCapabilities;
private static int inTimePeriod;
private readonly int period;
private int disposed;
[DllImport(WINMM, ExactSpelling = true)]
private static extern int timeGetDevCaps(ref TIMECAPS ptc, int cbtc);
[DllImport(WINMM, ExactSpelling = true)]
private static extern int timeBeginPeriod(int uPeriod);
[DllImport(WINMM, ExactSpelling = true)]
private static extern int timeEndPeriod(int uPeriod);
static TimePeriod()
{
int result = timeGetDevCaps(ref timeCapabilities, Marshal.SizeOf(typeof(TIMECAPS)));
if (result != 0)
{
throw new InvalidOperationException("The request to get time capabilities was not completed because an unexpected error with code " + result + " occured.");
}
}
internal TimePeriod(int period)
{
if (Interlocked.Increment(ref inTimePeriod) != 1)
{
Interlocked.Decrement(ref inTimePeriod);
throw new NotSupportedException("The process is already within a time period. Nested time periods are not supported.");
}
if (period < timeCapabilities.wPeriodMin || period > timeCapabilities.wPeriodMax)
{
throw new ArgumentOutOfRangeException("period", "The request to begin a time period was not completed because the resolution specified is out of range.");
}
int result = timeBeginPeriod(period);
if (result != 0)
{
throw new InvalidOperationException("The request to begin a time period was not completed because an unexpected error with code " + result + " occured.");
}
this.period = period;
}
internal static int MinimumPeriod
{
get
{
return timeCapabilities.wPeriodMin;
}
}
internal static int MaximumPeriod
{
get
{
return timeCapabilities.wPeriodMax;
}
}
internal int Period
{
get
{
if (this.disposed > 0)
{
throw new ObjectDisposedException("The time period instance has been disposed.");
}
return this.period;
}
}
public void Dispose()
{
if (Interlocked.Increment(ref this.disposed) == 1)
{
timeEndPeriod(this.period);
Interlocked.Decrement(ref inTimePeriod);
}
else
{
Interlocked.Decrement(ref this.disposed);
}
}
[StructLayout(LayoutKind.Sequential)]
private struct TIMECAPS
{
internal int wPeriodMin;
internal int wPeriodMax;
}
}
}
This seem to be an issue with windows 10 2004. I would guess that it has nothing to do with the processor/motherboard.
A possible workaround might be to use a stopwatch and spinwait on a thread. This would be inadvisable for regular consumer applications since it would consume a full thread, but might be feasible if you have full control of the system.
I encountered the exact same problem under Windows 10 2004. Previous versions did not seem to exhibit the same behavior. CreateTimerQueueTimer does not seem to honor timeBeginPeriod anymore and its minimum period seems to be 15ms (good old 15 ms...).
There are a few people complaining about this problem around, but not a lot. (see this forum entry for example.
I do not know if this is a bug introduced in v2004, or a power-saving "feature" that got sneaked past us.
That being said, official documentation never linked TimerQueueTimers and timeBeginPeriod, so if might have been a bug to begin with that they honored the timeBeginPeriod setting.
In any case, I ended up re-implementing a TimerQueue on top of timeBeginPeriod/timeSetEvent to achieve the required timer frequency.
Running into the same problem, I'm using CreateTimerQueueTimer. What still works is timeSetEvent. You'll loose some precision as it's in whole milliseconds, but it's better than nothing.

Make Timer intervals more accurate [duplicate]

Is there a high resolution timer that raises an event each time the timer elapses, just like the System.Timer class? I need a high resolution timer to Elapse every ms.
I keep running into posts that explain that the Stopwatch can measure high resolutions, but I don't want to measure time, I want to create an interval of 1 ms.
Is there something in .NET or am I going to write my own high res timer?
There is nothing built into the .NET framework that I am aware of. Windows has a mechanism for high resolution timer events via the Multimedia Timer API. Below is a quick example I whipped up which seems to do the job. There are also seems to be a good example here.
I will note that this API changes system wide settings that can degrade system performance, so buyer beware. For testing purposes, I would recommend keeping track of how often the timer is firing to verify the timing is similar to the device you are trying to simulate. Since windows is not a real-time OS, the load on your system may cause the MM timer be delayed resulting in gaps of 100 ms that contain 100 events in quick succession, rather than 100 events spaced 1 ms apart. Some additional reading on MM timers.
class Program
{
static void Main(string[] args)
{
TestThreadingTimer();
TestMultimediaTimer();
}
private static void TestMultimediaTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new MultimediaTimer() { Interval = 1 })
{
timer.Elapsed += (o, e) => Console.WriteLine(s.ElapsedMilliseconds);
s.Start();
timer.Start();
Console.ReadKey();
timer.Stop();
}
}
private static void TestThreadingTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new Timer(o => Console.WriteLine(s.ElapsedMilliseconds), null, 0, 1))
{
s.Start();
Console.ReadKey();
}
}
}
public class MultimediaTimer : IDisposable
{
private bool disposed = false;
private int interval, resolution;
private UInt32 timerId;
// Hold the timer callback to prevent garbage collection.
private readonly MultimediaTimerCallback Callback;
public MultimediaTimer()
{
Callback = new MultimediaTimerCallback(TimerCallbackMethod);
Resolution = 5;
Interval = 10;
}
~MultimediaTimer()
{
Dispose(false);
}
public int Interval
{
get
{
return interval;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
interval = value;
if (Resolution > Interval)
Resolution = value;
}
}
// Note minimum resolution is 0, meaning highest possible resolution.
public int Resolution
{
get
{
return resolution;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
resolution = value;
}
}
public bool IsRunning
{
get { return timerId != 0; }
}
public void Start()
{
CheckDisposed();
if (IsRunning)
throw new InvalidOperationException("Timer is already running");
// Event type = 0, one off event
// Event type = 1, periodic event
UInt32 userCtx = 0;
timerId = NativeMethods.TimeSetEvent((uint)Interval, (uint)Resolution, Callback, ref userCtx, 1);
if (timerId == 0)
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
public void Stop()
{
CheckDisposed();
if (!IsRunning)
throw new InvalidOperationException("Timer has not been started");
StopInternal();
}
private void StopInternal()
{
NativeMethods.TimeKillEvent(timerId);
timerId = 0;
}
public event EventHandler Elapsed;
public void Dispose()
{
Dispose(true);
}
private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
{
var handler = Elapsed;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
private void CheckDisposed()
{
if (disposed)
throw new ObjectDisposedException("MultimediaTimer");
}
private void Dispose(bool disposing)
{
if (disposed)
return;
disposed = true;
if (IsRunning)
{
StopInternal();
}
if (disposing)
{
Elapsed = null;
GC.SuppressFinalize(this);
}
}
}
internal delegate void MultimediaTimerCallback(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);
internal static class NativeMethods
{
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
internal static extern UInt32 TimeSetEvent(UInt32 msDelay, UInt32 msResolution, MultimediaTimerCallback callback, ref UInt32 userCtx, UInt32 eventType);
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")]
internal static extern void TimeKillEvent(UInt32 uTimerId);
}
I couldn't get Mike's solution to work and created a basic wrapper around Windows multi media timer based on this codeproject article https://www.codeproject.com/Articles/17474/Timer-surprises-and-how-to-avoid-them
public class WinMMWrapper
{
[DllImport("WinMM.dll", SetLastError = true)]
public static extern uint timeSetEvent(int msDelay, int msResolution,
TimerEventHandler handler, ref int userCtx, int eventType);
public delegate void TimerEventHandler(uint id, uint msg, ref int userCtx,
int rsv1, int rsv2);
public enum TimerEventType
{
OneTime = 0,
Repeating = 1
}
private readonly Action _elapsedAction;
private readonly int _elapsedMs;
private readonly int _resolutionMs;
private readonly TimerEventType _timerEventType;
private readonly TimerEventHandler _timerEventHandler;
public WinMMWrapper(int elapsedMs, int resolutionMs, TimerEventType timerEventType, Action elapsedAction)
{
_elapsedMs = elapsedMs;
_resolutionMs = resolutionMs;
_timerEventType = timerEventType;
_elapsedAction = elapsedAction;
_timerEventHandler = TickHandler;
}
public uint StartElapsedTimer()
{
var myData = 1; //dummy data
return timeSetEvent(_elapsedMs, _resolutionMs / 10, _timerEventHandler, ref myData, (int)_timerEventType);
}
private void TickHandler(uint id, uint msg, ref int userctx, int rsv1, int rsv2)
{
_elapsedAction();
}
}
Here's an example how to use it
class Program
{
static void Main(string[] args)
{
var timer = new WinMMWrapper(100, 25, WinMMWrapper.TimerEventType.Repeating, () =>
{
Console.WriteLine($"Timer elapsed {DateTime.UtcNow:o}");
});
timer.StartElapsedTimer();
Console.ReadKey();
}
}
The output looks like this
Update 2021-11-19: add TimerEventHandler class member per chris's comment.
There is an option: use Thread.Sleep(0). Attempt to call Thread.Sleep(1) or employ a System.Threading.Timer would always come down to system timer resolution. Depending on one is probably not the best idea, at the end of the day you app might be just not allowed to call timeBeginPeriod(...) from winmm.dll.
Following code can resolve down to +/- 10ns (0.10ms) on my dev machine (i7q) and could be higher. It would put a solid load on one of your CPU cores pushing its use up to 100%. No actual OS slowdown would happen, the code surrenders most of its CPU time quantum by calling Thread.Sleep as early as possible:
var requiredDelayMs = 0.1;
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
while (true)
{
if (sw.Elapsed.TotalMilliseconds >= requiredDelayMs)
{
// call your timer routine
}
Thread.Sleep(0); // setting at least 1 here would involve a timer which we don't want to
}
For the more comprehensive implementation see my other answer
Precision-Timer.NET
https://github.com/HypsyNZ/Precision-Timer.NET
https://www.nuget.org/packages/PrecisionTimer.NET/
A High Precision .NET timer that doesn't kill your CPU or get Garbage Collected.
Its designed to be as easy to use as any other .NET timer.
Try creating new System.Threading.Thread and using System.Threading.Thread.Sleep.
var thrd = new Syatem.Threading.Thread(() => {
while (true) {
// do something
System.Threading.Thread.Sleep(1); // wait 1 ms
}
});
thrd.Start();

c# waitable timer - wake up event fires instantly

The following code example (from here) creates a waitable timer and set the system in hibernation mode. After resuming back I expected an event with executable code:
class WakeUpTimer
{
[DllImport("kernel32.dll")]
public static extern SafeWaitHandle CreateWaitableTimer(IntPtr lpTimerAttributes,
bool bManualReset,
string lpTimerName);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool SetWaitableTimer(SafeWaitHandle hTimer,
[In] ref long pDueTime,
int lPeriod,
IntPtr pfnCompletionRoutine,
IntPtr lpArgToCompletionRoutine,
bool fResume);
public event EventHandler Woken;
private BackgroundWorker bgWorker = new BackgroundWorker();
public WakeUpTimer()
{
bgWorker.DoWork += new DoWorkEventHandler(bgWorker_DoWork);
bgWorker.RunWorkerCompleted += new RunWorkerCompletedEventHandler(bgWorker_RunWorkerCompleted);
}
public void SetWakeUpTime(DateTime time)
{
bgWorker.RunWorkerAsync(time.ToFileTime());
}
void bgWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e)
{
if (Woken != null)
{
Woken(this, new EventArgs());
}
}
private void bgWorker_DoWork(object sender, DoWorkEventArgs e)
{
long waketime = (long)e.Argument;
using (SafeWaitHandle handle = CreateWaitableTimer(IntPtr.Zero, true, this.GetType().Assembly.GetName().Name.ToString() + "Timer"))
{
if (SetWaitableTimer(handle, ref waketime, 0, IntPtr.Zero, IntPtr.Zero, true))
{
using (EventWaitHandle wh = new EventWaitHandle(false, EventResetMode.AutoReset))
{
wh.SafeWaitHandle = handle;
wh.WaitOne();
}
}
else
{
throw new Win32Exception(Marshal.GetLastWin32Error());
}
}
}
}
Usage of this class is as follows:
private void setHibernation(DateTime dateTime)
{
WakeUpTimer wakeUpTimer = new WakeUpTimer();
wakeUpTimer.Woken += backFromHibernate;
wakeUpTimer.SetWakeUpTime(dateTime);
Application.SetSuspendState(PowerState.Hibernate, false, false);
Debug.WriteLine("---Hibernation starts at " + DateTime.Now + " ---");
}
private void backFromHibernate(object sender, EventArgs e)
{
Debug.WriteLine("I execute this now " + DateTime.Now);
}
As I tested before this scenario several times, the code in the method backFromHibernation is executed immediately (1 sec after setSupsendState duo to debug logs).
Am I wrong or should the method be called when the system is back up from hibernation?
Would appriciate some help, would need such an event - fired automatically after wake up.
EDIT: Set hibernation for 2 min, all works great system comes up automatically.

High resolution timer in C#

Is there a high resolution timer that raises an event each time the timer elapses, just like the System.Timer class? I need a high resolution timer to Elapse every ms.
I keep running into posts that explain that the Stopwatch can measure high resolutions, but I don't want to measure time, I want to create an interval of 1 ms.
Is there something in .NET or am I going to write my own high res timer?
There is nothing built into the .NET framework that I am aware of. Windows has a mechanism for high resolution timer events via the Multimedia Timer API. Below is a quick example I whipped up which seems to do the job. There are also seems to be a good example here.
I will note that this API changes system wide settings that can degrade system performance, so buyer beware. For testing purposes, I would recommend keeping track of how often the timer is firing to verify the timing is similar to the device you are trying to simulate. Since windows is not a real-time OS, the load on your system may cause the MM timer be delayed resulting in gaps of 100 ms that contain 100 events in quick succession, rather than 100 events spaced 1 ms apart. Some additional reading on MM timers.
class Program
{
static void Main(string[] args)
{
TestThreadingTimer();
TestMultimediaTimer();
}
private static void TestMultimediaTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new MultimediaTimer() { Interval = 1 })
{
timer.Elapsed += (o, e) => Console.WriteLine(s.ElapsedMilliseconds);
s.Start();
timer.Start();
Console.ReadKey();
timer.Stop();
}
}
private static void TestThreadingTimer()
{
Stopwatch s = new Stopwatch();
using (var timer = new Timer(o => Console.WriteLine(s.ElapsedMilliseconds), null, 0, 1))
{
s.Start();
Console.ReadKey();
}
}
}
public class MultimediaTimer : IDisposable
{
private bool disposed = false;
private int interval, resolution;
private UInt32 timerId;
// Hold the timer callback to prevent garbage collection.
private readonly MultimediaTimerCallback Callback;
public MultimediaTimer()
{
Callback = new MultimediaTimerCallback(TimerCallbackMethod);
Resolution = 5;
Interval = 10;
}
~MultimediaTimer()
{
Dispose(false);
}
public int Interval
{
get
{
return interval;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
interval = value;
if (Resolution > Interval)
Resolution = value;
}
}
// Note minimum resolution is 0, meaning highest possible resolution.
public int Resolution
{
get
{
return resolution;
}
set
{
CheckDisposed();
if (value < 0)
throw new ArgumentOutOfRangeException("value");
resolution = value;
}
}
public bool IsRunning
{
get { return timerId != 0; }
}
public void Start()
{
CheckDisposed();
if (IsRunning)
throw new InvalidOperationException("Timer is already running");
// Event type = 0, one off event
// Event type = 1, periodic event
UInt32 userCtx = 0;
timerId = NativeMethods.TimeSetEvent((uint)Interval, (uint)Resolution, Callback, ref userCtx, 1);
if (timerId == 0)
{
int error = Marshal.GetLastWin32Error();
throw new Win32Exception(error);
}
}
public void Stop()
{
CheckDisposed();
if (!IsRunning)
throw new InvalidOperationException("Timer has not been started");
StopInternal();
}
private void StopInternal()
{
NativeMethods.TimeKillEvent(timerId);
timerId = 0;
}
public event EventHandler Elapsed;
public void Dispose()
{
Dispose(true);
}
private void TimerCallbackMethod(uint id, uint msg, ref uint userCtx, uint rsv1, uint rsv2)
{
var handler = Elapsed;
if (handler != null)
{
handler(this, EventArgs.Empty);
}
}
private void CheckDisposed()
{
if (disposed)
throw new ObjectDisposedException("MultimediaTimer");
}
private void Dispose(bool disposing)
{
if (disposed)
return;
disposed = true;
if (IsRunning)
{
StopInternal();
}
if (disposing)
{
Elapsed = null;
GC.SuppressFinalize(this);
}
}
}
internal delegate void MultimediaTimerCallback(UInt32 id, UInt32 msg, ref UInt32 userCtx, UInt32 rsv1, UInt32 rsv2);
internal static class NativeMethods
{
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeSetEvent")]
internal static extern UInt32 TimeSetEvent(UInt32 msDelay, UInt32 msResolution, MultimediaTimerCallback callback, ref UInt32 userCtx, UInt32 eventType);
[DllImport("winmm.dll", SetLastError = true, EntryPoint = "timeKillEvent")]
internal static extern void TimeKillEvent(UInt32 uTimerId);
}
I couldn't get Mike's solution to work and created a basic wrapper around Windows multi media timer based on this codeproject article https://www.codeproject.com/Articles/17474/Timer-surprises-and-how-to-avoid-them
public class WinMMWrapper
{
[DllImport("WinMM.dll", SetLastError = true)]
public static extern uint timeSetEvent(int msDelay, int msResolution,
TimerEventHandler handler, ref int userCtx, int eventType);
public delegate void TimerEventHandler(uint id, uint msg, ref int userCtx,
int rsv1, int rsv2);
public enum TimerEventType
{
OneTime = 0,
Repeating = 1
}
private readonly Action _elapsedAction;
private readonly int _elapsedMs;
private readonly int _resolutionMs;
private readonly TimerEventType _timerEventType;
private readonly TimerEventHandler _timerEventHandler;
public WinMMWrapper(int elapsedMs, int resolutionMs, TimerEventType timerEventType, Action elapsedAction)
{
_elapsedMs = elapsedMs;
_resolutionMs = resolutionMs;
_timerEventType = timerEventType;
_elapsedAction = elapsedAction;
_timerEventHandler = TickHandler;
}
public uint StartElapsedTimer()
{
var myData = 1; //dummy data
return timeSetEvent(_elapsedMs, _resolutionMs / 10, _timerEventHandler, ref myData, (int)_timerEventType);
}
private void TickHandler(uint id, uint msg, ref int userctx, int rsv1, int rsv2)
{
_elapsedAction();
}
}
Here's an example how to use it
class Program
{
static void Main(string[] args)
{
var timer = new WinMMWrapper(100, 25, WinMMWrapper.TimerEventType.Repeating, () =>
{
Console.WriteLine($"Timer elapsed {DateTime.UtcNow:o}");
});
timer.StartElapsedTimer();
Console.ReadKey();
}
}
The output looks like this
Update 2021-11-19: add TimerEventHandler class member per chris's comment.
There is an option: use Thread.Sleep(0). Attempt to call Thread.Sleep(1) or employ a System.Threading.Timer would always come down to system timer resolution. Depending on one is probably not the best idea, at the end of the day you app might be just not allowed to call timeBeginPeriod(...) from winmm.dll.
Following code can resolve down to +/- 10ns (0.10ms) on my dev machine (i7q) and could be higher. It would put a solid load on one of your CPU cores pushing its use up to 100%. No actual OS slowdown would happen, the code surrenders most of its CPU time quantum by calling Thread.Sleep as early as possible:
var requiredDelayMs = 0.1;
var sw = new System.Diagnostics.Stopwatch();
sw.Start();
while (true)
{
if (sw.Elapsed.TotalMilliseconds >= requiredDelayMs)
{
// call your timer routine
}
Thread.Sleep(0); // setting at least 1 here would involve a timer which we don't want to
}
For the more comprehensive implementation see my other answer
Precision-Timer.NET
https://github.com/HypsyNZ/Precision-Timer.NET
https://www.nuget.org/packages/PrecisionTimer.NET/
A High Precision .NET timer that doesn't kill your CPU or get Garbage Collected.
Its designed to be as easy to use as any other .NET timer.
Try creating new System.Threading.Thread and using System.Threading.Thread.Sleep.
var thrd = new Syatem.Threading.Thread(() => {
while (true) {
// do something
System.Threading.Thread.Sleep(1); // wait 1 ms
}
});
thrd.Start();

Categories

Resources