Friday, June 10, 2005

Back last year, I outlined a problem I had with calling Response.End from a thread other than the thread that handled the request.  It stemmed from a progress mechanism we had implemented that had the ability to cancel long-running tasks when the user hit stop or closed the browser.  As it turns out, that wasn't the whole story.

We recently added a few reports where, the majority of the time, gathering the data takes over a couple of minutes.  While doing memory and performance optimizations, we noticed our worker process was recycling with the following message in the System event log:

A process serving application pool 'XXXXXXXXXX' terminated unexpectedly. The process id was '####'. The process exit code was '0xff'.

Googling for this error doesn't do you much good since it could be caused by a variety of reasons.  Most of the advice I came across wasn't well thought out and made quite a few bad assumptions.  So we added some more debug logging and determined that this was happening when our canceling mechanism kicked in when a user decided they didn't really want to wait 5 minutes for the report.  I was greatly puzzled by this since this feature had been tested thoroughly and had been running in production for some time.  Looking back through the server logs, it was evident that it had been happening all along, just not very often. We had just gotten the performance on the vast majority of pages to be very good and it wasn't an issue.  The problem only came to the surface when we added the report that always takes a while.

Here's the problem.  If the handling thread is aborted, it causes a condition that IIS considers to be bad and that forces the worker process to recycle. (presumably, there is some communication that doesn't occur) This doesn't happen when Response.End() is called because it passes a special exception as the exception state to Thread.Abort.  The HttpApplication catches ThreadAbortException and checks the ExceptionState.  If it is an HttpApplication.CancelModuleException, it knows there was either a timeout, or Response.End() was called, and it cancels the Thread.Abort by calling ResetAbort which allows the thread to continue running at that point.  I thought that was pretty slick.

When I was aborting the handling thread, I was on a different thread, so I had to call Thread.Abort manually, so CancelModuleException was not being used, so the thread was ending completely and causing the recycle.  Since HttpApplication.CancelModuleException is internal (and rightly so) I could not simply use that mechanism.

The good news is that the Unload event always happens (for all practical purposes), even when the thread is being aborted.  So I added my own PageIsCancelling property to our base class Page and check it, along with the current ThreadState in Unload, and cancel any pending abort if the page is canceling.  So, the abort is contained within the callstack of the page, and the thread stays alive and all is well.  No more crazy recycling.

As an aside, it seems this is aggravated by multiple processors, which might explain how it passed testing on the dev's machine.  Although I don't have any proof of this.

posted on Friday, June 10, 2005 10:50:44 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Thursday, June 02, 2005

Those of you who follow my vacationing activities know that I have a certain affinity for aquariums, which is odd because one of my biggest phobias involves murky water.  Anyway, while in New Orleans over the weekend, we stopped by the aquarium (map) after the wedding. Enjoy the photos.

posted on Thursday, June 02, 2005 9:27:59 AM (Pacific Standard Time, UTC-08:00)  #    Comments [1]
 Wednesday, June 01, 2005

New Orleans Pano 1This past weekend, we (Me, Becky, my Mom and Dad, and my aunt Kenny) piled in a rented Ford Expedition and headed to New Orleans where my cousin Jeremy was getting married.  We stayed in the J.W. Marriot on Canal street where we had a great view of Canal street and much of the city.  All the wedding festivities went well.  We were very tired from the drive, and had to duck out early from several of the events, but it was really great.  We hit the aquarium (pictures coming soon) and took a stroll around the French Quarter, which I really didn't like at all.  Something about lots of people crammed into a confined space that smells like a mixture of B.O. and pee just seems to get to me for some strange reason.  I've got photo sets of the rehearsal dinner and the wedding/reception.  My dad and I also created a Flickr group to pool all our pictures.

I also played with myGmaps and created this little map that I will continue to annotate with stuff from the trip.

It was really fun to see the family and participate in the festivities, but I probably won't be planning any more New Orleans vacations any time soon.

posted on Wednesday, June 01, 2005 9:50:52 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Friday, May 20, 2005

