One of the many things missing from the Silverlight API is a ReaderWriterLock implementation, though it is not a day-to-day requisite yet sometimes you really need it. The good news is, the underpinnings for it's implementation are all their, basically carried in-tact from the desktop counterpart.

I found a very good implementation by Vance Morrison, which I understand forms the basis of the ReaderWriterLockSlim in .NET 3.5 using spin locks.  Below is the Sliverlight version of the same, with some minute changes:

/// <summary>
/// A reader-writer lock implementation that is intended to be simple, yet very
/// efficient.  In particular only 1 interlocked operation is taken for any lock 
/// operation (we use spin locks to achieve this).  The spin lock is never held
/// for more than a few instructions (in particular, we never call event APIs
/// or in fact any non-trivial API while holding the spin lock).   
/// 
/// Currently this ReaderWriterLock does not support recurision, however it is 
/// not hard to add 
/// </summary>
/// <remarks>
/// By Vance Morrison
/// Taken from - http://blogs.msdn.com/vancem/archive/2006/03/28/563180.aspx
/// Code at - http://blogs.msdn.com/vancem/attachment/563180.ashx
/// </remarks>
public class ReaderWriterLock
{
    // Lock specifiation for myLock:  This lock protects exactly the local fields associted
    // instance of MyReaderWriterLock.  It does NOT protect the memory associted with the
    // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent).
    int myLock;

    // Who owns the lock owners > 0 => readers
    // owners = -1 means there is one writer.  Owners must be >= -1.  
    int owners;

    // These variables allow use to avoid Setting events (which is expensive) if we don't have to. 
    uint numWriteWaiters;        // maximum number of threads that can be doing a WaitOne on the writeEvent 
    uint numReadWaiters;         // maximum number of threads that can be doing a WaitOne on the readEvent
    uint numUpgradeWaiters;      // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). 

    // conditions we wait on. 
    EventWaitHandle writeEvent;    // threads waiting to aquire a write lock go here.
    EventWaitHandle readEvent;     // threads waiting to aquire a read lock go here (will be released in bulk)
    EventWaitHandle upgradeEvent;  // thread waiting to upgrade a read lock to a write lock go here (at most one)

    public ReaderWriterLock()
    {
        // All state can start out zeroed. 
    }

    public void AcquireReaderLock(int millisecondsTimeout)
    {
        EnterMyLock();
        for (; ; )
        {
            // We can enter a read lock if there are only read-locks have been given out
            // and a writer is not trying to get in.  
            if (owners >= 0 && numWriteWaiters == 0)
            {
                // Good case, there is no contention, we are basically done
                owners++;       // Indicate we have another reader
                break;
            }

            // Drat, we need to wait.  Mark that we have waiters and wait.  
            if (readEvent == null)      // Create the needed event 
            {
                LazyCreateEvent(ref readEvent, false);
                continue;   // since we left the lock, start over. 
            }

            WaitOnEvent(readEvent, ref numReadWaiters, millisecondsTimeout);
        }
        ExitMyLock();
    }

    public void AcquireWriterLock(int millisecondsTimeout)
    {
        EnterMyLock();
        for (; ; )
        {
            if (owners == 0)
            {
                // Good case, there is no contention, we are basically done
                owners = -1;    // indicate we have a writer.
                break;
            }

            // Drat, we need to wait.  Mark that we have waiters and wait.
            if (writeEvent == null)     // create the needed event.
            {
                LazyCreateEvent(ref writeEvent, true);
                continue;   // since we left the lock, start over. 
            }

            WaitOnEvent(writeEvent, ref numWriteWaiters, millisecondsTimeout);
        }
        ExitMyLock();
    }

    public void UpgradeToWriterLock(int millisecondsTimeout)
    {
        EnterMyLock();
        for (; ; )
        {
            Debug.Assert(owners > 0, "Upgrading when no reader lock held");
            if (owners == 1)
            {
                // Good case, there is no contention, we are basically done
                owners = -1;    // inidicate we have a writer. 
                break;
            }

            // Drat, we need to wait.  Mark that we have waiters and wait. 
            if (upgradeEvent == null)   // Create the needed event
            {
                LazyCreateEvent(ref upgradeEvent, false);
                continue;   // since we left the lock, start over. 
            }

            if (numUpgradeWaiters > 0)
            {
                ExitMyLock();
                throw new InvalidOperationException("UpgradeToWriterLock already in process.  Deadlock!");
            }

            WaitOnEvent(upgradeEvent, ref numUpgradeWaiters, millisecondsTimeout);
        }
        ExitMyLock();
    }

