xofz
xofz

Reputation: 5660

SEHException when calling DeleteTimerQueueTimer

I'm using a Timer class that wraps CreateTimerQueueTimer and DeleteTimerQueueTimer.

Here is the class:

using System;
using System.Threading;
using MyCompany.Internal;
using TimerCallback = MyCompany.Internal.TimerCallback;

public class Timer : IDisposable
{
    public Timer()
    {
        this.callback = this.ticked;
        this.autoReset = true;
        Computer.ChangeTimerResolutionTo(1);
        this.priority = ThreadPriority.Normal;
    }

    public virtual event EventHandler Elapsed;

    public virtual bool AutoReset
    {
        get
        {
            return this.autoReset;
        }

        set
        {
            this.autoReset = value;
        }
    }

    public virtual ThreadPriority Priority
    {
        get
        {
            return this.priority;
        }

        set
        {
            this.priority = value;
        }
    }

    public virtual void Start(int interval)
    {
        if (interval < 1)
        {
            throw new ArgumentOutOfRangeException("interval", "Interval must be at least 1 millisecond.");
        }

        if (Interlocked.CompareExchange(ref this.started, 1, 0) == 1)
        {
            return;
        }

        NativeMethods.CreateTimerQueueTimer(
            out this.handle,
            IntPtr.Zero,
            this.callback,
            IntPtr.Zero,
            (uint)interval,
            (uint)interval,
            CallbackOptions.ExecuteInTimerThread);
    }

    public virtual void Stop()
    {
        if (Interlocked.CompareExchange(ref this.started, 0, 1) == 0)
        {
            return;
        }

        NativeMethods.DeleteTimerQueueTimer(IntPtr.Zero, this.handle, IntPtr.Zero);
    }

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

    private void ticked(IntPtr parameterPointer, bool unused)
    {
        if (!this.AutoReset)
        {
            this.Stop();
        }

        Thread.CurrentThread.Priority = this.Priority;
        var elapsed = this.Elapsed;
        if (elapsed != null)
        {
            elapsed(this, EventArgs.Empty);
        }
    }

    private int started;
    private IntPtr handle;
    private volatile bool autoReset;
    private ThreadPriority priority;
    private readonly TimerCallback callback;
}

The problem is, after awhile I'm getting an SEHException when calling Start and Stop simultaneously from multiple threads. The Interlocked.CompareExchange methods should prevent DeleteTimerQueueTimer from being called once after Stop() is called, right? Even if Stop() is called simultaneously from different threads?

The SEHException is being thrown at DeleteTimerQueueTimer(); I assume it's because it's trying to delete a timer that has already been deleted, making the handle invalid. Doesn't the CompareExchange prevent DeleteTimerQueueTimer from being called more than once, even by multiple threads simultaneously?

Upvotes: 1

Views: 201

Answers (1)

Matt
Matt

Reputation: 6050

The function Interlocked.CompareExchange prevent the variable 'started' from modified at same time from 2 threads, but the handle of the timer is the real one you want to protected, but the code fails to do in some cases.

For example, thread A call the start function, it execute the function Interlocked.CompareExchange and then this.started is 1; at this time thread A call the stop function, it sees that 'started' is one, so it will call function DeleteTimerQueueTimer to delete the timer, while the timer might not be created yet and the handle is invalid.

So you should protect the handle of the timer

Upvotes: 1

Related Questions