May 09, 2008
Events and Threads (Part 1)
Once upon a time, I mentioned that I'd like to blog about thread-safety as it relates to events, so I figured I'd better get moving on that.
There are so many issues with .NET events and threads that it's hard to know where to begin, but let's start with the adding and removing of event handlers.
Unless documentation specifies otherwise, one must assume that adding and removing an event handler falls under the same thread safety requirements as any other method of the class. So, if the class has thread affinity (Windows Forms controls, WPF elements, etc.), assume that events can only be added and removed from the UI thread. If the class is thread-compatible (most non-UI classes in .NET), assume that events can be added and removed from any thread, but no two threads can add or remove events (or call any other method, for that matter) at the same time.
When authoring an event, if you allow C# to implement the add and remove methods (by not including your own), the default implementation attempts to be thread-safe by locking "this" before adding or removing the handler from the event delegate. In other words, these two events are implemented the same way:
public event EventHandler Event1;
public event EventHandler Event2
{
add { lock (this) m_event2 += value; }
remove { lock (this) m_event2 -= value; }
}
private EventHandler m_event2;
If your event has thread affinity or is thread-compatible, the lock is unnecessary overhead, so you're better off with a lock-free implementation:
public event EventHandler Event3
{
add { m_event3 += value; }
remove { m_event3 -= value; }
}
private EventHandler m_event3;
Better yet, if your event has thread affinity, make sure that the caller is on the UI thread.
public event EventHandler Event4
{
add { VerifyAccess(); m_event4 += value; }
remove { VerifyAccess(); m_event4 -= value; }
}
private EventHandler m_event4;
Furthermore, locking "this" is not recommended (see the MSDN documentation on the lock statement and on MethodImplOptions.Synchronized), so you might consider always implementing your own add and remove methods anyway.
While we're on the subject of adding and removing event handlers, if your class has more than a few events, consider using the EventHandlerList class to manage all of the event handlers, or manage the event handlers in a similar way with your own collection. This will save memory when many of the events have no subscribers. The EventHandlerList class is not thread-safe, which makes it most suitable for thread-affined and thread-compatible events.
There's obviously much more to discuss, not the least of which is a discussion of what it would mean for an event to be entirely thread-safe; hopefully part 2 won't be so long in coming!
Posted by Ed Ball at 10:25 AM | Comments (4) | TrackBack (0)
April 09, 2008
Exception 0xc0020001 in C++/CLI assembly
After reorganising some code in a C++/CLI assembly, I started getting exception "0xc0020001: The string binding is invalid" when shutting down the C# application that loaded that assembly.
When the program was run under the debugger, it would throw the exception from a function in crtdll.c that was processing the DLL_PROCESS_DETACH notification sent to DllMain. The error occurred when attempting to call the function pointer function_to_call (on line 444).
437 /* cache the function to call. */
438 function_to_call = (_PVFV)_decode_pointer(*onexitend);
439
440 /* mark the function pointer as visited. */
441 *onexitend = (_PVFV)_encoded_null();
442
443 /* call the function, which can eventually change __onexitbegin and __onexitend */
444 (*function_to_call)();
445
446 onexitbegin_new = (_PVFV *)_decode_pointer(__onexitbegin);
447 onexitend_new = (_PVFV *)_decode_pointer(__onexitend);
Here's where a feature of the Visual Studio debugger that I hadn't seen before came in very handy. If I set a breakpoint on line 444 and simply hovered my mouse over function_to_call, the debugger tooltip showed the full decorated name of the function, in this case, "_t2m@???__FstaticNativeObject@?1??NativeMethod@@YAHH@Z@YAXXZ@?A0x754dd9c9@@YAXXZ".
Chris Brumme explains error C0020001 and identifies one of the causes as "trying to call into managed code … after the runtime has started shutting down". According to a forum post (about this same error), "t2m" stands for "transition to managed". The information in the decorated function name ("staticNativeObject" and "NativeMethod") was enough to piece together the rest of the puzzle. I had written code much like the following:
#pragma unmanaged
class NativeClass
{
public:
NativeClass() { }
~NativeClass() { }
};
bool NativeMethod()
{
static NativeClass staticNativeObject;
return true;
}
Even though NativeMethod is emitted as native code, the disassembly showed that it registers a managed entry point for the NativeClass destructor (for staticNativeObject) with the atexit function. But by the time atexit ran this destructor (from DllMain when the C++/CLI assembly was unloaded), the CLR had already started shutting down, and the function call failed.
This problem can be solved by removing the static variable. Either make it non-static, or move it to class or file (or global!) scope. (Slightly more complex workarounds may, of course, be necessary depending on the expense or difficulty of initialising the object.)
It seems like the compiler is emitting incorrect code here—it should register a native entry point for the destructor (or call the managed version from AppDomain.DomainUnloaded) instead—so I filed a bug report with Microsoft Connect on this problem.
Posted by Bradley Grainger at 12:08 PM | Comments (0) | TrackBack (0)
April 08, 2008
Finalizers called from partially constructed objects
Did you know that finalizers are called from partially constructed objects? I certainly didn't. If an exception is thrown from a class constructor, that object is considered “partially constructed” – and its finalizer is still run when the object is garbage collected. Chris Brumme mentioned this four years ago when he helped us understand that it’s hard to implement Finalize properly: “Your Finalize method must tolerate partially constructed instances.”
A coworker discovered this fact when he was unit testing a class that called Debug.Fail in its finalizer to make sure that its instances were being disposed properly. He passed an invalid argument to the constructor to verify that an exception would be thrown – but then found that the call to Debug.Fail in the finalizer was causing tests to fail.
We couldn't figure out a good way to determine whether an object is partially constructed, so we just had to hack around the problem. Any better ideas for detecting undisposed objects?
Posted by Ed Ball at 03:49 PM | Comments (1) | TrackBack (0)
April 05, 2008
“Memory leak” with BitmapImage and MemoryStream
The code snippet below has a small “memory leak”:
BitmapImage bitmap = new BitmapImage();
byte[] buffer = GetHugeByteArray(); // from some external source
using (MemoryStream stream = new MemoryStream(buffer, false))
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = stream;
bitmap.EndInit();
bitmap.Freeze();
}
// use bitmap...
The BitmapImage keeps a reference to the source stream (presumably so that you can read the StreamSource property at any time), so it keeps the MemoryStream object alive. Unfortunately, even though MemoryStream.Dispose has been invoked, it doesn't release the byte array that the memory stream wraps. So, in this case, bitmap is referencing stream, which is referencing buffer, which may be taking up a lot of space on the large object heap. Note that there isn't a true memory leak; when there are no more references to bitmap, all these objects will (eventually) be garbage collected. But since bitmap has already made its own private copy of the image (for rendering), it seems rather wasteful to have the now-unnecessary original copy of the bitmap still in memory.
The solution here is fairly straightforward: create an implementation of Stream that wraps another stream (in this example, the MemoryStream). The Dispose method of this wrapper class needs to release the wrapped stream, so that it can be garbage collected. Once the BitmapImage is initialised with this wrapper stream, the wrapper stream can be disposed, releasing the underlying stream, and allowing the large byte array itself to be freed.
Posted by Bradley Grainger at 12:40 PM | Comments (1) | TrackBack (0)
April 03, 2008
Managed Debugging Assistant Configuration Files
MDAs (Managed Debugging Assistants) are a feature of the CLR that help track down difficult-to-diagnose bugs in complex areas such as interop and threading. Basic information about MDAs can be found in the MSDN Library topic, Stephen Toub's MSDN magazine article, and Mike Stall's blog post. For fine-grained control over the MDAs that are enabled for an application (or to use MDAs without running the application under the Visual Studio debugger), you need to use an application-specific configuration file (as well as setting the MDA registry key or COMPLUS_MDA environment variable, as detailed in those articles). Unfortunately, advanced MDA usage (with an application-specific config file) can itself be a complex and difficult-to-diagnose process.
For starters, the config file must be named properly. Like a regular application config file, the name of this file needs to include the full name of the program (including the “.exe” suffix), followed by “.mda.config”, e.g., MdaTest.exe.mda.config.
I don't know of any way to check that the MDA configuration is being read, apart from deliberately introducing an error (see below) and seeing if the runtime raises the “InvalidConfigFile” MDA when the application is run.
If there is an error in the MDA config file, the following message will be displayed when the application is run:
If you're running the application under WinDbg, you'll see the following message instead:
<mda:msg xmlns:mda="http://schemas.microsoft.com/CLR/2004/10/mda">
<!--
The 'mdaConfig' configuration file is invalid.
-->
<mda:invalidConfigFileMsg break="true" configFile="mdaConfig"/>
</mda:msg>
There are two main causes I've found for this error.
1. Incorrect XML element names. Double-check every XML element and attribute name to ensure it's spelt correctly. The basic structure of the config file is:
<mdaConfig>
<assistants>
<!-- MDA elements go here -->
</assistants>
</mdaConfig>
If any of the element names are not recognised, the InvalidConfigFile error will occur. Note that the online MSDN documentation for .NET 2.0 and .NET 3.0 has incorrect examples; be sure you're using the .NET 3.5 version of the documentation. (For example, this 2.0 page says to use “<fatalExecutionError />”, but the equivalent 3.5 page correctly gives “<fatalExecutionEngineError />” as the configuration element name.)
The MDA parser doesn't seem to log any information about why the config file is invalid; to determine the element causing the problem, remove all the elements from the config file and add them back in one-by-one (running the application after each addition) until you find the one that causes the problem.
2. Incorrectly ordered XML elements. The MDA configuration elements must be in alphabetical order, or the file will not load properly. So, this will not work:
<assistants>
<bindingFailure/>
<asynchronousThreadAbort/>
</assistants>
but this will:
<assistants>
<asynchronousThreadAbort/>
<bindingFailure/>
</assistants>
One final thing to note is that Visual Studio uses the settings in the Debug > Exceptions dialog and ignores the MDA config file when the application is started under the VS debugger. To debug with the MDA config file settings, use WinDbg or attach the Visual Studio debugger to the process after it has started.
Posted by Bradley Grainger at 01:23 PM | Comments (0) | TrackBack (0)
April 02, 2008
Author Introduction: Bradley Grainger
My name is Bradley Grainger. I completed my B.Sc. in Computer Science at the University of Auckland in 1999 and have been employed as a software developer by Logos Bible Software since 2000.
I started programming with Microsoft BASIC (and Z80 assembly language) on my dad's TRS-80 Model 4 before graduating to GW-BASIC and 80286 assembly on a Tandy 1000 TL/2 PC. Since then I've mostly used QuickBASIC, C++, JavaScript, SQL, and C#; other languages I have dabbled in include Perl, Python, Ruby, Haskell and F#. While C# is my primary language these days, every now and then I fall back to my x86 assembly roots when the law of leaky abstractions rears its ugly head and we need to figure out what's actually happening when JITted MSIL is executed; I'll be chronicling some of the more interesting discoveries in this area in the “Debugging” category.
One of the things I greatly enjoy about working at Logos is being part of a small, focused team of great coders (including my wife, who works in our web development department). If an environment that encourages great code is appealing to you, check out http://www.logos.com/jobs for our current openings.
Posted by Bradley Grainger at 09:00 AM | Comments (0) | TrackBack (0)
April 01, 2008
Handling the PropertyChanged event
When handling the PropertyChanged event of an object that implements INotifyPropertyChanged, one is tempted to write a handler like this:
void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.PropertyName == "Text")
UpdateText();
else if (e.PropertyName == "Icon")
UpdateIcon();
}
Strictly speaking, this isn't entirely correct. According to the documentation for the PropertyChanged event, “the PropertyChanged event can indicate all properties on the object have changed by using either null or string.Empty as the property name in the PropertyChangedEventArgs.” So, you're forced to write something like this:
void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
bool bAllChanged = string.IsNullOrEmpty(e.PropertyName);
if (bAllChanged || e.PropertyName == "Text")
UpdateText();
if (bAllChanged || e.PropertyName == "Icon")
UpdateIcon();
}
To make checking for this situation a little bit easier, and to increase the likelihood that we remember to do it, we put together a nice little extension method on PropertyChangedEventArgs called HasChanged:
public static bool HasChanged(this PropertyChangedEventArgs e,
string strPropertyName)
{
string strEventPropertyName = e.PropertyName;
return string.IsNullOrEmpty(strEventPropertyName) ||
strPropertyName == strEventPropertyName;
}
Now, proper handling of the PropertyChanged event is as easy as this:
void Source_PropertyChanged(object sender, PropertyChangedEventArgs e)
{
if (e.HasChanged("Text"))
UpdateText();
if (e.HasChanged("Icon"))
UpdateIcon();
}
Just remember not to use "else"...
Posted by Ed Ball at 04:27 PM | Comments (2) | TrackBack (0)
March 28, 2008
Degrees of Thread Safety
I'd like to talk about .NET events and thread safety, but I first need to clarify what we mean by “thread safety.” After all, the most important consideration as regards thread safety is how thread-safe your code has to be. If you can get away with being thread-hostile, you should do so. There's no sense in worrying about other threads in a single-threaded console application, for example.
If you are writing components, it is critically important that you document your level of thread-safety. Having a consistent vocabulary for describing thread safety thus becomes important.
Here is our preferred taxonomy for describing degrees of thread safety, adapted from this article, and from Joshua Bloch's Effective Java Programming Language Guide.
Immutable
Instances of this class appear constant to their clients; no external synchronization is necessary. Examples include String and Regex.
Most immutable objects need no internal synchronization; however, “lazy loading” will generally require some form of synchronization in order to maintain thread safety.
Thread-safe
Instances of this class are mutable, but all methods contain sufficient internal synchronization that instances may be used concurrently without the need for external synchronization. Examples include many of the System.Threading types, including ManualResetEvent and Timer.
Since most concurrent applications require more coarsely-grained synchronization than the per-method-call synchronization of a thread-safe class, and since thread-safety causes unnecessary overhead for single-threaded operations, truly thread-safe classes are often not needed.
Conditionally Thread-safe
Like thread-safe, except that the class (or an associated class) contains methods that must be invoked in sequence without interference from other threads. To eliminate the possibility of interference, the client must obtain an appropriate lock for the duration of the sequence.
The object returned by Hashtable.Synchronized, for example, is documented as such: “Synchronized supports multiple writing threads, provided that no threads are reading the Hashtable. The synchronized wrapper does not provide thread-safe access in the case of one or more readers and one or more writers.”
Thread-safe for multiple readers
Many classes are conditionally thread-safe if they are not being mutated. Examples include List<T> and Dictionary<TKey, TValue>.
Readers can only presume thread-safety on an instance of this type if they can be certain that there will be no writers on any thread. If there is any possibility of a writer, both readers and writers must use the same external synchronization.
It is easy to assume that a class is thread-safe for multiple readers, but unsynchronized “lazy loading” can easily cause a class to violate this degree of thread safety. Worse, a future “upgrade” of the class could introduce such a thing as an optimization. If a class is thread-safe for multiple readers, its documentation must indicate as such.
Thread-safe for a subset of operations
Some classes are mostly thread-compatible (see below), except for a few methods that are thread-safe. For example, the BeginInvoke method of Control can safely be called from any thread, even though Control otherwise has thread affinity.
Thread-safe except for Dispose
It can also be convenient to declare a class thread-safe except for the Dispose method. See Thread-safe disposable objects for details.
Freezable
A “freezable” object has a method (often named Freeze) that causes the object to become immutable and thus thread-safe. Until that point, the object is mutable and generally thread-compatible or even thread-hostile. Examples include classes that derive from the Freezable class of WPF, including Brush, Pen, and Transform.
Thread-compatible
Instances of this class can safely be used concurrently by surrounding each method invocation (and in some cases, each sequence of method invocations) by external synchronization. Most classes in the .NET Framework fall into this category and are documented as such: “Any public static members of this type are thread safe. Any instance members are not guaranteed to be thread safe.”
Thread-affined
A thread-affined class has thread affinity; that is, instances of that class may only be used from one thread, generally the thread with which the instance was created. Most user interface components (Windows Forms, WPF, etc.) have affinity to the “user interface thread” and can thus only be used from that thread. Methods of thread-affined classes should assert that they are being called on the proper thread.
Thread-hostile
This class is not safe for concurrent use by multiple threads, even if all method invocations are surrounded by external synchronizaton. By this definition, thread-affined classes are also thread-hostile. In general, the only classes in the .NET Framework that are “thread hostile” are those with thread affinity. Thread-hostile behavior also includes:
Modifying static data
Modifying static data that affect other threads is thread-hostile behavior, for example, setting the value of System.Console.OutputEncoding.
Posted by Ed Ball at 02:06 PM | Comments (0) | TrackBack (0)
March 25, 2008
AllowDrop in WPF
When implementing a drag-and-drop target in WPF, don't forget to set AllowDrop to true! You wouldn't think it would be that hard to remember – after all, it usually doesn't work until you set it. However, I just tracked down a bug that resulted from AllowDrop not being set to true on a drag-and-drop target. In most circumstances, it was inheriting a true AllowDrop from an ancestor element, so it worked fine, but in other circumstances, one of its ancestors was setting AllowDrop to false for some reason. I really don't understand why, and I'm guessing there's a WPF bug hiding in there somewhere, but the fix was easy – always set AllowDrop to true if you want to handle the drag-and-drop target events (DragEnter, DragOver, Drop, etc.).
Incidentally, this was my first bug fix for which the .NET Framework Library Source Code proved invaluable. The ability to step through the code, set breakpoints, watch variables, etc. was key to tracking this one down.
Posted by Ed Ball at 01:26 PM | Comments (0) | TrackBack (0)
March 14, 2008
EmptyIfNull
Another great use for the fact that C# extension methods work on null references is a method we call EmptyIfNull:
public static IEnumerable<T> EmptyIfNull<T>(this IEnumerable<T> seq)
{
return seq ?? Enumerable.Empty<T>();
}
This extension method simply returns the specified enumerable, unless it is null, in which case it returns an empty enumerable. It requires slightly less typing than using the null-coalescing operator.
For example, suppose the GetNames method normally returns a collection of strings, but could return null if there are none. Either of these statements will write each of the strings to the console:
(GetNames() ?? Enumerable.Empty<string>()).
ToList().ForEach(Console.WriteLine);
GetNames().EmptyIfNull().ToList().ForEach(Console.WriteLine);
I like the EmptyIfNull syntax better.
Posted by Ed Ball at 11:07 AM | Comments (0) | TrackBack (0)