Thursday, February 15, 2007

Soon, my ownership area will extend to include delegates.  Since I'm fairly excited about this, I thought I'd celebrate by writing a little something about them.  So, what are delegates?  A casual observer might be tempted to write off delegates as a sort of managed function pointer.  While this comparison is certainly accurate, there's much more to explaining the power of delegates.

In general, delegates are a sort of universal method dispatch mechanism.  Initially, the scenario they supported was callbacks.  Delegates are one of the things that distinguish the CLR from other VMs like Java.  Java requires the use of interfaces to implement callbacks. (I'm only calling that out as a distinction, not saying the Java way is bad. although personally I like what delegates bring to the table)  So, delegates let you wrap up a method as an object to pass around, with the expectation that it will be called from another context.

Its sort of hard to talk about delegates because the discussion is often framed by the language that's exposing them.  Currently, no managed language exposes them in the way that they are represented in IL.  In C# and VB, you declare a delegate by simply defining a method signature.  From an IL perspective, the compilers generate a class that inherits from MulticastDelegate (another story I'll get to later), with an Invoke method that matches your signature, and some various constructors to support different things.  (You also usually get the corresponding asynchronous calling pattern support methods, but I don't want to get into that)  Some other delegate-related compiler trickery is involved in declaring events, which I'll cover later.

Under the covers, a delegate [conceptually] contains 2 things:

  • A target object
  • A target method

Now, generally speaking, the target method is the method to be run, and the target object is the object on which the target method will be run, but there are cases where this line is blurred a bit.  For instance, when a delegate is pointing to a static method, the target object is conceptually null (internally it's not, but that's an implementation detail).  I'll get into the other cases later.

So now you're saying, "Yup, that's a delegate.  Big deal.  What's so cool about that?"  What's cool about that, my friend is that delegates are the things that power virtually all of the coolest new language features that came out in v2.0 and will be coming out, including all the dynamic language goodness like IronPython.  It's the dynamic stuff that is really exciting, so let's talk about how delegates enable dynamic languages on top of a statically typed system.

(To be fair, Jim Hugunin did his initial Iron Python work before these features were available, but they now play a big role)  One of the pieces of work done in v2.0 was called delegate relaxation.  Previously, the target method had to match the delegate signature exactly.  Now, as you might expect intuitively, the signature can be relaxed such that the target method can have "more general" parameters and return something "more specific" than the delegate's signature.  This is typically defined in terms of covariance and contravariance, terms that even people who understand them get confused.  Here's the way I usually remember it: If I could wrap the target method with a method having the delegate's signature without casting, it will work. Anyway, this feature makes delegates quite a bit more flexible.

Before I go into the other features, lets talk a little about implementation. In normal method calling in the CLR, the first parameter becomes the "this" object.  (Which is why you see ldarg.0 in IL to put it on the stack.)  So, conceptually, the target object represents the first argument for the method. (There is an implementation detail that allows static methods to be called using the same convention, which is a really elegant solution) So, by extending this idea of the target object simply being the first argument, we get a couple of interesting variants.

The first is what is called "closed" static delegates.  This allows you to specify the first argument of a static method at delegate creation rather than at the callsite.  Notice this maps quite nicely to the dynamic language concept of adding a method to an existing instance of an object.  The language runtime just needs to be able to track these extra methods as part of its method dispatch logic.

The second feature is "open" instance delegates.  This allows you to create a delegate that points to an instance method, but doesn't define the target object.  Instead, the delegate signature can have an extra first argument that will specify the target object at the callsite.  When used with LCG (DynamicMethod), this can be used to implement things like adding a method to an existing type.  Again, the language runtime merely needs to add the logic to method dispatch.

These 2 features are intriguing to me because they are not directly exposed from VB or C#.  I believe VB9 exposes these, but they are not accessible in an early bound way in C#.  You can, however, create them via Delegate.CreateDelegate() using reflection, or use Reflection.Emit to generate the corresponding IL.

Hopefully, I'll have some time in the future to do some samples of these as well as discuss more about how these improve the dynamic language support in the CLR.

posted on Thursday, February 15, 2007 2:32:53 PM (Pacific Standard Time, UTC-08:00)  #    Comments [1]
 Monday, January 29, 2007

I dealt with several situations in the past months where the crux of the problem was confusion over assemblyname and filename.  Let's define what we're talking about:

  • Filename - The name of a file in the filesystem, such as System.dll
  • Assemblyname - The name given to an assembly to establish its identity.  In this case, we'll only concern ourselves with the "simple" name. such as System

Usually, any confusion that arises between the two can be resolved by reminding people that a filesystem is just one of the places you can get an assembly from.  For instance there are APIs for getting assemblies from byte arrays.

For those that still do see it... In the managed world, the assemblyname gives identity to the code that resides in the assembly.  If you have 2 assemblies with the same assemblyname, you expect them to represent the same identity (perhaps different versions, build flavors, bitness, etc.).  If we relied on the filesystem name, the identity of the code could change just by changing the filename.  That's not the semantics we expect.

So, why does the filename matter?  Why do we recommend keeping them the same?  Some of the reasons are simple convenience.  It's nice to look at a file and know what it is without cracking it open.  If the names are different, it's like me going to a party and wearing a nametag that says, "Peter".  While there is nothing keeping me from doing it, it causes confusion.  However, another more important reason to keep them the same is that assemblies are rarely loaded by filename.  References and most dynamic loads are done by assemblyname.  You don't take a reference to System.dll, you take a reference to System.  At some point, the loader has to find an appropriate file to load to satisfy that reference.  If System's filename is Peter.dll, then it's going to have a difficult time finding it to load.  This is actually the very reason that gacutil will not let you install an assembly into the global assembly cache if the filename doesn't match the assemblyname.  However, I think it's silly that it doesn't just fix the name for you.

What about multi-module assemblies?  Well, it's the module with the assembly manifest that matters.  It's the one that should match.  Then the rest of the files need to match the assembly manifest :). But, if you're using multi-module assemblies, let me know.  I'd like to know why.