    public void ReleaseReaderLock()
    {
        EnterMyLock();
        Debug.Assert(owners > 0, "ReleasingReaderLock: releasing lock and no read lock taken");
        --owners;
        ExitAndWakeUpAppropriateWaiters();
    }

    public void ReleaseWriterLock()
    {
        EnterMyLock();
        Debug.Assert(owners == -1, "Calling ReleaseWriterLock when no write lock is held");
        Debug.Assert(numUpgradeWaiters > 0);
        owners++;
        ExitAndWakeUpAppropriateWaiters();
    }

    public void DowngradeToReaderLock()
    {
        EnterMyLock();
        Debug.Assert(owners == -1, "Downgrading when no writer lock held");
        owners = 1;
        ExitAndWakeUpAppropriateWaiters();
    }

    /// <summary>
    /// A routine for lazily creating a event outside the lock (so if errors
    /// happen they are outside the lock and that we don't do much work
    /// while holding a spin lock).  If all goes well, reenter the lock and
    /// set 'waitEvent' 
    /// </summary>
    private void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent)
    {
        Debug.Assert(MyLockHeld);
        Debug.Assert(waitEvent == null);

        ExitMyLock();
        EventWaitHandle newEvent;
        if (makeAutoResetEvent)
            newEvent = new AutoResetEvent(false);
        else
            newEvent = new ManualResetEvent(false);
        EnterMyLock();
        if (waitEvent == null)          // maybe someone snuck in. 
            waitEvent = newEvent;
    }

    /// <summary>
    /// Waits on 'waitEvent' with a timeout of 'millisceondsTimeout.  
    /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine.
    /// </summary>
    private void WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, int millisecondsTimeout)
    {
        Debug.Assert(MyLockHeld);

        waitEvent.Reset();
        numWaiters++;

        bool waitSuccessful = false;
        ExitMyLock();      // Do the wait outside of any lock 
        try
        {
            if (!waitEvent.WaitOne(millisecondsTimeout))
                throw new InvalidOperationException("ReaderWriterLock timeout expired");
            waitSuccessful = true;
        }
        finally
        {
            EnterMyLock();
            --numWaiters;
            if (!waitSuccessful)        // We are going to throw for some reason.  Exit myLock. 
                ExitMyLock();
        }
    }

    /// <summary>
    /// Determines the appropriate events to set, leaves the locks, and sets the events. 
    /// </summary>
    private void ExitAndWakeUpAppropriateWaiters()
    {
        Debug.Assert(MyLockHeld);

        if (owners == 0 && numWriteWaiters > 0)
        {
            ExitMyLock();      // Exit before signaling to improve efficiency (wakee will need the lock)
            writeEvent.Set();   // release one writer. 
        }
        else if (owners == 1 && numUpgradeWaiters != 0)
        {
            ExitMyLock();          // Exit before signaling to improve efficiency (wakee will need the lock)
            upgradeEvent.Set();     // release all upgraders (however there can be at most one). 
            // two threads upgrading is a guarenteed deadlock, so we throw in that case. 
        }
        else if (owners >= 0 && numReadWaiters != 0)
        {
            ExitMyLock();    // Exit before signaling to improve efficiency (wakee will need the lock)
            readEvent.Set();  // release all readers. 
        }
        else
            ExitMyLock();
    }

    private void EnterMyLock()
    {
        if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0)
            EnterMyLockSpin();
    }

    private void EnterMyLockSpin()
    {
        for (int i = 0; ; i++)
        {
            if (i < 3 && Environment.ProcessorCount > 1)
                Thread.SpinWait(20);    // Wait a few dozen instructions to let another processor release lock. 
            else
                Thread.Sleep(0);        // Give up my quantum.  

            if (Interlocked.CompareExchange(ref myLock, 1, 0) == 0)
                return;
        }
    }
    private void ExitMyLock()
    {
        Debug.Assert(myLock != 0, "Exiting spin lock that is not held");
        myLock = 0;
    }

    private bool MyLockHeld { get { return myLock != 0; } }

}

This implementation features both timeouts and upgrading to writer locks, however it does not support recursion or try enter lock usage. Also, it's relatively fast, in some basic testings I did it ran 3x-4x v/s Monitor Locks, which compares well to other less feature-rich implementations (see here) that run 8x-10x v/s the generic Monitor Locks.  And just as a resource, you can read more about locks in general from this entry by Jeff Moser on his blog.

