2011-03-05

Semaphore with FIFO order and priorities.

Semaphore with FIFO order and priorities. Also support cancelation token.

public class OrderedList<K, V>
{
    private SortedList<K, List<V>> m_list = new SortedList<K, List<V>>();

    public void Add(K a_key, V a_value)
    {
        if (Values.Contains(a_value))
            throw new Exception();

        if (!Keys.Contains(a_key))
            m_list.Add(a_key, new List<V>());

        m_list[a_key].Add(a_value);
    }

    public IEnumerable<V> Values
    {
        get
        {
            foreach (var sublist in m_list.Values)
                foreach (var v in sublist)
                    yield return v;
        }
    }

    public IEnumerable<K> Keys
    {
        get
        {
            return m_list.Keys;
        }
    }

    public void RemoveByValue(V a_value)
    {
        foreach (var sublist in m_list.Values)
        {
            if (sublist.Remove(a_value))
                break;
        }
    }

    public int Count 
    {
        get
        {
            return Values.Count();
        }
    }

    public V RemoveFirst()
    {
        V v = Values.First();
        RemoveByValue(v);
        return v;
    }
}

public class QueuedSemaphore<P>
{
    private Object m_lock = new Object();
    private OrderedList<P, ManualResetEvent> m_queue =
        new OrderedList<P, ManualResetEvent>();
    private int m_working = 0;
    private readonly int m_count;

    public QueuedSemaphore(int a_count)
    {
        m_count = a_count;
    }

    public void WaitOne(CancellationToken a_token, P a_priority)
    {
        ManualResetEvent mre = null;

        lock (m_lock)
        {
            if (m_working == m_count)
            {
                mre = new ManualResetEvent(false);
                m_queue.Add(a_priority, mre);
            }
            else
                m_working++;
        }

        if (mre != null)
        {
            while (!mre.WaitOne(100))
            {
                if (a_token.IsCancellationRequested)
                {
                    lock (m_lock)
                    {
                        if (mre.WaitOne(0))
                            Release();
                        else if (m_queue.Values.Contains(mre))
                            m_queue.RemoveByValue(mre);
                    }
                    a_token.ThrowIfCancellationRequested();
                }
            }
        }
    }

    public void WaitOne(P a_priority)
    {
        ManualResetEvent mre = null;

        lock (m_lock)
        {
            if (m_working == m_count)
            {
                mre = new ManualResetEvent(false);
                m_queue.Add(a_priority, mre);
            }
            else
                m_working++;
        }

        if (mre != null)
            mre.WaitOne();
    }

    public void Release()
    {
        lock (m_lock)
        {
            if (m_queue.Count != 0)
            {
                m_queue.RemoveFirst().Set();
            }

            if (m_queue.Count < m_working)
                m_working = m_queue.Count;
        }
    }
}

ListBox with scroll events

Simple extension of ListBox provides you vertical scroll event.

public class ListBoxScroll : ListBox
{
    private const int WM_VSCROLL = 0x0115;
    private const int WM_HSCROLL = 0x0114;
    private const int WM_MOUSEWHEEL = 0x020A;
    private const int WM_MOUSEHWHEEL = 0x020E;
    private const int SB_THUMBTRACK = 5;
    private const int SB_ENDSCROLL = 8;

    public delegate void ListBoxScrollDelegate(object a_sender, bool a_tracking);

    public event ListBoxScrollDelegate VerticalScroll;

    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);

        if (m.Msg == WM_MOUSEWHEEL)
        {
            if (VerticalScroll != null)
                VerticalScroll(this, false);
        }

        if (m.Msg == WM_MOUSEHWHEEL)
        {
            if (VerticalScroll != null)
                VerticalScroll(this, false);
        }

        if (m.Msg == WM_VSCROLL)
        {
            int nfy = m.WParam.ToInt32() & 0xFFFF;
            if (VerticalScroll != null && (nfy == SB_THUMBTRACK || nfy ==
                   SB_ENDSCROLL))
            {
                VerticalScroll(this, nfy == SB_THUMBTRACK);
            }
        }
    }
}