Friday, February 15, 2008
Are reads and writes of reference type variables atomic?
This question came up at work today:
If you have a class that has a getter and setter for a property that is a reference type (such as a user defined class) and that property will be accessed by multiple threads, do you have to protect access to getting or setting the underlying member variable?
Bascially, the other developer was asking if he had to do the following:
My answer was "I never do". But, I soon as a paniced feeling as I started to second-guess myself. Do I need to do that? Had I been doing it wrong in everything .NET app I have ever written?
After thinking about it for awhile, I came to conclusion that setting and getting a reference variable has to be an atomic operation.
If it was not, I reasoned, then the
After to coming to this conclusion, I went to ECMA-334 to see if it had a concrete statement of what I had just determined must be. Sure enough, section 12.5 Atomicity of Variable References states:
Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.
Ok, case closed. But this also implies something else. This means that there is an upper limit of the number of objects that can exist in .NET at any one time. Assuming that each instance of an object is assigned a unique "reference" number, that number is probably 32-bit since that is the largest number of bits that can natively be copied atomically on a 32-bit system. This means that the absolute maximum number of objects that can exist in .NET at any point in time is
If you have a class that has a getter and setter for a property that is a reference type (such as a user defined class) and that property will be accessed by multiple threads, do you have to protect access to getting or setting the underlying member variable?
Bascially, the other developer was asking if he had to do the following:
class MyClass1
{
}
class MyClass2
{
object _syncLock = new object();
MyClass1 _myClass1;
MyClass1 Value
{
get
{
lock(_syncLock)
{
return _myClass1;
}
}
set
{
lock(_syncLock)
{
_myClass1 = value;
}
}
}
}
My answer was "I never do". But, I soon as a paniced feeling as I started to second-guess myself. Do I need to do that? Had I been doing it wrong in everything .NET app I have ever written?
After thinking about it for awhile, I came to conclusion that setting and getting a reference variable has to be an atomic operation.
object a = new object();
object b;
b = a; // This line must be atomic!
If it was not, I reasoned, then the
lock() statement we all love could not possibly be used to protect access to anything because the object referred to by the reference variable that is passed into lock could be changed by another thread as it was in-process of being assigned as the lock object for the Monitor.After to coming to this conclusion, I went to ECMA-334 to see if it had a concrete statement of what I had just determined must be. Sure enough, section 12.5 Atomicity of Variable References states:
Reads and writes of the following data types shall be atomic: bool, char, byte, sbyte, short, ushort, uint, int, float, and reference types. In addition, reads and writes of enum types with an underlying type in the previous list shall also be atomic. Reads and writes of other types, including long, ulong, double, and decimal, as well as user-defined types, need not be atomic. Aside from the library functions designed for that purpose, there is no guarantee of atomic read-modify-write, such as in the case of increment or decrement.
Ok, case closed. But this also implies something else. This means that there is an upper limit of the number of objects that can exist in .NET at any one time. Assuming that each instance of an object is assigned a unique "reference" number, that number is probably 32-bit since that is the largest number of bits that can natively be copied atomically on a 32-bit system. This means that the absolute maximum number of objects that can exist in .NET at any point in time is
2^32 = 4,294,967,296. Assuming that each of those objects took only 4-bytes to store its unique 32-bit number and nothing else, that would be 2^32 * 4 = 17,179,869,184 bytes (16GB) of memory to create every possible instance of simple objects, so I don't think we're going to run out anytime soon. Although, I can recall when 640K was considered to be 10x the amount of RAM a PC would ever need.Saturday, February 9, 2008
Unable to cast object of type 'Y' to type 'Z'
The other day, I had a very strange exception. I had an object that very clearly implemented an interface. Yet, when I tried to cast the object to the interface using
Since my application uses interfaces extensively, this seemed like a break-down in the very fabric of the .NET space-time continuum. All of the other casts of objects to interfaces were working fine, except for this one object. If there's one thing my years of enigneering and computer programming has taught me, it's that there is always a reason why something really weird happened.
I tried everything, and even verified that the object instance did indeed support the interface by using
"The type initializer for 'X' threw an exception. Unable to cast object of type 'Y' to type 'Z'"
This "type initializer" exception did, of course, clue me in that there was assembly version issue. But where? I double-checked all of our make files (we build at the command line instead of in Visual Studio), deleted all DLLs to make sure there was not some kind of circular reference of the assemblies, closed Visual Studio and all other apps to make sure nothing was keeping a file locked, rebooted the computer, etc. I spent hours trying to figure out what was happening. Then, by accident, I started the release version of our software instead of the bug version, and it worked!
That was new information -- it works in release mode by not in debug mode. Our build process generates two separate sets of binaries: release and debug. And, the filenames for the debug binaries all end in the letter "D".
With this new information, I was eventually able to find the culprit. Although the application is a WinForms app, it uses
Since our debug assemblies end in "D" but the release assemblies do not, the XAML files that were generated from release software could only be loaded in release software, and files generated by debug software could only be loaded by debug software. It just so happes that, during my hours of frustration, I was using the debug version of our software but trying to load XAML files that had apparently been created from our release version.
Once I found the problem, the solution was simple: adding the
as, the cast always returned null.
interface IMyInterface
{
void MyMethod();
}
class MyClass : IMyInterface
{
void MyMethod()
{
}
}
MyClass myClass = new MyClass();
IMyInterface myInterface = myClass as IMyInterface();
if (myInterface == null)
{
Debug.Assert(false, "What the heck?);
}
Since my application uses interfaces extensively, this seemed like a break-down in the very fabric of the .NET space-time continuum. All of the other casts of objects to interfaces were working fine, except for this one object. If there's one thing my years of enigneering and computer programming has taught me, it's that there is always a reason why something really weird happened.
I tried everything, and even verified that the object instance did indeed support the interface by using
.GetType().GetInterface() and .GetType().GetInterfaces(). Since casts using as "fail gracefully" and return null, I tried casting using (IMyInterface) instead to see if I could get an exception. Sure enough, I got an exception:"The type initializer for 'X' threw an exception. Unable to cast object of type 'Y' to type 'Z'"
This "type initializer" exception did, of course, clue me in that there was assembly version issue. But where? I double-checked all of our make files (we build at the command line instead of in Visual Studio), deleted all DLLs to make sure there was not some kind of circular reference of the assemblies, closed Visual Studio and all other apps to make sure nothing was keeping a file locked, rebooted the computer, etc. I spent hours trying to figure out what was happening. Then, by accident, I started the release version of our software instead of the bug version, and it worked!
That was new information -- it works in release mode by not in debug mode. Our build process generates two separate sets of binaries: release and debug. And, the filenames for the debug binaries all end in the letter "D".
With this new information, I was eventually able to find the culprit. Although the application is a WinForms app, it uses
XamlReader and XamlWriter to serialize and deseialize some custom WPF objects that get displayed in an ElementHost control. And, in the assembly that contained our custom controls, I had accidentally omitted the XmlnsDefinition assembly attribute that maps an XML namespace to a .NET namespace. As a result, the XamlWriter was putting the assembly name into the XAML file.Since our debug assemblies end in "D" but the release assemblies do not, the XAML files that were generated from release software could only be loaded in release software, and files generated by debug software could only be loaded by debug software. It just so happes that, during my hours of frustration, I was using the debug version of our software but trying to load XAML files that had apparently been created from our release version.
Once I found the problem, the solution was simple: adding the
XmlnsDefinition made the problem go away because the XML namespace was written into the file instead of the assembly. But, it really got me thinking. Anytime an assembly is loaded by name, this problem could exist. For example, the the Type.GetType() method allows you to load a type from a "type,assembly" string.
// Create and instance of type MyNameSpace.MyType from assembly MyAssembly
Type myType = Type.GetType("MyNameSpace.MyType,MyAssembly");
MyType myType = (MyType)Activator.CreateInstance(type);
Wednesday, February 6, 2008
WPF Frame, Content and ContentRendered
This is something that has bit me in the past, so I though I would mention it here. The .NET Framework 3.0 has the WPF extensions, including the
So, what's going on? How can
The answer is straight forward, if you know what's going on. The
System.Windows.Controls.Frame class. And, the Frame class as a Content property. But, if you have an instance of some WPF object (perhaps a System.Windows.Controls.Canvas) and set it to the Content property of the frame, the frame's Content may return null.
System.Windows.Controls.Frame myFrame =
new System.Windows.Controls.Frame();
System.Windows.Controls.Canvas myCanvas =
new System.Windows.Controls.Canvas();
myFrame.Content = myCanvas;
if (myFrame.Content == null)
{
Debug.Assert(false, "Why is the Content property still null?");
}
So, what's going on? How can
myFrame.Content still be null after I just set it?The answer is straight forward, if you know what's going on. The
Frame class renders its contents asynchronously. And, when the Frame.Content property is set, the property returns null until the frame has fully rendered its contents. Once the contents have been rendered, however, it will return the object you expect. You can catch the frame's ContentRendered event if you need to know when rendering has completed.Subscribe to Posts [Atom]