posted on Monday, January 29, 2007 12:46:56 PM (Pacific Standard Time, UTC-08:00)  #    Comments [1]
 Thursday, January 25, 2007

Yup, it's that time again.  Today is my birthday.  I was lucky enough to get a Wii, which has been quite enjoyable.  Now I just need to get the component cables so I can stand to look at it. :)

posted on Thursday, January 25, 2007 9:05:54 AM (Pacific Standard Time, UTC-08:00)  #    Comments [2]
 Monday, January 22, 2007

I've mentioned before that one of my ownership areas at MS is the CLR "shim".  Most people I've said that to ask me, "What's that?".  I usually reply that generally, it's mscoree.dll, to which they typically respond, "Oh yeah.  What does that do?"  In general terms, the shim is in charge of firing up the runtime in a process.  In addition, it exposes all the hosting APIs and other stuff you need to do stuff with the CLR from unmanaged code. If you look at a managed app, you will see that it has a dependency on mscoree.dll, and nothing else CLR-related.  Ater the runtime is spun up, most of the things that mscoree exposes are simply forwarded calls into the mscorwks.dll of the runtime you have installed.

What's interesting about mscoree.dll, is that it is the only piece of the runtime that doesn't run side by side.  You can have v1.1 and v2.0 installed on the machine, but you will only have one mscoree.dll.  You always have the version of mscoree.dll that corresponds to the latest version of the runtime installed on your machine (unless you have installed a patch or something that services mscoree, in which case you may have a v2.0 shim even though only v1.1 is installed on your machine).

So, naturally, backwards compatibility is extremely important in the Shim.  When you start up managed code, the Shim decides which version of the CLR to fire up based on lots of different things.  These things are all fairly well documented and all have a specific scenario they enable, but by their nature they are very confusing.

