There is no such thing as a "simple" feature. The example of-the-day is Debugger.Launch, the API that will launch a managed debugger with special command line arguments that will direct it to automatically attach it to the calling app (see here for details). We call this a "just-in-time (JIT) attach".
The basis pseudo code would be: if (Debugger.IsAttached) return; LaunchTheDebugger(); WaitForAtachOrAbort();Sounds easy enough, right?
Once you weed through all the issues like security ("am I allowed to launch a debugger"), policy ("which debugger do I launch?"), etc in the LaunchTheDebugger() phase, you still have a giant surface area for design holes and bugs:
You could probably rattle off a dozen more interesting multi-threaded races. There are so many moving parts: multiple-threads trying to share 1 jit-attach request, threads having to block waiting for the attach, jit-attach aborting, jit-attach succeeding but detaching immediately, another debugger trying to attach during this window, etc...
I just rewrote the mechanism for managed jit-attach. I foolishly thought it would be simple, and then quickly realized how much surface area there was. Nothing is easy!