I've had a bunch of new pictures in the queue to get uploaded.  Flickr announced that the per-photo size limit had been raised to 10MB (from 5), so I thought I'd go crazy and save my raw conversions as lossless PNG files rather than JPG.  This made most of them between 5 and 10MB a piece.  Then I find out that the currently available uploaders had the limit hard-coded, so I could not upload them.  So, I built my own uploader.  Unlike most of the loaders, this one runs as a service and watches a directory.  You can just drop files into the directory and it will load them.  I used it last night to upload some and it worked great.  It can use sub-folders to apply general tags to the images. I have a couple of improvements to make, like:

  • A task tray "agent" that will allow me to "suspend" it when I'm playing Halo, as well as do some configuration stuff.
  • Bandwidth throttling - I'd like to specify a cap to make it play nicer with other outbound traffic.
  • Better retries - Right now if a picture fails, it goes immediately back in the queue.  It needs more standard retry intelligence, like trying again at successively longer intervals.
  • Utilizing the user's monthly limits to stop uploading before some percentage of the limit is reached.
  • Priority spools - I've still gots tons of old photos to upload, so I want to be able to use up my limit at the end of the month without managing that process myself.
  • Automatic processing.  I'd like to just drop my RAW files in there and have my standard photoshop processing applied.  I need to think about that some more because I usually like to tweak them before sending.  With the huge per-month limit, maybe I could do that and use them as "proofs".  Then delete the ones I was to tweak manually and resend them.  Oooh, and perhaps automatically deleting the one it replaces
  • I also don't like having the password in the config file.  I'm going to try to fix that as well.  At least encrypt it a bit.

Anyone got any other cool things it ought to do?

posted on Friday, May 20, 2005 8:57:53 AM (Pacific Standard Time, UTC-08:00)  #    Comments [4]
 Tuesday, May 17, 2005

The generic Nullable type in Whidbey and the C# language features it supports are great.  They've enabled me to eliminate gobs of code and simplify things incredibly. But, Joe Duffy's latest request for feedback on some of the "syntactic suger" on this feature brings up some interesting points.  He shows us several different equality tests against a nullable value type and asks if the results are intuitive.  I suspect it's a loaded question, because they are not.  Someone with a good understanding of the framework can fairly easily guess the outcome, but deep down I think they would have to admit it's not ideal.

What it boils down to is what is the purpose of the generic Nullable type.  I originally viewed it as helping to code against scenarios where values can be null like databases.  Without nullable value types, you frequently have to check to see whether you're dealing with a null value, and carry around alot of state information, and Nullable simply does that for you.  I think some of the designers of this feature see it much more broadly as a concept to unify the type system in a more performant way than simply being able to cast to object.  This requires some special casing, as is evident by the special case for type parameter constraints on generic types.  Specifying struct will not allow you to use a generic Nullable, despite the fact that it is a value type.  The problem is that the special casing stops short of completely abstracting the concepts.

The problem can readily be seen in Joe's second two tests, boxing a valueless "int?", and using the equality operator "==" against a generic argument that is instantiated as "int?" and has no value.  Both do the opposite of what you might expect if you don't think through it first.  Both do not equate to null.

