Tuesday, August 24, 2004

So today I gave Scott Holden of Compact Framework Dev Team fame to discuss my obsession with the GC.  These were my take-aways from the call:

  1. The GC actually runs *on* the executing thread, so there's no specific GC thread to get ahold of.  So much for that idea (at least I got a real Process Viewer out of it, not that piece of crap Running Programs thing).
  2. Since GC won't happen during a P/Invoke call I was considering calling an unmanaged DLL that would host the EE, pass it a function pointer (evidently available in CF 2.0 - been too busy to actually investigate), and have the DLL run the ISR.  Evidently when you step across the managed/unmanaged boundary in either direction, the GC is given a chance to run.  There goes that idea.
  3. The GC has no mechanisms for telling it not to run.  The only way to prevent it's running in any piece of code is to not make an allocation (simple for the ISR itself).  You must be guaranteed that no allocation happens in the process.  Since the ISR really must run in its own thread, you'd have to have some sort of locking mechanism against *all* memory allocations during the ISR execution.  Ugly if possible at all.  There goes that idea.
  4. Scott believes that “impossible“ is rare in software, though this one borders on very, very difficult.  He said getting a driver hosted in device.exe might be more difficult, but the two are probably the closest to the impossible line he can think of.

So the call left me a bit disappointed.

Since nothing fun ever comes easy, I back-burnered the idea for a while - until I was washing dishes tonight to be precise - and then I had an epiphany - “what alcoholics refer to as a moment of clarity” if you will.

Given that the GC runs on the current thread and given that if you make no allocations during the execution of a section of code (your ISR) then the GC won't run, then all we have to do is to make sure that our ISR makes no allocations and is guaranteed to be the running thread, right?  Well why not use CeSetThreadPriority to set our ISR/IST thread priority higher (well lower numerically) than any other managed thread can go? 

Think of it this way: you have your main thread at priority 251, it creates an IST and sets it's priority to 245 or so.  The IST allocates whatever it needs for the ISR execution and then blocks on a WaitForSingleObject waiting for the interrupt event.  The main thread then runs along merrily until blam!  We get an interrupt.  The IST unblocks and interrupts the primary thread because of its priority (setting a high quantum may be a good idea too?).  The ISR runs, does its thing, then blocks again at WaitForSingleObject.

In theory it sounds good, but right now it's just an idea.  I'll attempt to find time to give it a try in the next few days.  The only problem I see right now is what if the GC is already collecting at the time of the interrupt?  Will the IST be blocked until the GC is done, or will the GC be running on the primary thread, so it in turn will be interrupted by the higher priority IST?  Only experimentation will tell.

8/24/2004 9:58:38 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [1]  | 
 Saturday, August 21, 2004

Here's a great look from Steven at how POOM will be accessed (most likely anyway) through version 2.0 of the CF.

8/21/2004 9:41:05 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 

The OpenNETCF Coding Competition has only 10 days left!  Time to burn the midnight oil and crank out some code - that SmartPhone is calling your name....

8/21/2004 1:36:21 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  | 
 Friday, August 20, 2004

So I've been playing with the idea of how you could get deterministic behaviour out of managed code.  Of course the party line is that it's not possible, but to me that's just throwing down the gauntlet.  They also say you can't suspend and resume a managed thread, to which I replied “Yeah? Watch me!”

This was fueled when I was at DevCon and sat in on a lab of Nat Frampton's where you simply create an ISR that responds to a contact on a serial port pin.  I wondered “well why the hell can't I do that in managed code?”  No reason really - I mean it's simply setting up an event to fire when an interrupt is received, and all of that can be P/Invoked (I haven't actually written the code yet, but there's nothing magic about it).  The problem comes with determinism.

Conventional wisdom says that the GC suspends all managed threads when it's doing its work (which can be annoying in most cases).  If this were to happen while you were doing your ISR work, you could get a really bad outlier.  So your system responds in milliseconds most of the time, but occasionally it takes a few seconds.  That's unacceptable - a box flies of the conveyor and hits the floor, making a mess.

So how do we handle this?  How about setting the GC thread priority to 255 at the start of your ISR, then back to it's original priority afterward?  To even better handle it, your ISR priority needs to be set high.

How do we proceed then?  We need the GC thread handle to manipulate its priority.  In comes the toolhelp libraries.  I wrote a ToolHelp wrapper for the SDF earlier in this quest, but I just realized yesterday I only had the PROCESSENTRY32 stuff done, not the THREADENTRY32, so I spent last night doing that piece.

Still, the THREADENTRY32 only has a ThreadID - how are we to determine which thread is the GC as opposed to all the other threads we may have in our process?  Well, I ran a quick check with a slap-together process viewer app based on my new toolhelp wrappers and I see this:

Notice the Priority 248 thread then think about managed priorities - there are five ranging from Lowest (0) to Highest(4), which presumably equate to 255 to 251, though I've yet to check.

My initial thought was that the 248 priority might be the GC, after all it would make sense that it's higher priority, though I also recall that all manged apps need 3 threads.  So why do I have 5?  Well the answer is that this was running through the debugger.  When run stand-alone, I get 3 threads, all with a priority of 251.

So where does that leave us?  Well first, it's evident that 251 is “Normal” priority, so I'll need to map out the actual values for the managed equivalents, but more importantly we still have no way to uniquely identify the GC thread (that I've found anyway).

Right now that's the show stopper.  If you've got any ideas on how to reliably identify the GC thread handle, I'd love to hear them. 

I guess on the plus side, outside of the designer work, it's only about 10 lines of code to create the process viewer you see here.  I'll probably add a few features and turn it into a sample for using the SDF.

8/20/2004 1:49:41 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [2]  | 
 Tuesday, August 10, 2004

Man, I've been asking for it for sometime and finally here it is, a full explanation of how the GC works in the CF!  Yet more required reading for the CF developer.

8/10/2004 10:18:58 PM (Eastern Daylight Time, UTC-04:00)  #    Comments [0]  |