MemoryAccessViolationException and multimedia timer problem

M

MM

Hi,

I have imported the Multimedia timer funcs from "winmm.dll" and setup a c#
class with the TimerEventHandler:-
class MSecTmer
{
ulong count; // holds tick count of timer
<stuff>
private void tickHandler(int id, int msg, int userdata, int r1, int r2)
{
count++; // private ulong holding tick count

// now call listeners (from other classes) waiting on the given time
period
if (onTimePeriod != null)
onTimePeriod();
}

It works to a point. The listeners get called for a certain number of
iterations and then a MemoryAccessViolation exception occurs - always at the
same number of iterations. I really know very little about the
InteropServices namespace and about wrapping dll functions. I read somewhere
in the docs that the callback should not include any system calls, so I
commented out all but the increment and it *appeared* to work without
problems for a good 15 minutes.
private void tickHandler(int id, int msg, int callback, int r1, int r2)
{
count++; // private ulong holding tick count

// now call listeners waiting on the given time period
//if (onTimePeriod != null)
// onTimePeriod();
}

Adding....
{
count++; // private ulong holding tick count
Console.WriteLine(count);
}
caused the same problem as mentioned above but after a different number of
iterations. It seems to me that the thread is running out of stack/heap
space or something or a bad pointer somewhere - but where!? Any ideas what's
going wrong and how-to fix it? Thanks! matt.

Here's whole class...

//*******************************************************************

// Based on LP TimerTest

// a test application for .NET timers

// Copyright: Luc Pattyn, January 2007

// This code is freely available for any non-commercial use.

//*******************************************************************

using System;

using System.Collections; // ArrayList

using System.Text;

using System.Runtime.InteropServices; // DllImport

using System.Threading;

namespace Clocks

{

using TCount = System.Int64; // type to store tick counts

using TRes = System.UInt32; // timer resolution

public class MSecTimer

{

public delegate void TimePeriodHandler();

public event TimePeriodHandler onTimePeriod;

private int msecTimer;

TRes minMSec = 0;

TRes maxMSec = 0;

private TCount count;

private uint interval;

private DateTime start;

private DateTime stop;

public MSecTimer(TRes msecs)

{

GetCapabilities();

if ((msecs < minMSec) || (msecs > maxMSec))

throw new ArgumentOutOfRangeException("Timer period out of range");

else

{

interval = msecs;

}

}

private void GetCapabilities() {

TimeCaps timeCaps=new TimeCaps(0, 0);

uint result=timeGetDevCaps(out timeCaps, Marshal.SizeOf(timeCaps));

if (result!=0)

Console.WriteLine("timeGetDevCaps result="+result);

minMSec=timeCaps.minimum;

maxMSec=timeCaps.maximum;

}

public void Start() {

msecTimer=timeSetEvent(interval, interval, new
TimerEventHandler(tickHandler),

0, 1); // type=periodic

}

public void Stop()

{ timeKillEvent(msecTimer); }

private void tickHandler(int id, int msg, int userCtx, int rsv1, int rsv2)

{

count++;

if (onTimePeriod != null)

onTimePeriod();

}

[DllImport("Winmm.dll")]

private static extern int timeGetTime();

[DllImport("winmm.dll")]

private static extern uint timeGetDevCaps(out TimeCaps timeCaps, int size);

struct TimeCaps {

public uint minimum;

public uint maximum;

public TimeCaps(uint minimum, uint maximum) {

this.minimum=minimum;

this.maximum=maximum;

}

}

[DllImport("WinMM.dll", SetLastError=true)]

private static extern int timeSetEvent(uint msDelay, uint msResolution,

TimerEventHandler handler, int userCtx, int eventType);

[DllImport("WinMM.dll", SetLastError=true)]

static extern int timeKillEvent(int timerEventId);

public delegate void TimerEventHandler(int id, int msg, int userCtx,

int rsv1, int rsv2);

}

}
 
J

Jeroen Mostert

MM said:
I have imported the Multimedia timer funcs from "winmm.dll"

These functions are obsolete even in the unmanaged world. Use
System.Timers.Timer or System.Threading.Timer instead (not to be confused
with System.Windows.Forms.Timer, which uses Window messages and is far less
accurate).

I'm going to be unhelpful and not diagnose your actual problem, because I
get the feeling it's a waste of time.
 
M

MM

I have tried those (System.Timers.Timer & System.Threading.Timer) and the
code works without problem. However the problem remains that I've found
these solutions just too unpredictable - even using modest time intervals
like 1000msecs causes chronic drift particularily when machines under heavy
load. I found the multimedia timers to work a treat (barring this annoying
problem - see later post). If the funcs in "winmm.dll" are obsolete - what
do the multimedia apps use for their timing? Thanks for the advice.
 
J

Jeroen Mostert

Peter said:
Unfortunately, they are _not_ obsolete, as Jeroen says. As you've found,
the multimedia timers are much more precise than any of the
thread/message-based timers. It's easy to compensate the regular timers
so that over time they produce accurate results, but you'll always have
jitter no matter what.
This is not at all what you'd expect. What versions of Windows are we
talking about, anyway? Are we comparing the winmm functions to
CreateTimerQueueTimer() or to Threading.Timer (which I've been led to
believe is a managed wrapper around the former, on systems that support it)?
It seems odd that Microsoft would itself mark functions obsolete that are
more accurate than their designated replacements.
 
J

Jeroen Mostert

MM said:
I have imported the Multimedia timer funcs from "winmm.dll" and setup a c#
class with the TimerEventHandler:-

Here's a little bone to make up for my gaffe earlier about this being
unnecessary. The following code reduces the scenario to its simplest form
and Works On My Machine (tm). Notably it fixes some declarations that had
the wrong size on 64-bit machines in your code (in case you use one) and
uses a pure delegate instead of events. This suggests any remaining problems
may be due to complications introduced by using events (and/or the actual
code in the listeners themselves).

delegate void TIMECALLBACK(int uTimerID, int uMsg, IntPtr dwUser, IntPtr
dw1, IntPtr dw2);

static class UnsafeNativeMethods {
public const int TIME_ONESHOT = 0;
public const int TIME_PERIODIC = 1;
public const int TIME_CALLBACK_FUNCTION = 0;
public const int TIME_CALLBACK_EVENT_SET = 16;
public const int TIME_CALLBACK_EVENT_PULSE = 32;
public const int TIME_KILL_SYNCHRONOUS = 0x0100;

[DllImport("winmm.dll")]
public static extern int timeSetEvent(int uDelay, int uResolution,
TIMECALLBACK lpTimeProc, IntPtr dwUser, int fuEvent);

[DllImport("winmm.dll")]
public static extern int timeKillEvent(int uTimerID);
}

public sealed class MMTimer : IDisposable {
private int timerId;
private int tickCount;
public int TickCount { get { return tickCount; } }

private readonly Action callback;

public MMTimer(Action callback, int interval) {
this.callback = callback;
timerId = UnsafeNativeMethods.timeSetEvent(interval, interval,
timeCallback, IntPtr.Zero, UnsafeNativeMethods.TIME_PERIODIC);
}

private void timeCallback(int uTimerID, int uMsg, IntPtr dwUser, IntPtr
dw1, IntPtr dw2) {
++tickCount;
callback();
}

public void Stop() {
((IDisposable) this).Dispose();
}

void IDisposable.Dispose() {
if (timerId != 0) UnsafeNativeMethods.timeKillEvent(timerId);
GC.SuppressFinalize(this);
}

~MMTimer() {
Stop();
}
}

class Program {
static void Main(string[] args) {
int i = 0;
using (MMTimer m = new MMTimer(delegate { Console.WriteLine(++i); },
10)) {
Console.ReadLine();
m.Stop();
Console.WriteLine(m.TickCount);
}
}
}
 
J

Jeroen Mostert

Peter said:
I haven't double-checked the implementation of System.Threading.Timer.
However, a couple of years ago I tried the three .NET timer classes I
was aware of (System.Threading.Timer, System.Windows.Forms.Timer, and
System.Timers.Timer) and as far as I could tell, they all produced
roughly the same behavior. In particular, they all appeared to be
subject to thread scheduling issues.

I wasn't aware of the newer CreateTimerQueueTimer() function in Windows,
but based on my reading of the documentation and my previous experiment,
I would say that assuming System.Threading.Timer does use that, it's not
passing the settings or flags that would make it a suitable replacement
for the multimedia timers.
A memory stirs!

Threading.Timer uses .NET's roll-your-own thread pool to dispatch timer
events (the one built so that Windows 95 could still run .NET as well). The
roll-your-own thread pool is the closest thing there is to an actual native
thread pool, except that it isn't one. This confused the hell out of me the
first time I learned about it and it still does, because I made the same
mistake again, assuming the framework delegated to the obvious native
implementation.

The roll-your-own thread pool supports timers in the same way the native one
does (the same interface and everything), but it *doesn't* use the system's
timer queues. It uses its own dedicated timer thread that keeps track of the
timers and uses SleepEx() and APCs to wake up at the desired time. This
should usually be more accurate than WM_TIMER messages, but it's nowhere
near kernel-level accuracy, and obviously preemption is a very real obstacle.

I'm very curious as to whether the 4.0 CLR improves any of this (to go along
with the framework's own parallel extensions); I've recently installed VS
2010 but haven't checked yet.
 
M

MM

Hi Jeroen, Thanks for the bone, but here's the thing - your code generates
exactly the same problem as I'm having in my code -

AccessViolationException. I changed 1 thing in your code adding a zero param
delegate to handle the

lamda (marked in the code with ***). With Console.WriteLine in the delegate
the code bombs at

exactly the same iteration count (9004 on my machine) irrespective of the
msec specified. Change the

delegate to just "++i" and it runs ad infinitum just as my original code
does. I guess the next step

is to ascertain whether this behaviour is unique to my machine(s) (I've
tried it on 3 running

XP/VS2005) or whether it bombs on other one's too. In the meantime, I'll
test further. The docs for

timeSetEvent makes a reference to avoid System calls in the callback which I
wonder if this the

problem - if it is I haven't found a way around it - any ideas. A BIG
thank-you to both you and Pete

for your comments and help. matt.

--- CODE ---

delegate void TIMECALLBACK(int uTimerID, int uMsg, IntPtr dwUser, IntPtr
dw1, IntPtr dw2);

static class UnsafeNativeMethods {
public const int TIME_ONESHOT = 0;
public const int TIME_PERIODIC = 1;
public const int TIME_CALLBACK_FUNCTION = 0;
public const int TIME_CALLBACK_EVENT_SET = 16;
public const int TIME_CALLBACK_EVENT_PULSE = 32;
public const int TIME_KILL_SYNCHRONOUS = 0x0100;

[DllImport("winmm.dll")]
public static extern int timeSetEvent(int uDelay, int uResolution,
TIMECALLBACK lpTimeProc, IntPtr dwUser, int fuEvent);

[DllImport("winmm.dll")]
public static extern int timeKillEvent(int uTimerID);
}

public sealed class MMTimer : IDisposable {

*** // Add a parameterless delegate if not running .NET3.5 else include
"using System.Core;"
*** publid delegate void Action()
private int timerId;
private int tickCount;
public int TickCount { get { return tickCount; } }

private readonly Action callback;

public MMTimer(Action callback, int interval) {
this.callback = callback;
timerId = UnsafeNativeMethods.timeSetEvent(interval, interval,
timeCallback, IntPtr.Zero, UnsafeNativeMethods.TIME_PERIODIC);
}

private void timeCallback(int uTimerID, int uMsg, IntPtr dwUser, IntPtr
dw1, IntPtr dw2) {
++tickCount;
callback();
}

public void Stop() {
((IDisposable) this).Dispose();
}

void IDisposable.Dispose() {
if (timerId != 0) UnsafeNativeMethods.timeKillEvent(timerId);
GC.SuppressFinalize(this);
}

~MMTimer() {
Stop();
}
}

class Program {
static void Main(string[] args) {
int i = 0;
using (MMTimer m = new MMTimer(delegate { Console.WriteLine(++i); },
10)) {
Console.ReadLine();
m.Stop();
Console.WriteLine(m.TickCount);
}
}
}
 
J

Jeroen Mostert

MM said:
Hi Jeroen, Thanks for the bone, but here's the thing - your code generates
exactly the same problem as I'm having in my code -

AccessViolationException. I changed 1 thing in your code adding a zero param
delegate to handle the

lamda (marked in the code with ***). With Console.WriteLine in the delegate
the code bombs at

exactly the same iteration count (9004 on my machine) irrespective of the
msec specified. Change the

delegate to just "++i" and it runs ad infinitum just as my original code
does. I guess the next step

is to ascertain whether this behaviour is unique to my machine(s) (I've
tried it on 3 running

XP/VS2005) or whether it bombs on other one's too.

FWIW, I'm unable to repro this problem on my machine (XP SP3/.NET 3.5 SP1/2
GB RAM) with *this* code, but I can repro it if I make it more
interesting: create a new thread that calls GC.Collect() regularly.
timeSetEvent makes a reference to avoid System calls in the callback which I
wonder if this the

problem - if it is I haven't found a way around it - any ideas.

Amend the code like this and see if it helps, as it fixes a rather obvious bug:

sealed class MMTimer : IDisposable {
...
private readonly TIMECALLBACK internalCallback;
...

public MMTimer(Action callback, int interval) {
this.callback = callback;
this.internalCallback = timeCallback;
timerId = UnsafeNativeMethods.timeSetEvent(interval, interval,
internalCallback, IntPtr.Zero, UnsafeNativeMethods.TIME_PERIODIC);
}

Passing "callback" to timeSetEvent() on the fly creates a
garbage-collectable delegate, which will duly be garbage collected when the
system feels like it (and it can't detect that unmanaged code still needs
it). Assigning it to a field prevents this.

Let me know if this helps.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top