I pondered this for quite a while before deciding how bad this was (and I'm still not exactly sure).  For me, this is no problem since I understand the issues at work.  For the future of the language, the outcome is not so clear.  The real question is, "Is a value-less value type different from a null reference?"  I don't think it should be.  If you cast a null string reference to object, does it retain its identity as a string? No. Can you cast it back to a string reference? Yes. So, should there be a difference in behavior with nullables? I'll let you decide.  Then there is the issue of how (and if) to effectively fix it.  Adding a conversion from Nullable to object that makes it a null reference instead of a boxed value type might do it, at least for the boxing scenario.  That's probably a pretty naive solution.  I haven't really thought that through.  For the generic situation, Nullable seems to be already special cased to some degree.  It doesn't seem like it would be too much of a stretch to generate appropriate code for Nullable.  The big problem here is that since this happens at JIT time, it's no longer a C# feature. Unfortunately, it's likely too late to address the issue in the in any real way, and that will likely set too much precedence to change it in the future if a solution could be reached.  Perhaps some FxCop rules could find these types of situations and at least flag them so they could be looked at more closely.

posted on Tuesday, May 17, 2005 12:43:10 PM (Pacific Standard Time, UTC-08:00)  #    Comments [0]
 Friday, May 13, 2005
We saw Kung Fu Hustle this evening with the Barneses.  It's been a long time since I really enjoyed a movie that much.  I really really liked it.  I thought it was a masterful re-invention of a classic genre.  It had just enough "plot" to get us to care a little about the characters, then it just let them go at it, while keeping a hilarious and sometimes dark sense of humor.  I also appreciated that it was subtitled rather than re-dubbed.  I suppose we have Crouching Tiger to thank for that.  I also liked that even though they relied heavily on special effects that they also relied on subtleness (like showing the shadows of things happening rather than trying to make the actual things realistic (which they also did)).  Anyway, I thought it was great.  And Becky, having a black belt in Kung Fu, also found it quite enjoyable.  Definitely a purchase when it arrives on DVD.
posted on Friday, May 13, 2005 10:15:54 PM (Pacific Standard Time, UTC-08:00)  #    Comments [3]
 Wednesday, May 11, 2005

Things are coming along very nicely in my bathroom remodeling project.  The whole thing started out with me re-caulking the tub and has resulted in me replacing the outside of my house and completely remodeling the bathroom. Long story.  Anyway, I picked up the last of the hardware this weekend and we've picked tile and paint colors.  Here's a rundown of what's occurred in the bathroom:

  • Pretty much everything has been removed from the bathroom, aside from the toilet (so far. It may eventually get replaced as well). This includes:
    • Old tub
    • wall tile in the shower area
    • sheet rock on strategic walls
    • old double-sink counter-top and cabinets
    • carpet and linoleum
    • wallpaper
  • We removed the wall that separated the main bathroom area from what I refer to as the "toilet nook".
  • We capped off a set of sink supply lines and drains.
  • We moved another set of sink supply lines and drains over about 2 feet to accommodate a new pedestal sink.

I'm currently in the process of running new plumbing from the old tub/shower supply lines to the new shower hardware and a separate Roman tub faucet. I've decided I like plumbing quite a bit.  I've always wanted an excuse to play with a blowtorch.

After the plumbing is complete, I've got to run two new circuits to run the pump and heater on the tub.  That should be fun as well.  The easiest way would probably be to run conduit from the outside breaker box along the outside of the house since it's only about 20 feet.  I'd prefer coming off of the inside box, but that will be considerably harder.  I'm still evaluating the pros and cons of each approach.

Anyway, this is what has been consuming my free time lately.

posted on Wednesday, May 11, 2005 9:11:58 AM (Pacific Standard Time, UTC-08:00)  #    Comments [1]

So much stuff has been going on in the last few weeks.  I hate it when I get behind here.  Yes, the rumors are correct.  Becky and I are going to have a baby.  I don't really like the term "expecting".  Whenever someone says they're expecting, I always say, "I hope you're pregnant, or you're going to be dissapointed."  I also don't like the term "due".  It makes me think, "Boy, I hope we get it turned in before it's due, or we'll get points taken off".  Anyway, I was waiting to make this announcement until I could get the ultrasound picture scanned so I could share it.  But the scanner is on the fritz and I haven't had time to mess with it.

Oh, yeah, the official date is December 4th.

posted on Wednesday, May 11, 2005 8:56:53 AM (Pacific Standard Time, UTC-08:00)  #    Comments [5]
 Monday, May 02, 2005

We have a big configuration file that maps business logic concepts to their currently used implementations so we can dynamically switch these out.  Under ASP.NET 1.0, all those implementations were in assemblies with "well-known" names (meaning we know them deterministically before compile-time).  Some of the implementations were in nested classes of user controls (we'll save the debate over whether that is a good idea for another time).  This means that the ASP.NET compiler is now in charge of them, which makes their names more difficult to discover.

Luckily, there's someone looking out for us here.  The System.Web.Compilation namespace has all kinds of goodies to help us out here, namely the BuildManager class.  It has a GetType overload that at first glance appears to do exactly what we want, unfortunately it only seems to work if the assembly in question has already been loaded.  This is not usually the case when our configuration code runs the first time.  Instead, you can use a combination of GetCompiledAssembly and good ol' Assembly.GetType.  So now, instead of knowing the assembly name, we need to know the virtual path to the compiled control. So here's a snippet that does generally what I want:

if (!String.IsNullOrEmpty(urlString)) {

    System.Reflection.Assembly assembly = BuildManager.GetCompiledAssembly(urlString);

    theTypeIWant = assembly.GetType(typeString, true);

}

else {

    theTypeIWant = Type.GetType(typeString, true);

}

So, for those types that reside under ASP.NET's control, I add the virtual path to the configuration and leave out the assembly name, and use it's presence to determine if the BuildManager needs to get involved.

Also notice the wonderful String.IsNullOrEmpty method.  Now, if there were only some kind of operator I could use to be even lazier about that check.

posted on Monday, May 02, 2005 10:16:09 AM (Pacific Standard Time, UTC-08:00)  #    Comments [0]