IanG on Tap

Ian Griffiths in Weblog Form (RSS 2.0)

Blog Navigation

April (2018)

(1 item)

August (2014)

(1 item)

July (2014)

(5 items)

April (2014)

(1 item)

March (2014)

(1 item)

January (2014)

(2 items)

November (2013)

(2 items)

July (2013)

(4 items)

April (2013)

(1 item)

February (2013)

(6 items)

September (2011)

(2 items)

November (2010)

(4 items)

September (2010)

(1 item)

August (2010)

(4 items)

July (2010)

(2 items)

September (2009)

(1 item)

June (2009)

(1 item)

April (2009)

(1 item)

November (2008)

(1 item)

October (2008)

(1 item)

September (2008)

(1 item)

July (2008)

(1 item)

June (2008)

(1 item)

May (2008)

(2 items)

April (2008)

(2 items)

March (2008)

(5 items)

January (2008)

(3 items)

December (2007)

(1 item)

November (2007)

(1 item)

October (2007)

(1 item)

September (2007)

(3 items)

August (2007)

(1 item)

July (2007)

(1 item)

June (2007)

(2 items)

May (2007)

(8 items)

April (2007)

(2 items)

March (2007)

(7 items)

February (2007)

(2 items)

January (2007)

(2 items)

November (2006)

(1 item)

October (2006)

(2 items)

September (2006)

(1 item)

June (2006)

(2 items)

May (2006)

(4 items)

April (2006)

(1 item)

March (2006)

(5 items)

January (2006)

(1 item)

December (2005)

(3 items)

November (2005)

(2 items)

October (2005)

(2 items)

September (2005)

(8 items)

August (2005)

(7 items)

June (2005)

(3 items)

May (2005)

(7 items)

April (2005)

(6 items)

March (2005)

(1 item)

February (2005)

(2 items)

January (2005)

(5 items)

December (2004)

(5 items)

November (2004)

(7 items)

October (2004)

(3 items)

September (2004)

(7 items)

August (2004)

(16 items)

July (2004)

(10 items)

June (2004)

(27 items)

May (2004)

(15 items)

April (2004)

(15 items)

March (2004)

(13 items)

February (2004)

(16 items)

January (2004)

(15 items)

Blog Home

RSS 2.0

Writing

Programming C# 5.0

Programming WPF

Other Sites

Interact Software

When Synchronous Locking Goes Bad

Tuesday 17 August, 2004, 01:04 PM

In my previous entry, I discussed Jason Whittington's recent example showing an alternate technique for protecting shared data. I noted how it was reminiscent of how things were once done in kernel mode ISRs. I also pointed out that as of Windows XP, the kernel added support for a straightforward 'acquire, do, release' approach to locking even for data accessible to the ISR.

But I also said that the 'acquire, do, release' approach is not always appropriate, and I'd like to elaborate on that here. Sometimes the callback model is the right one. This is because sometimes, there is no lock. And this is not some lame Matrixesque existentialism for dummies - sometimes there just isn't a lock.

It is precisely for these scenarios that ISynchronizeInvoke was invented. ISynchronizeInvoke is an interface that presents the ability to execute code in some particular context. Being an abstract interface, it does not say anything about what that context might be - it is up to an implementation to decide which aspects of the context are significant.

For example, it would be possible to provide an implementation of ISynchronizeInvoke that calls you back after obtaining a particular lock. This is similar in concept to the KeSynchronizeExecution API I mentioned in my previous entry, and also similar to Jason's sample. So in this particular case, there is a lock. (It's right next to the spoon.)

Windows Forms provides a completely different implementation though. The Control class implements ISynchronizeInvoke, and in that implementation, the distinguishing feature of the context in which it calls you back is that you're allowed to do stuff to the UI. This means it will be calling you on a certain thread - because of the way Windows Forms works, you're only allowed to use a control if you are on the right thread.

This provides us, finally, with a convincing answer to the question posed in the previous entry - what's wrong with the 'acquire, do, release' approach? Why can't we do something like this:

// NOTE: not real code!
control.EnterUIContext();
try
{
    control.BackColor = Color.PeachPuff;
}
finally
{
    control.ExitUIContext();
}

Once you are aware that in Windows Forms, you have to be running on a specific thread before you can touch the UI, it's clear that the above code is never going to be workable. (And not just because of the tasteless colour scheme.) The code to update the UI has to be in a separate function so that it can be called from the appropriate context - it's just not possible to switch to a different thread mid-function. Of course Whidbey's anonymous functions make it possible to write code that looks quite close to how it would look if you could switch threads mid function:

control.BeginInvoke((MethodInvoker) delegate
{
    control.BackColor = Color.PeachPuff;
});

(Aside: I've not yet managed to find a coding style that I'm happy with for this kind of anonymous function usage though...) But that's just a handy syntax for writing an extra function that has access to your scope. Anyway, I digress; back to the main topic.

At one point it was looking like Avalon was going to change this. As I mentioned in a blog entry a few months ago, the PDC build of Longhorn does support this 'acquire, do, release' approach for its UI context. It was written so that it didn't care which thread you happened to update the UI on so long as you were in possession of the UI context. The UI context felt much more like a lock.

However, Chris Anderson wrote a response indicating that this may change. The problem is that if you ever use any pre-Avalon features in your Avalon UI, suddenly the UI context gets pinned to a thread just like today. This means that if you attempt to use the 'acquire, do, release' approach from an arbitrary thread, it will in fact fail. This feature is still in the WinHEC build, but it may well go away in due course. And even if it remains, the callback approach is the only one that can be depended upon in all scenarios.

This shows the advantage that a callback approach has over a synchronous 'acquire, do, release' approach. If we take the callback function approach exemplified by KeSynchronizeExecution, ISynchronizeInvoke, and Jason's example, then it becomes possible for the chosen block of code to be executed in whatever context is deemed most appropriate by the relevant framework. (The relevant framework here being the Windows kernel, the ISynchronizeInvoke implementation, or Jason, respectively). If that means running the code on another thread, as in the case of the Windows Forms ISynchronizeInvoke implementation, then so be it. And if the API is designed right, it can allow us the option to have the relevant code called asynchronously. (This is possible with ISynchronizeInvoke's BeginInvoke method.) This simply cannot be done with a linear 'acquire, do, release' approach where all the code is written inline as a single method.

Given that an awful lot of the bugs that afflict multithreaded code are deadlocks caused by having multiple threads all sitting around waiting for the same locks, this makes me wonder if the callback approach mightn't be a better one for lock acquisition after all. Jason's example works synchronously, and calls the code back on the same thread, but it wouldn't have to. You could easily imagine an implementation where all access to a particular data structure ends up being marshalled to the same thread inside of Jason's DoModify method - that would give you synchronization without locks. And an asynchronous version of this idea would make deadlock much easier to avoid. Of course the downside is that it then becomes extremely difficult to predict exactly what order things will happen in, so maybe it's not such a great idea after all. Maybe I'll try next time I have some free time to work on one of my own projects and see how it works out.

Copyright © 2002-2024, Interact Software Ltd. Content by Ian Griffiths. Please direct all Web site inquiries to webmaster@interact-sw.co.uk