<?xml version="1.0" encoding="UTF-8" ?>
<?xml-stylesheet type="text/xsl" href="http://blogs.msdn.com/utility/FeedStylesheets/rss.xsl" media="screen"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/" xmlns:wfw="http://wellformedweb.org/CommentAPI/"><channel><title>Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx</link><description>Fibers let both sides think they're in control.</description><dc:language>en-US</dc:language><generator>CommunityServer 2.1 SP1 (Build: 61025.2)</generator><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344805</link><pubDate>Fri, 31 Dec 2004 15:47:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344805</guid><dc:creator>Anthony Wieser</dc:creator><description>Thanks for this Raymond.  &lt;br&gt;&lt;br&gt;I've always wanted to see a real use of fibers shown somewhere.  Do newer versions of windows make much use of them?&lt;br&gt;&lt;br&gt;</description></item><item><title>The history of fibers...</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344819</link><pubDate>Fri, 31 Dec 2004 16:10:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344819</guid><dc:creator>Memet</dc:creator><description> Thanks Raymond,&lt;br&gt;&lt;br&gt;btw, did the Windows team introduce fibers for any reason? (e.g. MS SQL?) Since from previous conversations, I get the impression that only Win32 has such an explicitly defined Fiber API, and also because Fibers are a relatively obscure thing that most people probably wouldn't ask for, let alone know about.&lt;br&gt;</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344844</link><pubDate>Fri, 31 Dec 2004 17:06:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344844</guid><dc:creator>Grant</dc:creator><description>Just FYI, C# iterators (new in v2.0) allow this same feat without using fibers.  Instead the compiler transforms the producers' preferred code into the consumers' perferred interface.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344847</link><pubDate>Fri, 31 Dec 2004 17:11:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344847</guid><dc:creator>Joe Game Developer</dc:creator><description>Very cool example!&lt;br&gt;&lt;br&gt;As a point of information, some games use fibers. You give each object in the game world its own fiber.&lt;br&gt;&lt;br&gt;You could use threads (or even processes) instead, but then you have much more overhead because of having to lock to avoid potential race conditions.</description></item><item><title>Games</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344860</link><pubDate>Fri, 31 Dec 2004 17:34:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344860</guid><dc:creator>Sven G. Ali</dc:creator><description>Yes, this is incredibly cool.&lt;br&gt;&lt;br&gt;When I started reading this series I was immediately reminded of my own struggles creating a game engine some time ago.&lt;br&gt;&lt;br&gt;In a perfect world each game entity would be on its own thread running its own script. The renderer would be on another thread, getting samples of the game world as necessary.&lt;br&gt;&lt;br&gt;The problem of course is that threads are expensive, locks are required, and the thread scheduler doesn't provide enough control.&lt;br&gt;&lt;br&gt;The only way to make it work was, as Raymond says, to make life difficult for one side or the other. In my case, and I believe in most game engines, life was made easy for the renderer.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344890</link><pubDate>Fri, 31 Dec 2004 18:39:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344890</guid><dc:creator>Michael Grier [MSFT]</dc:creator><description>Fibers are definitely designed to help applications which really understand their data sets, their I/O patterns and don't have a big plug-in/extensibility mechanism.  At some point large applications like this become CPU bound since they're actually used by enterprises who do insanely expensive things like buy large storage arrays that let the number of concurrent I/Os scale as far as the CPU and operating system can push them.&lt;br&gt;&lt;br&gt;So then they need to start optimizing CPU usage and they can't afford the statistical goodness of things like thread scheduling and I/O completion ports.&lt;br&gt;&lt;br&gt;I bet you can figure out which applications really take advantage of fibers and therefore which bugs around using fibers have gotten fixed.  They're enterprise applications which do a lot of I/O but are run by large enough physical systems that the real limiting factor is CPU use.&lt;br&gt;&lt;br&gt;Caveat emptor.  To solve the easy enumerators problem you really want to have smaller things like  closures/continuations without having to keep the entire stack around.  This is hard without compiler support and even then managing the lifetime of the storage that the closure references is hard without GC.  This is effectively how iterators/&amp;quot;yield&amp;quot; works in C#.&lt;br&gt;</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344899</link><pubDate>Fri, 31 Dec 2004 19:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344899</guid><dc:creator>RJ</dc:creator><description>It seems that SwitchToFiber would invalidate all CPU predictions and stall the pipeline.  If that is true, another warning for CPU bound apps would be to not call it in a tight loop.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344910</link><pubDate>Fri, 31 Dec 2004 19:24:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344910</guid><dc:creator>Ben Cooke</dc:creator><description>I co-wrote a simple game engine some time ago which used coroutines, which fibers seem to be an implementation of, to allow objects to independently operate.&lt;br&gt;&lt;br&gt;In my case, each &amp;quot;object&amp;quot; in the game was able to register one or more coroutines which would be resumed each frame. Each time the coroutine was resumed the current tick count was passed in so that the coroutine would know how much to adjust its associated object by, depending on what it was supposed to be doing.&lt;br&gt;&lt;br&gt;This wasn't done with Win32 Fibers, though. Indeed, Raymond's series here was the first I've ever heard of them. My co-author had implemented a simple scripting system, so my coroutine implementation just played with the stack kept inside the script interpreter. The coroutines didn't exist outside the script context.&lt;br&gt;&lt;br&gt;The nice thing about coroutines vs. threads is that it's up to each routine when it yields, so things like locking become less important. If a coroutine is going to do something unsafe, it must just make sure it leaves everything in a sensible state before yeilding.&lt;br&gt;&lt;br&gt;In practice, most applications other than &amp;quot;real-time&amp;quot; games are better off with threads, of course.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344945</link><pubDate>Fri, 31 Dec 2004 21:00:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344945</guid><dc:creator>Rob Earhart</dc:creator><description>One nitpick: ConvertThreadToFiber() should fail with GetLastError() == ERROR_ALREADY_FIBER if the thread's already a fiber.  So try it; if it fails for that reason, keep going, but skip the call to ConvertFiberToThread() at the end, and you can work with components which called yours on a fiber.&lt;br&gt;&lt;br&gt;In Longhorn, there's also IsThreadAFiber(), which should always work.&lt;br&gt;&lt;br&gt;GetCurrentFiber() and GetFiberData() don't return garbage; they just don't return what you're expecting (the field in the TEB is overloaded with OS2 information).  If you file a bug against me, I'll look into fixing them.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344950</link><pubDate>Fri, 31 Dec 2004 21:14:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344950</guid><dc:creator>Raymond Chen</dc:creator><description>ERROR_ALREADY_FIBER is returned only on Windows Server 2003. For Windows 98, 2000, and XP, converting a thread to a fiber twice results in random behavior.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344953</link><pubDate>Fri, 31 Dec 2004 21:34:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344953</guid><dc:creator>Pavel Lebedinsky</dc:creator><description>Given all the dire warnings about fibers, has anyone actually measured how much slower the equivalent version using threads would be?&lt;br&gt;&lt;br&gt;I suspect that switching back and forth between two fiber stacks for each produced element is also not very good for performance. &lt;br&gt;&lt;br&gt;If threads are used instead then for large collections you could batch the elements by putting a producer-consumer queue between the enumerator thread and the main thread. Then the overhead of thread switching will be amortized over a large number of elements, and you will not have to worry about fiber-safety.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344954</link><pubDate>Fri, 31 Dec 2004 21:37:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344954</guid><dc:creator>Bug Hunter</dc:creator><description>&amp;quot;ERROR_ALREADY_FIBER is returned only on Windows Server 2003. For Windows 98, 2000, and XP, converting a thread to a fiber twice results in random behavior.&amp;quot;&lt;br&gt;&lt;br&gt;How did this ever get designed this way?!?&lt;br&gt;</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#344956</link><pubDate>Fri, 31 Dec 2004 21:44:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:344956</guid><dc:creator>Raymond Chen</dc:creator><description>Because fibers are a brain-the-size-of-a-planet feature. You are expected to have already worked through all the difficulties yourself - like deciding who will do the thread-to-fiber conversion. In the same way you don't get an error if you free memory twice; you're expected simply not to do that in the first place.&lt;br&gt;&lt;br&gt;Remember, the ENTIRE PROGRAM must coordinate fiber management - see all the dire warnings - if you're coordinating fibers on a program-wide basis surely you can decide who will be responsible for the thread-to-fiber conversion; that's the least of your problems.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#345192</link><pubDate>Sat, 01 Jan 2005 09:49:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:345192</guid><dc:creator>M Knight</dc:creator><description>Pavel Lebedinsky, switching between fibers is *cheap* compared to switching between threads.&lt;br&gt;&lt;br&gt;A switch between fibers involves; saving the registers state, and then loading the new register state from the new fiber. This will also switch the stack too. You are looking at a few dozen bytes which need to be moved about.&lt;br&gt;&lt;br&gt;With a thread; it involves a trip to kernel land, saving the registers state, loading the new registers state, and then a trip back to user-land.&lt;br&gt;&lt;br&gt;The timesaver is the removal of the trips between user-mode and kernel-mode todo a context switch. </description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#345244</link><pubDate>Sat, 01 Jan 2005 18:11:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:345244</guid><dc:creator>Michael Grier [MSFT]</dc:creator><description>If memory serves, SQL Server gets something like a 5-10% boost when running in fiber mode.  However I believe that it then can't use XSPs (the DLL-based plug-in mechanism) because, in general, code can't deal with fibers / shifting across threads.  (This is why it's not the default; it does break some code that may be running in-process with SQL.)&lt;br&gt;&lt;br&gt;Maybe they've done the work so that XSP invocations are remoted to run on threads that aren't fibrous; it's been too many years since I've paid deep attention to SQL server, so please research the details here (XSPs) yourself.&lt;br&gt;&lt;br&gt;Re: cost:&lt;br&gt;&lt;br&gt;I may be wrong here, but I believe that a context switch forces reloading the PCR (on x86) which is a very expensive operation for the CPU.  Fiber switches switch a lot less state, don't go into kernel mode and don't change the PCR.&lt;br&gt;&lt;br&gt;Mike&lt;br&gt;</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#345323</link><pubDate>Sun, 02 Jan 2005 00:17:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:345323</guid><dc:creator>Pavel Lebedinsky</dc:creator><description>&amp;gt; switching between fibers is *cheap* compared to switching between threads.&lt;br&gt;&lt;br&gt;My point was that any context switching (thread or fiber) has additional costs because of poor locality of reference. These costs may dominate everything else (things like saving the registers, transition to KM and back, running the scheduler code etc). If that's the case then using fibers doesn't really buy you anything, except all the bugs you're going to discover because most code has never been tested with fibers and probably is not fiber-safe.&lt;br&gt;&lt;br&gt;Now I'm not an expert on this to be able to say one way or another. But when I googled for [&amp;quot;context switch&amp;quot; &amp;quot;cache miss&amp;quot; cost] the first hit was this article:&lt;br&gt;&lt;br&gt;The Effect of Context Switches on Cache Performance&lt;br&gt;&lt;a target="_new" href="http://www.eecs.harvard.edu/cs245/papers/Shieyuan.html"&gt;http://www.eecs.harvard.edu/cs245/papers/Shieyuan.html&lt;/a&gt;&lt;br&gt;&lt;br&gt;They say that at least in some cases, cache-performance dominates the cost of a context switch. Note that this was back in 1991, and by all accounts the situation has only become worse in recent years because CPU speeds grew much faster than memory speeds.&lt;br&gt;&lt;br&gt;So it seems to me that unless you've actually *measured* the performance of both solutions and found fibers significantly faster, you're better off staying with threads.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#345487</link><pubDate>Sun, 02 Jan 2005 19:52:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:345487</guid><dc:creator>Ryan Myers [MSFT]</dc:creator><description>Any kind of context switch -- whether an explicit one made in fibers, or a true thread change, is painful in terms of cache swap.  It's still true, though, that SwitchToFiber() is cheaper than a normal thread context switch, as fiber switching can be done almost entirely in non-priviledged (usermode) code.&lt;br&gt;&lt;br&gt;And yes, it is still true today that context switches kill the cache.  Those of us in the performance team have been on several crusades to reduce the number of switches needed in an &amp;quot;idle&amp;quot; system over the last few years -- pushing for fewer services, fewer ISRs/DPCs, etc.  In my particular case, my rallying cry has been high-definition video playback; we've found that having a high number of ISRs/DPCs per second will absolutely sink decoding, even if every single interrupt executes quickly, because they kill the cache.  (This is in addition, of course, to the problem of ISRs/DPCs that take too long and lock up the CPU for extended periods of time, such as long NDIS chains or non-conformant ATAPI drives.)</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#346007</link><pubDate>Tue, 04 Jan 2005 00:18:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:346007</guid><dc:creator>Frank</dc:creator><description>Just a thought - couldn't you do something similar with &amp;quot;good-old&amp;quot; POSIX setjmp()/longjmp()? Not sure though, I guess it lacks the 'double stack'.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#346423</link><pubDate>Tue, 04 Jan 2005 19:06:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:346423</guid><dc:creator>Michael Grier [MSFT]</dc:creator><description>Yes, a fiber is very similar to a jmp_buf.  The thing is that setjmp/longjmp explicitly only are enabled for jumping up the stack, not across between stacks.  There's no defined way to allocate a new stack, get code running on it and save its context into a jmp_buf in standard C etc.&lt;br&gt;&lt;br&gt;Note also that since a jmp_buf is a struct, its size can't really vary and depends on the version of the C runtime (regardless of platform) that you are built with.  Fibers are provided by the OS on windows and therefore if someone discovers that some critical piece of state wasn't captured/switched, it can be added.&lt;br&gt;&lt;br&gt;Unfortunately those additions do tend to break apps who were depending on that piece of context not being switched.  Luckily most people have heeded the warning and aren't using fibers except for people who think they already understand their per-fiber context pretty well.&lt;br&gt;</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#347149</link><pubDate>Wed, 05 Jan 2005 21:48:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:347149</guid><dc:creator>lukaszg</dc:creator><description>&amp;gt;&amp;gt;&lt;br&gt; (It's amazing how many people ask questions without taking even the slightest steps towards figuring out the answer themselves. Try writing a test program.) &lt;br&gt;&amp;lt;&amp;lt;&lt;br&gt;&lt;br&gt;Isn't that exactly how people come to rely on undocumented behavior? Just because a test program behaves in a certain way on your machine doesn't mean it will on others. Answers to questions like this should be in the docs (don't know if this one is, just making a general point). Of course, sometimes writing a test program is the only way, but it shouldn't be the first one tried.</description></item><item><title>re: Using fibers to simplify enumerators, part 3: Having it both ways</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#347159</link><pubDate>Wed, 05 Jan 2005 22:03:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:347159</guid><dc:creator>Raymond Chen</dc:creator><description>The test program is *one step* towards figuring out the answer. A test program can't tell you that you're right, but it can tell you that you're wrong.</description></item><item><title>Why does Win32 even have Fibers?</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#347316</link><pubDate>Thu, 06 Jan 2005 02:58:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:347316</guid><dc:creator>Larry Osterman's WebLog</dc:creator><description /></item><item><title>Partially Debuggable CoRoutines</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#357618</link><pubDate>Fri, 21 Jan 2005 02:33:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:357618</guid><dc:creator>Wizzy's World</dc:creator><description>A while back there was an article in MSDN magazine about wrapping up the unmaged fibers API to implement...</description></item><item><title>C# iterators are not as powerful</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#361998</link><pubDate>Thu, 27 Jan 2005 23:10:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:361998</guid><dc:creator>Kannan Goundan</dc:creator><description>While C# iterators cover a good chunk of what you'd normally want to do, they don't use separate stacks and so you can't do everything fibres can.  The main restriction is that all your &amp;quot;yield&amp;quot;s have to be in the top function instance.&lt;br&gt;&lt;br&gt;IEnumerator&amp;lt;X&amp;gt; InOrder(Tree&amp;lt;X&amp;gt; t) {&lt;br&gt;   if (t != null) {&lt;br&gt;      InOrder(t.left);&lt;br&gt;      yield return t.value;&lt;br&gt;      InOrder(t.right);&lt;br&gt;   }&lt;br&gt;}&lt;br&gt;&lt;br&gt;At the least, you'd have to put a for loop around the recursive calls and &amp;quot;re-yield&amp;quot; the values to the top.  This is pretty inefficient.&lt;br&gt;&lt;br&gt;Full continuation support would fix this.  Unfortunately (AFAIK), adding continuation support causes a significant performance hit (even if you don't use them).&lt;br&gt;</description></item><item><title>Coroutines in C# and Delphi: Part 1</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#369483</link><pubDate>Wed, 09 Feb 2005 02:43:00 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:369483</guid><dc:creator>Joe White's Blog</dc:creator><description /></item><item><title>Coroutines</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#1453680</link><pubDate>Fri, 12 Jan 2007 06:19:55 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1453680</guid><dc:creator>Joe White's Blog</dc:creator><description /></item><item><title>Coroutines</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#1453685</link><pubDate>Fri, 12 Jan 2007 06:21:27 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:1453685</guid><dc:creator>Joe White's Blog</dc:creator><description /></item><item><title>When does it make sense to use Win32 Fibers?</title><link>http://blogs.msdn.com/oldnewthing/archive/2004/12/31/344799.aspx#8518223</link><pubDate>Sun, 18 May 2008 18:29:49 GMT</pubDate><guid isPermaLink="false">91d46819-8472-40ad-a661-2c78acb4018c:8518223</guid><dc:creator>Eric Eilebrecht's blog</dc:creator><description>&lt;p&gt;This has been discussed fairly frequently on the Web. Chris Brumme discusses this here: &lt;a rel="nofollow" target="_new" href="http://blogs.msdn.com/cbrumme/archive/2003/04/15/51351.aspx"&gt;http://blogs.msdn.com/cbrumme/archive/2003/04/15/51351.aspx&lt;/a&gt;&lt;/p&gt;
</description></item></channel></rss>