RSS 2.0

Personal Info:

Joe Send mail to the author(s) leads the architecture of an experimental OS's developer platform, where he is also chief architect of its programming language. His current mission is to enable writing large-scale software that is reliable, secure, and scalable by-construction. Before this, Joe founded the Parallel Extensions to .NET project. He has been granted 19 patents, with 49 pending. When not working, Joe enjoys travelling with his wife, writing books, writing music, studying music theory & mathematics, and doing anything involving food & wine.

My books

My music

Disclaimer:
The content of this site are my own personal opinions and do not represent my employer's view in anyway.

© 2012, Joe Duffy

 
 Monday, December 06, 2004

A guarded wait is a locking primitive which enters an object's monitor and performs some processing once a certain predicate condition arises. You could imagine a buffer which refills itself once its contents have been consumed, a consumer which empties a buffer once it reaches a certain capacity, and so on.

Using the new Predicate<T> class in the Framework 2.0, the following is a pretty straightforward implementation of such a construct:

public class GuardedWait<T>

{

 

    private Predicate<T> predicate;

 

    public GuardedWait(Predicate<T> p)

    {

        predicate = p;

    }

 

    public bool IsTrue(T on)

    {

        return predicate(on);

    }

 

    public bool Wait(T on)

    {

        return Wait(on, -1);

    }

 

    public bool Wait(T on, int millisecondsTimeout)

    {

        int counter = millisecondsTimeout;

        while (true)

        {

            long beginTick = DateTime.Now.Ticks;

            lock (on)

            {

                if (!Monitor.Wait(on, counter))

                    return false;

                if (IsTrue(on))

                    return true;

            }

            counter -= (int)new TimeSpan(

                DateTime.Now.Ticks - beginTick).TotalMilliseconds;

            if (counter <= 0)

                return false;

        }

    }

 

}

As you can see, the public contract is very simple and familiar. It has a constructor which takes a predicate operation. This is the condition to guard on, that is, when waiting this is the condition that must be true for the lock to be considered successful. Then we have two simple bool Wait(...) operations which are very much like the Monitor.Wait(...) methods. This relies on the use of Monitor.Notify*() in order to wake up the wait class to check for its predicate condition.

Lastly, consider a simple example of its use. In this snippet, we share a queue between a producer and a consumer. Assume there is a contract in place that the consumer never lets the queue get beyond a certain threshold, and will deplete the buffer to half of its capacity each time it reaches such a threshold. This code effectively accomplishes this:

class GuardedWaitTest

{

    public static void Main(string[] args)

    {

        Queue<int> q = new Queue<int>();

        Producer(q);

        Consumer(q, 150);

        Thread.Sleep(5000);

    }

 

    static void Producer(Queue<int> q)

    {

        Thread t = new Thread(delegate()

        {

            Random r = new Random();

            while (true)

            {

                lock (q)

                {

                    int next = r.Next();

                    q.Enqueue(next);

                    Console.WriteLine("Produced {0} [{1}]", next, q.Count);

                    Monitor.Pulse(q);

                }

            }           

        });

        t.IsBackground = true;

        t.Start();

    }

 

    static void Consumer(Queue<int> q, int count)

    {

        Thread t = new Thread(delegate()

        {

            GuardedWait<Queue<int>> wait = new GuardedWait<Queue<int>>(delegate(Queue<int> tq)

            {

                return tq.Count >= count;

            });

            while (true)

            {

                lock (q)

                {

                    if (wait.Wait(q))

                    {

                        while (q.Count >= (count / 2))

                        {

                            int consumed = q.Dequeue();

                            Console.WriteLine("Consumed {0} [{1}]", consumed, q.Count);

                        }

                    }

                }

            }

        });

        t.IsBackground = true;

        t.Start();

    }

 

}

This code is relatively straightforward, albeit verbose because I am trying to carefully orchestrate the interaction between threads. Producer simply generates random numbers and Enqueue()s them into the shared Queue. Consumer uses the GuardedWait<T> class to “wake up” when (in this case) 150 items have been placed into the queue. It then consumed half of these, and relinquishes the lock back to the Producer. Obviously a simple example, but it should give you a good idea of when such a construct might be useful.

12/6/2004 10:31:40 PM (Pacific Standard Time, UTC-08:00)  #   
Tracked by:
http://www.bluebytesoftware.com/blog/PermaLink.aspx?guid=ee3e3dbf-e166-4930-bf57... [Pingback]
"buy valium" (buy valium) [Trackback]
"valium" (valium) [Trackback]
"rv part store" (on line) [Trackback]
"buy chip poker set" (on line) [Trackback]
"order tramadol" (on line) [Trackback]
"finance mortgage" (on line) [Trackback]

 

Recent Entries:

Search:

Browse by Date:
<December 2004>
SunMonTueWedThuFriSat
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

Browse by Category:

Notables: