API's that Suck

October 24, 2009

Making Reader/Writer Locks More Palatable

Filed under: Uncategorized — Grauenwolf @ 10:16 pm

The lock most .NET developers are most familiar is the Monitor. Used via the handy lock/SyncLock construct, it is suitable for most low-contention applications. But when many threads are all trying to access a protected resource at the same time, something a bit more sophisticated is in order. If the number of readers vastly outnumber the number of writers, a reader/writer lock is in order. Here is how Microsoft recommends using it:

try
{
    theLock.EnterReadLock();
    //code
}
finally
{
    theLock.ExitReadLock();
}

This is quite tedious compared the lock/SyncLock construct.

lock (theLock)
{
    //code
}

So how we fix this? It would be nice if Microsoft were to extend the language somehow to allow for a nice syntax in a variety of locks. But since that is probably not going to happen, let us look at the original example again. Looks familiar, doesn’t it? Somewhat like the usage pattern of IDisposable objects.

try
{
    obj = new Resource();
    //code
}
finally
{
    if (obj != null) obj.Dispose();
}

Aside from a few poor souls stuck using VB 7, no one actually writes code like that.  Instead we use the Using construct.

using (obj = new Resource())
{
    //code
}

So now we have our construct, but how do we actually use it? By creating a shim that can be disposed. First, let us look at how we want to call it:

using (theLock.ReadSection())
{
    //code
}

That is about as easy as it is going to get in C#/VB . As for the shim, it is a private class that links back to the real Reader/Writer Lock. Developers should never really know they are using this object, from their perspective they just happened to call ReadSection inside a using construct’s header.

    <Extension()> Public Function ReadSection(ByVal lock As Global.System.Threading.ReaderWriterLockSlim) As IDisposable
        Return New LockToken(lock, LockMode.Read)
    End Function

    <Extension()> Public Function UpgradeableReadSection(ByVal lock As Global.System.Threading.ReaderWriterLockSlim) As IDisposable
        Return New LockToken(lock, LockMode.Upgradable)
    End Function

    <Extension()> Public Function WriteSection(ByVal lock As Global.System.Threading.ReaderWriterLockSlim) As IDisposable
        Return New LockToken(lock, LockMode.Write)
    End Function

    Private NotInheritable Class LockToken
        Implements IDisposable
        Private m_Mode As LockMode
        Private m_Lock As ReaderWriterLockSlim
        Private m_Disposed As Boolean = False

        Friend Sub New(ByVal lock As ReaderWriterLockSlim, ByVal mode As LockMode)
            m_Lock = lock
            m_Mode = mode
            Select Case mode
                Case LockMode.Read
                    m_Lock.EnterReadLock()
                Case LockMode.Upgradable
                    m_Lock.EnterUpgradeableReadLock()
                Case LockMode.Write
                    m_Lock.EnterWriteLock()
            End Select
        End Sub

        Private Sub Dispose(ByVal disposing As Boolean)
            If Not Me.m_Disposed Then
                If disposing Then
                    Select Case m_Mode
                        Case LockMode.Read
                            m_Lock.ExitReadLock()
                        Case LockMode.Upgradable
                            m_Lock.ExitUpgradeableReadLock()
                        Case LockMode.Write
                            m_Lock.ExitWriteLock()
                    End Select
                End If
                m_Lock = Nothing
            End If
            Me.m_Disposed = True
        End Sub

        Private Sub Dispose() Implements IDisposable.Dispose
            Dispose(True)
        End Sub

    End Class

    Private Enum LockMode
        Read
        Upgradable
        Write
    End Enum

End Module

The code for this is in version 2.2 of ClrExtensions.

On the Stupid Inconsistencies between CLR Nulls, DB Nulls, and Nullable Nulls.

Filed under: Uncategorized — Grauenwolf @ 10:28 am

One of the most aggravating things about .NET is the false distinction between nulls. While semantically there is no difference between the three main types of nulls, developers are required to track them separately. As I have never seen anyone choose to use more than type of null in the same variable, I can’t help but think it is simply a horrible mistake.

Converting from CLR and Nullable nulls is no big deal. Not only is it rare, boxing and unboxing automatically takes care of the conversion. The real issue is going from DB nulls to the other two types. Here is some of the gyrations that VB and C# developers have to go through when reading from a DataTable.

name = row.IsNull("Name") ? null : (string)row["Name"];
name = row["Name"] != DBNull.Value ? null : (string)row["Name"];
name = row["Name"] as string;

name = If(row.IsNull("Name"), Nothing, CStr(row("Name")))
name = If(row("Name") IsNot DBNull.Value, Nothing, CStr(row("Name")))
name = TryCast(row("Name"), String)

As you can see, this can get quite tedious very quickly. And what’s worse, the shortest versions only work for strings. All the other primitive data types like DateTime and Int32 are structs, and thus don’t qualify. What we get instead is this mess.

age = If(row.IsNull("Age"), Nothing, CInt(row("Age")))
age = If(row("Age") IsNot DBNull.Value, Nothing, CInt(row("Age")))

Can you see the bug? It’s subtle, but if you think about it for a moment it makes sense. The If expression is inferring the return type Int32, not Nullable<Int32>, because that is the only type it sees. It is the = symbol that implicitly converts it from an Int32 to a Nullable<int32>. As for the Nothing keyword, this is considered to be a 0 in this context.

Since C# lacks an equivalent to Nothing, only the semantically valid version will compile. Without inference to add us, we have to use the slightly longer forms.

age = row.IsNull("Age") ? new int?() : (int)row["Age"];
age = row["Age"]  == DBNull.Value ? new int?() : (int)row["Age"];
age = If(row.IsNull("Age"), New Integer?, CInt(row("Age")))
age = If(row("Age") IsNot DBNull.Value, New Integer?, CInt(row("Age")))

There is another variant I see people try to use from time to time, especially with C#.

name = Convert.ToString(row["Name"]);
age = Convert.ToInt32(row["Age"]);

These two lines look reasonable, but both are wrong. The first converts DB Nulls not into a null string as expected, but rather an empty string. The second throws an exception when DB Nulls are encountered.

What should happen

Ideally Microsoft would just add an explicit cast from DBNull to whatever reference or nullable type you are targeting. But that isn’t very likely after all this time.

Workarounds

For VB, I am using an extension of its well known CXxx functions. I would like to name my functions CXxx?, but since that isn’t an option I am using the visually similar CXxx2. For example:

name = CStr2(row("Name"))
age = CInt2(row("Age"))

In C# you cannot avoid including the static class name when calling a function. This encourages to turn to extension methods on object.

name = row["Name"].FromDBString();
age = row["Age"].FromDBInt();

Source Code

If you want them, source code for the CXxx? methods are available on CodePlex.

Create a free website or blog at WordPress.com.