Posted by Rishi on 08-Mar-09 5:20 AM, 20 Comments

Categories: Code, Silverlight

Earlier, I was looking for some wrappers around the .NET zip class as they were a bit cumbersome to use, so I found a ground wrapper, which I've improved upon with some more additional methods. You may find them useful, particularly when consuming stuff from the internet which is compressed over HTTP.

Using GZip Compression:

public static class GZipHelper
{

    public static long Compress(Stream input, Stream output)
    {

        // Create a buffer to transfer the data
        byte[] buffer = new byte[1024];
        int bytesRead = 0;
        long totalBytes = 0;

        // Get the stream that will perform the decompression
        using (Stream zip = new GZipStream(output, CompressionMode.Compress))
        {

            // Use the buffer to move data to the compression stream until complete
            while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
            {

                totalBytes += bytesRead;
                zip.Write(buffer, 0, bytesRead);

            }

            // Close the compression stream
            zip.Close();

        }

        // Return the total number of bytes of compressed data
        return totalBytes;

    }

    public static long Decompress(Stream input, Stream output)
    {

        // Create a buffer to transfer the data
        byte[] buffer = new byte[1024];
        int bytesRead = 0;
        long totalBytes = 0;

        // Get the stream that will perform the decompression
        using (Stream zip = new GZipStream(input, CompressionMode.Decompress, true))
        {

            // Use the buffer to move data to the compression stream until complete
            while ((bytesRead = zip.Read(buffer, 0, buffer.Length)) > 0)
            {

                totalBytes += bytesRead;
                output.Write(buffer, 0, bytesRead);

            }

            // Close the compression stream
            zip.Close();

        }


        // Returns the total number of bytes of decompressed data
        return totalBytes;

    }

    public static byte[] Compress(byte[] data)
    {

        byte[] final;

        // Create a stream to hold the output
        using (MemoryStream output = new MemoryStream(), input = new MemoryStream(data))
        {

            // Process the compression
            Compress(input, output);

            // Get the resultant data
            final = output.ToArray();

            // Close the underlying streams
            input.Close();
            output.Close();

        }

        // Convert the output stream to a byte array
        return final;

    }

    public static byte[] Decompress(byte[] gzipData)
    {

        byte[] final;

        // Create a stream to hold the output
        using (MemoryStream output = new MemoryStream(), input = new MemoryStream(gzipData))
        {

            // Process the compression
            Decompress(input, output);

            // Get the resultant data
            final = output.ToArray();

            // Close the underlying streams
            input.Close();
            output.Close();

        }

        // Convert the output stream to a byte array
        return final;

    }

    public static string Compress(string data)
    {

        // get a byte array of the data and pass to the Compress method.
        return Convert.ToBase64String(Compress(Encoding.Default.GetBytes(data)));

    }

    public static string Decompress(string gzipString)
    {

        // Decompress and convert data to a string
        return Encoding.Default.GetString(Decompress(Convert.FromBase64String(gzipString)));

    }

    public static string Decompress(Stream input)
    {

        byte[] final;

        // Create a stream to hold the output
        using (MemoryStream output = new MemoryStream())
        {

            // Process the compression
            Decompress(input, output);

            // Get the resultant data
            final = output.ToArray();

            // Close the underlying streams
            input.Close();
            output.Close();

        }

        // we get the string
        return Encoding.Default.GetString(final);

    }

}

Using Deflate Compression:
public static class DeflateHelper
{

    public static long Compress(Stream input, Stream output)
    {

        // Create a buffer to transfer the data
        byte[] buffer = new byte[1024];
        int bytesRead = 0;
        long totalBytes = 0;

        // Get the stream that will perform the decompression
        using (Stream zip = new DeflateStream(output, CompressionMode.Compress))
        {

            // Use the buffer to move data to the compression stream until complete
            while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
            {

                totalBytes += bytesRead;
                zip.Write(buffer, 0, bytesRead);

            }

            // Close the compression stream
            zip.Close();

        }

        // Return the total number of bytes of compressed data
        return totalBytes;

    }