Aside: When I was job hunting, I interviewed at several companies other than Microsoft.  During some of those other interviews, I was asked questions about what runtime would be started under certain conditions. The rules are so confusing, that some of the interviewers, although all extremely smart people, had formed incorrect models of what the rules were.  Some told me I had the wrong answer to their question, when if fact it was correct. (that's not to say that I knew the correct answers to all of them.)

I'm not certain that I can clear up the confusion, but I do hope to have a series of posts in the coming months on why the shim does what it does under certain circumstances.  Then, at least you might understand what's going on when it happens.

posted on Monday, January 22, 2007 10:50:36 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Tuesday, January 09, 2007

I have had occasion recently to play with the latest (December 2006) CTP of WPF/e.  It's a really cool technology that I think is poised to change the way people write rich web apps.  It's cross-platform (Windows and MacOS at the moment) and cross-browser (IE5-7, Firefox, and Safari at the moment).

The CTP has some limitations at the moment that are keeping it from really being a replacement for HTML-based web apps (It's missing the WPF layout manager for one), but it certainly doesn't keep you from doing some really cool stuff with it.

Rather than just point you to the existing samples (scroll down to see them), I thought I'd put together one of my own.  I created a proof of concept replacement for my blog's header that shows my photos from Flickr as if they were hundreds of polaroids strewn about and panning by.  You'll need to go to the WPF/e site and download the plugin that's appropriate for your OS (look under Top Downloads on the right). After you've done that, click the image below to see the demo.

Click to see demo

It consists of:

  • A xaml file with the "background" (some of which is in the foreground) and some general declarative animation instructions.
  • An html file with some script infrastructure for pulling JSON data from the Flickr APIs and turning them into visual elements.
  • The provided WPF/e scripts for instantiating the control
  • the MicrosoftAjax.js file from the AJAX library.
  • A little .ashx proxy for routing the async calls to Flickr (to avoid cross-domain scripting issues)

If you watch long enough, it should scroll through all 7,140 of my pictures (with some gaps in between while loading new images), and then start over.

All in all, it was really easy to do.  There are some problems I need to address before it could realistically take over as the header for my blog, but I think it shows some real promise.  I can't wait for the actual WPF/e release.

posted on Tuesday, January 09, 2007 2:52:10 PM (Pacific Standard Time, UTC-08:00)  #    Comments [1]
 Thursday, January 04, 2007

The couch that we ordered over 2 months ago was finally delivered yesterday.  When we moved into our house here, we picked out a couple of couches...  a less-expensive, fold-out sleeper for the upstairs living room, and a really nice, non-sleeper for the big downstairs room where we spend most of the time.  The sleeper arrived shortly after we ordered it, but the nice one was delayed multiple times.

We spent our first 7 years of marriage with really old couches that my parents had when I was growing up.  They were so old and broken down that we decided not to bother bringing them with us to Washington.  It's very nice to have a sweet couch (it's nice to have a couch at all).  We made sure to get one long enough that I can lay on it without my head or feet having to be on an armrest (something I rarely experience at 6' 2").  It also sits up nice and high so you don't have to feel like you're climbing out of a hole when you get up.  Becky's feet don't even touch the floor when she's sitting on it.

posted on Thursday, January 04, 2007 2:33:13 PM (Pacific Standard Time, UTC-08:00)  #    Comments [1]
 Wednesday, January 03, 2007

Someone asked me the other day if you could reflect on other people's assemblies using the CLR.  My answer was, "ABSOLUTELY!!!!!"

As it turns out, they were having some problems achieving this, and they were wondering if there was some kind of security mechanism in place that was preventing reflection on 3rd party assemblies.  Here's the basic scenario.  They had a 3rd party library with a type that defined several "constants" using fields.  They needed to be able to specify a named constant via a string and return the value for the constant.  Security and performance arguments aside (they had already been considered), they simply wanted to lookup the field by name via reflection and get its value.  This can be accomplished via just a few fairly reasonable lines of code using one of the GetField family of methods on System.Type and then getting the value.  I'll leave this as an exercise for the reader so they can have the fun of wresting with the silly BindingFlags enum.

After some discussion, I learned that the trouble actually revolved around getting the Type object in the first place.  They were using Type.GetType to load the type with a namespace-qualified name as the string argument.  It was returning null (Nothing in VB).  They were validating that the string was correct using Intellisense, and concluding that since Intellisense could see the type, that Type.GetType should also "see" it (which seems like a perfectly reasonable assumption).

Type.GetType() takes a string argument specifying the type to retrieve.  When specifying types in strings, you can usually use a namespace qualified name ("[namespace.]type"), or an assembly-qualified name ("[namespace.]type, assemblyName").  If you don't provide the assembly name, the API looks through the assemblies already loaded in the AppDomain for a type that matches the name.  If an assembly name is specified, a bind occurs to the assembly and it is loaded if necessary.

In this case, there was a reference to the assembly containing the type, so Intellisense was picking it up and providing completion. The trouble was that, at runtime, the assembly had not yet been loaded into the AppDomain, so the type was unavailable.

So, the options were (in order of appropriateness in my opinion):

  • Use an early-bound type - Since the type was known at compile-time, use typeof() (GetType() in VB).  This will create a compile-time, assembly-qualified type reference in the IL rather than a runtime parsing/bind/load of the type string.
  • Use an assembly-qualified type string - Adding the assembly name to the type string will let the CLR know what assembly to look in (and load if necessary).  There are some subtle versioning issues with this approach, especially for strongly-named assemblies.
  • Make sure the assembly is loaded prior to calling Type.GetType() - Making an early-bound call to something else in the assembly first will get it loaded into the AppDomain.  This seems like a fragile solution and I would not recommend it, although it will technically work.

The real issue here is the number of samples (especially in VB) provided by MS that use Type.GetType() with a non-qualified name (ex. "System.String").

posted on Wednesday, January 03, 2007 9:09:25 AM (Pacific Standard Time, UTC-08:00)  #    Comments [1]
 Tuesday, January 02, 2007

This year, we gave the gift that keeps on giving... the stomach flu. But, before I get to that, let me back up a bit...

If you recall, we got our power restored after over a week of outage with just a little more than 12 hours to prepare for our holiday travels.  However, we managed to get everything together and arrive at the airport the suggested 2 hours before the flight.  Out trip went through Phoenix instead of Denver, so I figured we'd be in the clear weather-wise since nothing ever happens in Phoenix.

Wouldn't you know it, we got to the airport to discover our flight had been delayed due to fog in Phoenix.  After a bout with a touchy airline representative (which I may eventually devote an entire entry to), we decided to go ahead to Phoenix since everything was delayed coming and going in hopes that our connecting flight would be delayed as well.

4.5 hours after our flight was to leave originally, we boarded the plane with still a slight chance of catching our connection.  After we landed in Phoenix, we discovered we'd missed the connection by a mere 19 minutes.  With no flights available to El Paso until Christmas, we really had only one acceptable option... rent a car and drive.  So, at about 9pm, we hopped in a car and drove the 6.5 hours from Phoenix to El Paso to Becky's parents house.  All in all, I felt a great sense of accomplishment in having executed such an old-school road trip with a 1-year-old.

Christmas with the Pattersons was great.  I got something I have wanted most of my life... a radio-controlled helicopter.  Thanks everyone.  The only issue was that things in our room were much colder than we were used to, which resulted in Jenna waking up every 30 minutes after midnight due to being cold, and she got all sniffly and such.

We went to Cattleman's (a world-famous steakhouse) for dinner Christmas eve, and I had a huge t-bone.  It was delicious.  Becky had been feeling ill earlier in the day, but we attributed it to lack of sleep.  After dinner, she took a turn for the worse.  I started feeling bad, but attributed it to eating too much.  Becky went to the doctor while I put Jenna to sleep.  By the time Becky got back, I was full on sick, but it was too late to see the doctor.  It was probably the worst night of my life.  Becky's dad helped us tremendously by tending to Jenna, who had also caught the bug.

The next morning, after an unpleasant episode in which I thought I was literally dying, I went to the doctor too.  We were scheduled to fly to Austin and drive to Belton this day, and were contemplating delaying the trip.  We ultimately decided that the pros of keeping our original schedule outweighed the benefits of staying longer.  So, somehow, we got on a plane to Austin and then drove the hour from Austin to Belton.  Unfortunately, the wake of our illness still spread to virtually all of Becky's family.  Some of them got sick on their flights home.

In order to avoid getting sick when we arrived in Belton, my parents just waved to us as we went to the apartment behind the house where they work.  They had gone to alot of trouble to quarantine us away, which was great because we had alot of room for Jenna and we had complete control over the temperature.  For the next few days, we followed a protocol of constant hand washing and such to prevent the spread of the plague.  We even avoided seeing my brother's family completely to avoid any complications for their new son who was less than a few weeks old.  This seemed to be working until my Dad got sick, followed by my mother that night.  My aunt also got sick on her flight home.  We ended up canceling several rendezvous we had planned with friends in the area.  In the end, we finally had a very short visit with my brother's family on his back porch.

Even though we had to be up at the crack of dawn on the 1st to get to the airport, we thankfully had an uneventful and undelayed flight home, and we just sat on our couch in disbelief of the events that had transpired during the previous few weeks.  Now, we just have to shift Jenna 2 hours back to Pacific time.

So, to everyone we had hoped to visit while we were in Texas, we're sorry things had to go down like that.

posted on Tuesday, January 02, 2007 4:26:53 PM (Pacific Standard Time, UTC-08:00)  #    Comments [1]
 Friday, December 22, 2006

So, 1 week +12 hours give or take, and power has returned.  Unfortunately, cable is still out, so no XBox for me tonight.  We leave for Texas tomorrow morning.  Let the fun begin!

posted on Friday, December 22, 2006 1:14:18 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]