1

Is it possible (in a meaningful way) ?

For example, say I'd like to implement a waitable queue as follows:

public class WaitableQueue : WaitHandle {
    public WaitableQueue() {
      q = new Queue();
    }

    public void Enqueue(Object Arg) {
      lock(this) {
        q.Enqueue(Arg);
        // set the waithandle here (how?)
      }
    }

    public Type Dequeue() {
      lock(this) {
        if(q.Count == 1)
        // reset the waithandle here (how?)
        return q.Dequeue();
      }
    }

    Queue q;
  }
schwrz
  • 56
  • 7

4 Answers4

7

The important thing to remember is that you must set the SafeWaitHandle property.

From MSDN:

When you derive from WaitHandle, use the SafeWaitHandle property to store your native handle operating system handle. You do not need to override the protected Dispose method unless you use additional unmanaged resources.

Here is how I would do this.

public class QueueWaitHandle<T> : WaitHandle
{
    private Queue<T> queue = new Queue<T>();
    private ManualResetEvent signal = new ManualResetEvent(false);

    public QueueWaitHandle()
    {
        base.SafeWaitHandle = signal.SafeWaitHandle;
    }

    public void Enqueue(T item)
    {
        lock (queue)
        {
            queue.Enqueue(item);
            signal.Set();
        }
    }

    public T Dequeue()
    {
        lock (queue)
        {
            T item = queue.Dequeue();
            if (queue.Count == 0)
            {
                signal.Reset();
            }
            return item;
        }
    }
}

When done this way Enqueue acts like the Set method and Dequeue acts like the Reset method. So basically this works like a counting event where non-zero is signaled and zero is unsignaled. The queue is doing the counting in this case and it just happens to also hold data.

I know you were asking about subclassing WaitHandle in general, but this specific data structure is more than just an exercise. It can be useful in some scenarios. I would not, however, call it a waitable queue because that implies, at least to me anyway, that the Dequeue operation will block when the queue is empty. Clearly that is not what will happen in this particular implemenation.

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
  • You can always have Dequeue(bool Blocking) but in any case that's the solution I was looking for. I would have expected to have protected Set and Reset or alternatively the protected ctor to have a parameter, as in WaitHandle(SafeWaitHandle Handle). I could find neither of those and that's what threw me off (shamefully I also missed the MSDN bit you quoted). My question was indeed general but it did arise from a real-life scenario and your solution will be put to good use. Thanks! – schwrz Dec 02 '11 at 15:27
3

The EventWaitHandle class has a Set and Reset method, so you could inherit from that instead of WaitHandle.

However, I would say that for the example provided, I don't think this would be a good implementation, because the SafeWaitableQueue class now has two different roles: a queue and a wait handle.

But, if you have something else in mind where you need to implement your own kind of wait handle, I'd suggest trying to inherit from EventWaitHandle.

It's not a great solution, because the Set and Reset methods are publicly exposed, meaning that consumers of your SafeWaitableQueue class could also call Set and Reset, probably resulting in some decidedly unsafe behavior.

Dr. Wily's Apprentice
  • 10,212
  • 1
  • 25
  • 27
  • I assumed EventWaitHandle would be sealed just like AutoResetEvent and ManualResetEvent. The exposure problem, as you correctly pointed out, is an issue. – schwrz Dec 02 '11 at 14:24
1

I'd create a class which aggregates both Queue AND WaitHandle, so:

public class WaitableQueue<T>
{
    private Queue<T> _queue;
    private WaitHandle _waitHandle;

    public WaitableQueue()
    {
        _queue = new Queue<T>();
        _waitHandle = new WaitHandle();
    }

    public void Enqueue(T Arg) {
      lock(this) {
        _queue.Enqueue(Arg);
        _waitHandle.Set();
      }
    }
    ...
}
Sergey Kudriavtsev
  • 10,328
  • 4
  • 43
  • 68
  • 1
    Perhaps _waitHandle should be exposed as a read-only public property and/or there should be a constructor overload that accepts a WaitHandle as a parameter. That way, you can use this WaitableQueue class, and if you need to do WaitHandle.WaitAny(q1,q1,...) then you can still do so because you have access to the WaitHandle that the queue is utilizing. – Dr. Wily's Apprentice Dec 02 '11 at 13:46
  • 1
    You will be able to do it if you expose _waitHandle as a public `get` property – Sergey Kudriavtsev Dec 02 '11 at 13:46
  • 1
    @schwrz But you could call WaitableQueue.WaitAny(q1, q2) as long as you provide a method to do that. This has the advantage of encapsulating the wait handle. But if your intent is to allow your queue to intermix with other waithandles then your approach should work, – tcarvin Dec 02 '11 at 13:53
  • The property solution is good for the example I gave, I've even used it in real life situations in the past but what I'm ACTUALLY wondering about is: Can you derive from WaitHandle? (see the question's title) – schwrz Dec 02 '11 at 14:06
  • @tcarvin I never thought to implement WaitAny. Anyway, my intent was indeed to have a flexible structure that can be mixed with other handles, but thank you for stating what should have been an obvious alternative. – schwrz Dec 02 '11 at 15:08
0

I think that is not a good use of inheritance. Your object is a queue and uses a wait handle to provide thread safety, so you shouldn't be derived from WaitHandle.

jlew
  • 10,491
  • 1
  • 35
  • 58
  • I'm not specifically looking to implement a waitable queue. It was just an example. I'm asking if WaitHandle can be derived in a meaningful way (see my original post) – schwrz Dec 02 '11 at 13:38
  • 1
    It is an abstract base class, so it is derived from in a meaningful way by all of the concrete synchronization classes in the .NET framework (Mutex, Semaphore, etc). I'm sure you could use it to develop another type of synchronization primitive that is not supported out of the box by .NET. – jlew Dec 02 '11 at 13:43
  • How would you go about doing that? It seems to me that it can't be done using .NET primitives as you have no control over the inherited handle. – schwrz Dec 02 '11 at 13:50
  • Careful with this, can't make GetEnumerator() thread-safe. Should therefore not inherit Queue<> – Hans Passant Dec 02 '11 at 13:51
  • I know. My example does not inherit Queue. – schwrz Dec 02 '11 at 14:00
  • I believe you would need to receive the native handle from the operating system. I'd guess that .NET already implements wrappers for all the different types of native Windows handles, so it would be potentially useful for an implementation on a different OS. – jlew Dec 02 '11 at 14:00