    public static long Decompress(Stream input, Stream output)
    {

        // Create a buffer to transfer the data
        byte[] buffer = new byte[1024];
        int bytesRead = 0;
        long totalBytes = 0;

        // Get the stream that will perform the decompression
        using (Stream zip = new DeflateStream(input, CompressionMode.Decompress, true))
        {

            // Use the buffer to move data to the compression stream until complete
            while ((bytesRead = zip.Read(buffer, 0, buffer.Length)) > 0)
            {

                totalBytes += bytesRead;
                output.Write(buffer, 0, bytesRead);

            }

            // Close the compression stream
            zip.Close();

        }


        // Returns the total number of bytes of decompressed data
        return totalBytes;

    }

    public static byte[] Compress(byte[] data)
    {

        byte[] final;

        // Create a stream to hold the output
        using (MemoryStream output = new MemoryStream(), input = new MemoryStream(data))
        {

            // Process the compression
            Compress(input, output);

            // Get the resultant data
            final = output.ToArray();

            // Close the underlying streams
            input.Close();
            output.Close();

        }

        // Convert the output stream to a byte array
        return final;

    }

    public static byte[] Decompress(byte[] deflateData)
    {

        byte[] final;

        // Create a stream to hold the output
        using (MemoryStream output = new MemoryStream(), input = new MemoryStream(deflateData))
        {

            // Process the compression
            Decompress(input, output);

            // Get the resultant data
            final = output.ToArray();

            // Close the underlying streams
            input.Close();
            output.Close();

        }

        // Convert the output stream to a byte array
        return final;

    }

    public static string Compress(string data)
    {

        // get a byte array of the data and pass to the Compress method.
        return Convert.ToBase64String(Compress(Encoding.Default.GetBytes(data)));

    }

    public static string Decompress(string deflateString)
    {

        // Decompress and convert data to a string
        return Encoding.Default.GetString(Decompress(Convert.FromBase64String(deflateString)));

    }

    public static string Decompress(Stream input)
    {

        byte[] final;

        // Create a stream to hold the output
        using (MemoryStream output = new MemoryStream())
        {

            // Process the compression
            Decompress(input, output);

            // Get the resultant data
            final = output.ToArray();

            // Close the underlying streams
            input.Close();
            output.Close();

        }

        // we get the string
        return Encoding.Default.GetString(final);

    }

}

Posted by Rishi on 11-Feb-09 12:25 PM, 9 Comments

Categories: Code

While coding for the MIX challenge, I was trying to find as compact as a form as possible to write code. And given the repetitions, I had created a helper class (named H), see below:

static class H
{
	public static void For(int i, Action<int> a) { for (var x = 0; x < i; x++) a(x); }
	public static void For(int o, int i, Action<int> a) { for (var x = 0; x < i; x++) a(x); }
	public static void For<T>(IEnumerable<T> l, Action<T> a) { foreach (T i in l) a(i); }
	public static R While<R>(R i,Func<R,bool> e, Func<R,R> f) where R: class {while (e(i)) { i = f(i); } return i; } 
	public static T GetV<T>(Object c, DependencyProperty prop) { return (T)((DependencyObject)c).GetValue(prop); }
	public static void SetV(Object c, DependencyProperty prop, Object v) {((DependencyObject)c).SetValue(prop, v); }
	public static T New<T>(T v) where T : new() { return new T(); }
	public static T New<T>(T v, params Object[] a) { return (T)Activator.CreateInstance(typeof(T), a); }
}

This helped in reducing the characters count, for example, a double loop to initialize 15x15 squares on the board could be written like this:
for (int i = 0; i < 15; i++) 
{ 
	for (int j = 0; j < 15; j++) 
		Board.Add(new Square(j, i)); 
}
Now, using the helper class (H) the same could be written like:
H.For(15, i => H.For(15, j => Board.Add(new Square(j, i))));

LINQ and it's related features obviously are a big help in writing compact code, one of them I use extensively is automatic properties. However, as of the current version of .NET we can't initialize them, which meant writing thing like this.
public ObservableCollection<Letter> Bag { get; set; }
// and to initialize
Bag = new ObservableCollection<Letter>();

Again, for the sake of brevity I wrote the same thing with the helper as:
public ObservableCollection<Letter> Bag { get; set; }
// and to initialize
Bag = H.New(Bag);
The interesting thing is, and which I've not seen used anywhere else is, in using the generic type on the static 'New' method with the 'new' constrain and a dummy parameter - the dummy parameter ensured we didn't need to specify the type needed and also without which the compiler wouldn't be able to infer the type.

Obviously these are not production quality coding practises, rather just interesting ways to keep things under 10K.

Posted by Rishi on 05-Feb-09 9:56 PM, 11 Comments

Tags: ,
Categories: Code