Search code examples
c#autoreseteventwaithandle

Using an array of WaitHandles with WaitAny()


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;
using System.ComponentModel;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        class WakeUp
        {

            public static SafeWaitHandle[] tSafeWaitHandles = new SafeWaitHandle[6];
            public static WaitHandle[][] waitHandles = new WaitHandle[6][];

            bool rslt;

            //Various imports of kernel32.dll so the waitable timer can be set
            //on the system
            [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);

            [DllImport("kernel32.dll", SetLastError = true)]
            public static extern bool CancelWaitableTimer(SafeWaitHandle hTimer);

            //SafeHandle.DangerousGetHandle Method
            [DllImport("kernel32.dll", SetLastError = true)]
            [return: MarshalAs(UnmanagedType.Bool)]
            public static extern bool CloseHandle(IntPtr hObject);

            //The constructor will use a TimeSpan to set a waitable timer
            public WakeUp(string wtName, int alarmNum)
            {
                tSafeWaitHandles[alarmNum] = CreateWaitableTimer(IntPtr.Zero, true, wtName);
            }

            public int initWakeUp(TimeSpan smParam, int alarmNum)
            {
                //The number of ticks needs to be negated to set a waitable timer in this fashion
                long waketicks = -smParam.Ticks;
                rslt = SetWaitableTimer(tSafeWaitHandles[alarmNum], ref waketicks, 0, IntPtr.Zero, IntPtr.Zero, true);
                return Convert.ToInt32(rslt);
            }

            public int DoWork(int alarmNum)
            {
                waitHandles[0] = new WaitHandle[] 
                {
                    new AutoResetEvent(false),
                    new AutoResetEvent(false)
                };


                waitHandles[alarmNum][0].SafeWaitHandle = tSafeWaitHandles[alarmNum];

                WaitHandle.WaitAny(waitHandles[alarmNum]);

                Thread.Sleep(5000);


                return 0;
            }

        } 

        static void Main(string[] args)
        {
            Console.WriteLine( DateTime.Now );

            WakeUp temp = new WakeUp("spalarm1", 0);
            temp.initWakeUp(TimeSpan.FromMinutes(1), 0);
            temp.DoWork(0);

            //I would like an optional Set which will cause DoWork to stop blocking
            //when set is called. Is this possible to do?
            //WakeUp.waitHandles[0][1].Set();

            Console.WriteLine(DateTime.Now);
            Console.WriteLine("Done...");
            Console.ReadKey();
        }
    }
}

The program sets a waitable timer. That part works fine. The thread is blocking until the timer triggers. What I would like to do is to be able to call .Set on one of the WaitHandles to release the thread from blocking. It seems that the way this code is written .Set is not available so I have commented that line out for right now. I need to be able to make a call to one of the timers to release the thread from blocking. Does anyone know how to do this?

The problem I have right now is my call to set ends with:

Error 1 'System.Threading.WaitHandle' does not contain a definition for 'Set' and no extension method 'Set' accepting a first argument of type 'System.Threading.WaitHandle' could be found (are you missing a using directive or an assembly reference?) C:\Users\Eric\Documents\Visual Studio 2013\Projects\waitany\ConsoleApplication1\Program.cs 86 38 ConsoleApplication1


Solution

  • The Set method is declared in EventWaitHandle (the base class for AutoResetEvent and ManualResetEvent), not in WaitHandle. Since the WakeUp class exposes the AutoResetEvents as an array of WaitHandles, you can't call Set on them directly. Anyway, a cleaner approach would be to add a specific method in the WakeUp class to set the wait handles; exposing them publicly breaks encapsulation.