Categories

Blogroll

Search

The Exception Model

May 29th, 2008 by kerrysoft and tagged , ,



I had skiped this article would be on changes to the next version of the CLR which let it to be hosted inside SQL Server and other “ambitious” environments.  This is more by and large interesting than you might cogitate, because it makes an opportunity for other processes (i.e. your processes) to host the CLR with a like level of integration and control.  This lets in control over memory usage, synchronization, meandering (including fibers), led security models, assembly storage, and more.

 

Nevertheless, that topic is of necessity related to our next release, and I cannot discuss abstruse details of that next release until those details have been publically divulged.  In former October, Microsoft is concuring its PDC and I require us to reveal many details at that time.  In fact, I’m blessed up to be a member of a PDC panel on this topic.  If you work at a database or an application server or a likewise perplexed product that might benefit from hosting the CLR, you may require to go to.

Customer Help Solution: jbTop is Jabber/XMPP Live Chat Soulution for your website.

 

After we’ve gave away the hosting changes for our next release, you can require a blog on hosting in former October or some time in November.

 

Alternatively, this blog is on the cared exception model.  This is an strange topic for me.  In the past, I’ve broken up topics where I can plunge information without having to learn any of my facts or do any research.  But in the case of exceptions I keep on discovering questions I cannot do.  At the top level, the handled exception model is prissy and unproblematic.  But – as with everything else in software – the closer you count, the more you find.

 

Hence for the first time I made up one’s mind to have some CLR experts take my blog entry before I carry it.  In addition to bespeaking out a bunch of my errors, all the reviewers were consentient on one point: I should spell unretentive blogs.

 

Of course, we can’t spill almost handled exceptions without first of all reckoning Windows Integrated Exception Handling (SEH).  And we besides take to look at the C++ exception model.  That’s because both carryed off exceptions and C++ exceptions are implemented on top of the underlying SEH mechanism, and because pulled off exceptions must interoperate with both SEH and C++ exceptions.

 

 

Windows SEH

 

Since it’s at the base of all exception handling on Windows, permit’s look at SEH first.  As far as I know, the unequivocal explanation of SEH is notwithstanding Matt Pietrek’s first-class 1997 article for Microsoft Systems Journal: http://www.microsoft.com/msj/0197/exception/exception.aspx .  There have been some extensions since so, like vectored exception handlers, some security enhancements, and the novel mechanisms to hold up IA64 and AMD64.  (It’s difficult to found exceptions on FS:[0] chains if your processor doesn’t have an FS segment register).  We’ll look at all these changes in short.  But Matt’s 1997 article staies on a goldmine of information.  In fact, it was very utile to the developers who enforced exceptions in the CLR.

 

The SEH model is exposed by MSVC via two makes:

 

  1. __try {…} __except(filter_expression) {…}
  2. __try {…} __in the end {…}

 

Matt’s article excuses how the underlying mechanism of two passes over a chain of single callbacks is employed to leave try/except/ultimately semantics.  In brief, the OS dispatches an exception by calling up the head of the SEH chain from TLS.  Since the head of this chain is at the top of the TIB/TEB (Meander Information Block / Thread Environment Block, relying on the OS and the header file you consider), and since the FS segment register leaves truehearted access to this TLS block on X86, the SEH chain is much called up the FS:[0] chain.

 

Each entry consists of a next or a prev pointer (reckoning on how you deal it) and a callback function.  You can sum up whatever data you like after that received entry header.  The callback function is called with all sorts of extra information related to the exception that’s being treated.  This lets in the exception record and the register state of the machine which was captured at the time of the exception.

 

To apply the 1st form of MSVC SEH above (__try/__except), the callback evaluates the filter expression during the first pass over the handler chain.  As exposed by MSVC, the filter expression can result in one of three effectual values:

 

EXCEPTION_CONTINUE_EXECUTION = -1

EXCEPTION_CONTINUE_SEARCH = false 0

EXCEPTION_EXECUTE_HANDLER = true 1

 

Of course, the filter could besides switch its ain exception.  That’s not by and large suitable, and I’ll talk over that possibility and other flow control issues later.

 

But if you take the underlying SEH mechanism, the handler in reality turns back an EXCEPTION_DISPOSITION:

 

typedef enum _EXCEPTION_DISPOSITION

{

   ExceptionContinueExecution,

   ExceptionContinueSearch,

   ExceptionNestedException,

   ExceptionCollidedUnwind

} EXCEPTION_DISPOSITION;

 

So there’s some mapping that MSVC is doing hither.  Part of that mapping is simply a piddling conversion between the MSVC filter values and the SEH handler values.  For instance ExceptionContinueSearch has the value 1 at the SEH handler level but the tantamount EXCEPTION_CONTINUE_SEARCH has the value 0 at the MSVC filter level.  Ouch.

 

But the other part of the mapping has to do with a difference in functionality.  For example, ExceptionNestedException and ExceptionCollidedUnwind are in the first place used by the OS dispatch mechanism itself.  We’ll hear the circumstances in which they turn out later.  More importantly, MSVC filters can argue that the __except clause should run by reverting EXCEPTION_EXECUTE_HANDLER.  But we shall realize that at the SEH level this decision is achieved by having the exception dispatch routine set up up the register context and and so taking up execution at the proper spot.

 

The EXCEPTION_CONTINUE_EXECUTION case holds up a rather esoteric use of SEH.  This return value permits the filter to adjust the problem that made the exception and to take up execution at the faulting instruction.  For example, an application might be finding out to find out when segments are being written to thus that it can lumber this information.  This could be achieved by ticking the segment as ReadOnly and waiting for an exception to come on foremost spell.  And then the filter could apply VirtualProtect to exchange the segment comprising the faulting address to ReadWrite and so re-start the faulting instruction.  Instead, the application could have two VirtualAllocs for each region of memory.  One of these could be checked off as ReadOnly and the second could be a shadow that is checked off as ReadWrite.  Today the exception filter can but interchange the register state of the CPU that blamed, thence that the register incorporating the faulting address is changed from the ReadOnly segment to the shaded off ReadWrite segment.

 

Apparently anyone who is runing these games must have a lot of sophistication and a recondite knowledge of how the program executes.  Some of these games work out better if you can tighten up the code that’s generated by your program to simply allude faulting memory applying a predictable cliché similar offsets from a especial register.

 

I’ll discuss this kind of restartable or resumable exception in the context of handled code later.  For today, allow’s guess that the filter either returns “true – I would care my ‘except’ clause to manage this exception” or “false – my ‘except’ clause is uninterested in this exception”.  If the filter reverts faux, the next SEH handler is fetched from the chain and it is required this same question.

 

The OS is pretty paranoid about corrupted stacks during this chain traversal.  It determines that all chain entries are within the bounds of the stack.  (These bounds are likewise recorded in the TEB).  The OS too finds out that all entries are in moving up order on the stack.  If you go against these rules, the OS will regard the stack to be corrupted and will be ineffectual to process exceptions.  This is one of the reasons that a Win32 application cannot break in its stack into multiple disjoined segments as an innovative technique for dealing with stack overflow.

 

In any case, finally a handler supposes “true – I would wish my ‘except’ clause to care this exception”.  That’s because there’s a backstop entry at the end of the chain which is ordered at that place by the OS when the thread is made.  This last entry needs to manage all the exceptions, yet if your application-level handlers ne’er do.  That’s where you catch the default OS behavior of confabing the unhandled exception filter list, fliping up dialog boxes for Terminate or Debug, etc.

 

As shortly as a filter argues that it requires to care an exception, the first pass of exception handling ceases and the second pass leads off.  As Matt’s article excuses, the handler can employ the poorly documented RtlUnwind service to deliver second pass notifications to all the former handlers and crop up them off the handler chain.

 

In other words, no slowing down befallen as the first pass built up.  But during the second pass we realize two distinguishable forms of unwind.  The first form necessitates croping up SEH records from the chain that was threaded from TLS.  Each such SEH record is popped before the like handler catchs involved the second pass.  This allows for the SEH chain in a sensible form for any nestled exceptions that might occur within a handler.

 

The other form of unwind is the real popping of the CPU stack.  This doesn’t befall as thirstily as the popping of the SEH records.  On X86, EBP is employed as the frame pointer for methods comprising SEH.  ESP points to the top of the stack, as ever.  Until the stack is in reality unwound, all the handlers are executed on top of the faulting exception frame.  So the stack in reality springs up when a handler is necessitated the first or second pass.  EBP is set to the frame of the method comprising a filter or at long last clause so that local variables of that method will be in scope.

 

The real popping of the stack doesn’t come until the geting ‘except’ clause is run.

 

So we’ve caught a handler whose filter announced in the first pass that it would manage this exception via EXCEPTION_EXECUTE_HANDLER.  And that handler has tuged the second pass by decompressing and pitching all the second pass notifications.  Typically it will and so fiddle with the register state in the exception context and take up execution at the top of the appropriate ‘except’ clause.  This isn’t needs the case, and later we’ll discover some situations where the exception propagation catchs hived off.

 

How well-nigh the try/in the end form of SEH?  Well, it’s rested on the same underlying notion of a chain of callbacks.  During the first pass (the one where the filters run, to make up one’s mind which except block is plumping to get), the at last handlers all suppose EXCEPTION_CONTINUE_SEARCH.  They ne’er in reality get anything.  So in the second pass, they run their at last blocks.

 

 

Subsequent additions to SEH

 

All of the above – and a lot more – is in Matt’s article.  There are a few things that aren’t in his article because they were added to the model later.

 

For example, Windows XP brought out the notion of a vectored exception handler.  This permits the application to register for a first crack at an exception, without having to wait for exception handling to disseminate down the stack to an planted handler.  Luckily, Matt spelt an “Under The Hood” article on this exceptional topic.  This can be found at http://msdn.microsoft.com/msdnmag/issues/01/09/hood/default.aspx .

 

Another change to SEH is related to security.  Buffer infests – whether on the stack or in heap blocks – stay on a pet attack vector for hackers.  A distinctive buffer infested attack is to go a magnanimous string as an argument to an API.  If that API required a forgetful string, it might have a local on the stack like “coal filename[256];”.  Today if the API is jerky enough to strcpy a malicious hacker’s argument into that buffer, so the hacker can assign some fairly arbitrary data onto the stack at addresses higher (further backward on the stack) than that ‘filename’ buffer.  If those higher locations are opined to incorporate call up return addresses, the hacker may be capable to catch the CPU to reassign execution into the buffer itself.  Oops.  The hacker is shooting arbitrary code and and so runing it, potentially inside someone else’s process or under their security credentials.

 

There’s a fresh speed find that an application can apply to deoxidise the likelihood of a successful stack-grounded buffer invaded attack.  This requires the /GS C++ compiler switch, which utilises a cookie check in the function epilog to find out whether a buffer overrun has bought the return address before runing a return based on its value.

 

Still, the return address trick is but one way to tap buffer infests.  We’ve already learnt that SEH records are needs built on the stack.  And in fact the OS in reality learns to be certain they are within the stack bounds.  Those SEH records comprise callback pointers which the OS will appeal if an exception comes.  So another way to tap a buffer overrun is to rewrite the callback pointer in an SEH record on the stack.  There’s a fresh linker switch (/SAFESEH) that can allow for its ain speed bump against this sort of attack.  Modules built up this way adjudge that all their handlers are embedded in a table in the image; they do not point to arbitrary code sequences sprinkled in the stack or in heap blocks.  During exception processing, the exception callbacks can be validated against this table.

 

Of course, the first and betterest line of defense against all these attacks is to ne’er infested a buffer.  If you are writing in dealt code, this is unremarkably pretty prosperous.  You cannot make a buffer overrun in dealt code unless the CLR incorporates a bug or you do insecure operations (e.g. unobjective MC++ or ‘unsafe’ in C#) or you apply high-pitched-privilege insecure APIs like StructureToPtr or the assorted overloads of Copy in the System.Runtime.InteropServices.Marshal class.

 

Hence, not amazingly and not only for this reason, I urge writing in dealt code.  But if you must spell some unmanaged code, you should in earnest view utilizing a String abstraction that rids of all those by-rote opportunities for error.  And if you must inscribe each strcpy separately, be certain to employ strncpy or else!

 

A last interesting change to the OS SEH model since Matt’s article is imputable to Win64.  Both IA64 and AMD64 have a model for exception handling that fends off reliance on an denotative handler chain that starts in TLS and is threaded through the stack.  Alternatively, exception handling depends upon the fact that on 64-bit systems we can perfectly unwind a stack.  And this ability is itself due to the fact that these chips are badly constrained on the calling up conventions they hold up.

 

If you consider X86, there are an limitless number of phoning conventions potential.  Surely, there are a few mutual well-cognized conventions like stdcall, cdecl, thiscall and fastcall.  But optimising compilers can formulate custom telephoning conventions based on inter-adjective analysis.  And developers writing in assembly language can progress to fresh decisions about which records to conserve vs. scratch, how to apply the swiming point stack, how to encode structs into registers, whether to back-propagate results by re-utilizing the stack that incorporated in-obligated arguments, etc.  Within the CLR, we have places where we still unbalance the stack by encoding data after a CALL instruction, which is and then addressable via the return address.  This is a particularly grievous game because it untunes the branch prediction code of the CPU and can do prediction misses on several subsequent RET instructions.  So we are measured to book this technique for low-pitched frequency call paths.  And we likewise have some stubs that figure collateral JMPs to come out-of-line RET ‘n’ instructions in order to rebalance the stack.

 

It would be insufferable for a stack crawler to successfully slow down these off-the-wall stacks for exception purposes, without completely simulating arbitrary code execution.  So on X86 the exception mechanism must depend on the existence of a chain of crawlable FS:[0] handlers that is explicitly held.

 

By the way, the above distinction between pure stack crawling on 64-bit systems vs. hopeless stack crawling on X86 systems has recondite repercussions for the CLR than equitable exception handling.  The CLR takes the ability to creep all the handled portions of a thread’s stack on all architectures.  This is a requirement for right enforcement of Code Access Security; for exact reporting of handled references to the GC; for highjacking return addresses in order to asynchronously read control of threads; and for various other reasons.  On X86, the CLR devotes considerable resources to attaining this.

 

In any event, on 64-bit systems the correspondence between an activation record on the stack and the exception record that applies to it is not achieved through an FS:[0] chain.  Alternatively, unwinding of the stack lets out the code addresses that correspond to a especial activation record.  These instruction pointers of the method are bet up in a table to discover out whether there are any __try/__except/__at long last clauses that comprehend these code addresses.  This table too argues how to proceed with the unwind by naming the actions of the method epilog.

 

 

Cared Exceptions

 

Okay, enough about SEH – for today.  Let’s switch to the handled exception model.  This model comprises a number of constructs.  Depending on the language you cypher in, you in all probability merely have access to a subset of these.

 

try {…} in the end {…}

This is pretty received.  All cared languages should reveal this, and it should be the most mutual style of exception handling in user code.  Of course, in the case of MC++ the semantics of ‘in the end’ is exposed through auto-destructed stack objects kinda than through denotative ultimately clauses.  You should be employing ‘in the end’ clauses to guarantee consistency of application state far more ofttimes than you employ ‘get’ clauses.  That’s because get clauses increase the likelihood that developers will get down exceptions that should be cared elsewhere, or maybe should yet be allowed unhandled.  And if catch clauses don’t in reality get down an exception (i.e. they ‘rethrow’), they even so make a inadequate debugging experience as we shall get wind.

 

try {…} get (Object o) {…}

This is pretty received, also.  One thing that might storm some developers is that you can get any instance that’s of type Object or derived from Object.  Notwithstanding, there is a CLS rule that only subtypes of System.Exception should be switched.  In fact, C# is so eager for you to only deal with System.Exception that it doesn’t leave any access to the switched object unless you are geting Exception or one of its subtypes.

 

When you view that only Exception and its subtypes have support for stack traces, HRESULT mapping, received access to exception messages, and well support throughout the frameworks, and then it’s pretty unmortgaged that you should throttle yourself to fliping and treating exceptions that derive from Exception.

 

In retrospect, perchance we should have trammeled exception support to Exception kind of than Object.  To begin with, we required the CLR to be a utile execution engine for more run-time libraries than but the.NET Frameworks.  We guessed that unlike languages would execute on the CLR with their ain especial run-time libraries.  So we didn’t want to couple the base engine operations too tightly with CLS rules and constructs in the frameworks.  Of course, nowadays we infer that the commonality of the partaken in framework classes is a vast part of the value proposition of our managed environment.  I surmise we would revisit our original design if we all the same could.

 

try {} get (Object o) if (expression) {}

This is formulated syntax, though I’m said it’s close to what MC++ is regarding.  As far as I cognise, the only two.NET languages that presently sustain exception filters are VB.NET and – of course – ILASM.  (We ne’er progress a cared construct without bringing out it via ILDASM and ILASM in a manner that permits these two tools to round-trip between source and binary forms).

 

VB.NET has sometimes been thrown out as a language that’s entirely for less advanced developers.  But the way this language lets out the made headway feature of exception filters is a outstanding example of why that position is too simplistic.  Of course, it is dependable that VB has historically made a brilliant job of allowing for an accessible toolset and language, which has let less advanced developers to be highly generative.

 

At any rate, isn’t this cool:

 

Try

   …examine statements…

Catch e As InvalidOperationException When expressionFilter

   …get statements…

End Try

Of course, at the runtime level we cannot fork the test for the exception type expression and the filter expression.  We simply hold a mere expression.  So the VB compiler turns over the above catch into something like this, where $exception_obj is the inexplicit argument passed to the filter.

Catch When (IsInst($exception_obj, InvalidOperationException)

            && expressionFilter)

 

While we’re on the topic of exception handling in VB, have you e’er questioned how VB.NET implements its On Error statement?

 

      On Error { Goto { <line> | 0 | -1 } | Take up Next }

 

Me neither.  But I opine it’s pretty obvious how to apply this sort of thing with an interpreter.  You wait for something to plump incorrect, and and so you confabulate the dynamic “On Error” setting.  If it saies you to “Take up Next”, you but read frontwards to the next statement and off you plump.

 

But in an SEH world, it’s a little more perplexed.  I tested some elementary test cases with the VB 7.1 compiler.  The ensuing codegen is based on geting ahead a _Vb_t_CurrentStatement local variable to argue the progression of execution through the statements.  A single try/filter/catch encompasss execution of these statements.  It was interesting to learn that the ‘On Error’ command simply applies to exceptions that derive from System.Exception.  The filter resists to treat any other exceptions.

 

So VB is nicely comprehended.  But what if you did necessitate to apply exception filters from C#?  Well, in V1 and V1.1, this would be quite hard.  But C# has denoted a feature for their next release telephoned anon. methods.  This is a compiler feature that postulates no CLR changes.  It lets blocks of code to be noted inline via a delegate.  This lightens the developer from the tedium of delineating denotative methods and state objects that can be gathered into the delegate and the explicit sharing of this state.  This and other seductive forthcoming C# features are described at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dv_vstechart/html/vbconcprogramminglanguagefuturefeatures.asp .

 

Utilising a mechanism like this, someone has betokened out that one could delineate delegates for try, filter and get clauses and run them to a partaken in chunk of ILASM.  I fuck the way the C# compiler emploies type inferencing to mechanically deduce the delegate types.  And it constructs a state object to see to it that the locals and arguments of DoTryCatch are useable to the “try out statements”, “filter expression” and “get statements”, near as if everything was scoped in a single method body.  (I suppose “almost” because any locals or arguments that are of byref, argiterator or typedbyref types cannot be disassociated from a stack without breaking in safety.  So these cases are proscribed).

 

I’m venturing that access to filters from C# could calculate something like this:

 

public void delegate __Try();

public Int32 depute __Filter();

public void delegate __Catch();

 

// this recyclable helper would be defined in ILASM or VB.NET:

void DoTryCatch(__Try t, __Filter f, __Catch c)

 

// And C# could so employ it as traces:

void m(…arguments…)

{

   …locals…

   DoTryCatch(

      { …examine statements…},

      { regress filter_expression; },

      { …get statements…}

   );

}

 

You may acknowledge that I riped off a slight bit.  I didn’t leave a way for the ‘get’ clause to note the exception type that it is geting.  Of course, this could be stated as part of the filter, but that’s not in truth runing fair.  I surmise the solution is to reach DoTryCatch a generic method that has an unbound Type parameter.  Then DoTryCatch<T> could be instantiated for a special type.  Still, I haven’t in reality examined this so I detest to hazard that it would figure out.  I am way slow on understanding what we can and cannot do with generics in our next release, how to state this in ILASM, and how it in reality works under the covers.  Any blog on that topic is years off.

 

While we are on the subject of interesting C# codegen, that same document on approaching features as well hashs out iterators.  These let you to employ the ‘yield’ statement to convince the normal pull model of delineating iteration into a commodious push model.  You can discover the same ‘yield’ notion in Ruby.  And I’m said that both languages have taken over this from CLU, which initiated the feature about the time that I was taken over.

 

When you catch your hands on an updated C# compiler that holds this handy make, be certain to ILDASM your program and learn how it’s attained.  It’s a outstanding example of what a compiler can do to get to life easier for a developer, so longsighted as we’re unforced to cauterize a few more cycles compared to a more matter-of-fact loop build.  In today’s world, this is almost e’er a sensitive trade-off.

 

Okay, that last part has nothing to do with exceptions, does it?  Let’s catch backward to the dealt exception model.

 

try {…} fault {…}

Have you e’er spelt code like this, to confine execution of your at last clause to merely the particular cases?

 

bool particular = honest;

try {

   …body of try…

   especial = imitation;

} in the end {

   if (special) {…}

}

 

Or how about a catch with a rethrow, as an alternating technique for attaining at long last behavior for merely the especial cases:

 

try {

  

} catch {

  

   rethrow;

}

 

In each case, you are accommodating for the fact that your language doesn’t disclose fault blocks.  In fact, I imagine the only language that lets on these is ILASM.  A fault block is but a in the end clause that only executes in the special case.  It ne’er executes in the non-especial case.

 

By the way, the first alternative is preferred to the second.  The second approach ceases the first pass of exception handling.  This is a basically dissimilar semantics, which has a real impact on debugging and other operations.  Let’s take rethrow in more detail, to hear why this is the case.

 

 

Rethrow, restartable exceptions, debugging

 

Gee, my language has rethrow, but no filter.  Why can’t I only process the following builds as tantamount?

 

try {…} filter (expression) catch (Exception e) {…}

try {…} get (Exception e) { if (!expression) rethrow; …}

 

In fact, ‘rethrow’ tries firmly to make the illusion that the initial exception handling is notwithstanding in progress.  It applies the same exception object.  And it augments the stack trace associated with that exception object, thence that it lets in the portion of stack from the rethrow to the eventual catch.

 

Hmm, I pretend I should have already remarked that the stack trace of an Exception is purposely restricted to the segment of stack from the throw to the catch.  We do this for performance reasons, since part of the cost of an exception is elongate with the depth of the stack that we catch.  I’ll discuss the implications of exception performance later.  Of course, you can employ the System.Diagnostics.StackTrace class to get together the rest of the stack from the point of the catch, and and so manually unite it into the stack trace from the Exception object.  But this is a little awkward and we have sometimes been expected to leave a helper to make this more commodious and less unannealed to changes in the formatting of stack traces.

 

By the bye, when you are runing some with stack traces (whether they are associated with exceptions, debugging, or denotative use of the StackTrace class), you will e’er discover JIT inlining arriving your way.  You can test to overcome the JIT inliner through use of indirected calls like function pointers, practical methods, interface calls and delegates.  Or you can reach the called up method “interesting” enough that the JIT makes up one’s mind it would be unproductive or too hard to inline.  All these techniques are blemished, and all of them will fail over time.  The right way to master inlining is to utilize the MethodImpl(MethodImplOptions.NoInlining) pseudo-custom attribute from the System.Runtime.CompilerServices namespace.

 

One way that a rethrow differs from a filter is with respect to resumable or restartable exceptions.  We’ve already got a line how SEH permits an exception filter to regress EXCEPTION_CONTINUE_EXECUTION.  This makes the faulting instruction to be re-started.  Apparently it’s unproductive to do this unless the filter has first of all read care of the faulting situation someways.  It could do this by interchanging the register state in the exception context so that a dissimilar value is dereferenced, or so that execution resumes at a unlike instruction.  Or it could have altered the environment the program is running in, as with the VirtualProtect cases that I noted earlier.

 

In V1 and V1.1, the handled exception model does not hold up restartable exceptions.  In fact, I imagine that we set up EXCEPTION_NONCONTINUABLE on some (but maybe not all) of our exceptions to argue this.  There are several reasons why we put on’t sustain restartable exceptions:

 

  • In order to resort a faulting situation, the exception handler demands versed knowledge about the execution environment.  In dealt code, we’ve gone to outstanding lengths to hide out these details.  For example, there is no architecture-indifferent mapping from the IL expression of stack-established execution to the register set of the underlying CPU.

 

  • Restartability is much desired for asynchronous exceptions.  By ‘asynchronous’ I intend that the exception is not initiated by an denotative call to ‘switch’ in the code.  Kind of, it results from a memory fault or an shot failure like Abort that can happen on any instruction.  Propagating a dealt exception, where this demands execution of a dealt filter, of necessity necessitates the potential for a GC.  A JIT has some discretion over the GC-secure points that it selects to support in a method.  For sure the JIT must get together GC information to cover roots accurately at all call-sites.  But the JIT commonly isn’t expected to keep GC info for every instruction.  If any instruction might fault, and if any such fault could be taken up, so the JIT would call for GC info for all instructions in all methods.  This would be expensive.  Of course, ‘mov eax, ecx’ cannot fault due to memory access issues.  But a surprising number of instructions are open to fault if you view all of memory – including the stack – to be unmapped.  And even ‘mov eax, ecx’ can fault due to a Thread.Abort.

 

If you were making up attention to that last bullet, you might be questioning how asynchronous exceptions could head off GC corruption yet without resumption.  After all, the handled filter will however execute and we cognize that the JIT doesn’t have concluded GC information for the faulting instruction.

 

Our current solution to this on X86 is rather anno Domini hoc, but it does work out.  First, we tighten the JIT to ne’er flux the contents of the scratch registers between a ‘try’ clause and any of the exception clauses (‘filter’, ‘in the end’, ‘fault’ and ‘catch’).  The scratch registers in this case are EAX, ECX, EDX and sometimes EBP.  Our JIT compiler makes up one’s mind, method-by-method, whether to utilise EBP as a stack-frame register or a scratch register.  Of course, EBP isn’t in truth a scratch register since callees will conserve it for us, but you can get word where I’m plumping.

 

Nowadays when an asynchronous exception comes, we can toss the state of all the scratch registers.  In the case of EAX, ECX & EDX, we can flatly zero them in the register context that is flowed via exception propagation.  In the case of EBP, we but zero it if we aren’t applying EBP as a frame register.  When we execute a cared handler, we can nowadays report GC roots based on the GC information that’s associated with the handler’s instruction pointer.

 

The downside to this approach, other than its ad hoc nature, is that it tightens the codegen of any method that comprises exception handlers.  At some point we may have to mold asynchronous exceptions more accurately, or exposit the GC information spewed by the JIT compiler, or a combination, thus that we can enable best code generation in the presence of exceptions.

 

We’ve already discovered how VB.NET can employ a filter and denotative logic flow from a catch clause to make the illusion of restartable exceptions to hold up ‘On Error Take up Next’.  But this should not be confused with dependable restartability.

 

Before we provide the topic of rethrow, we should in short see the InnerException property of System.Exception.  This lets one exception to be enfolded up in the state of another exception.  A couple of of import places where we read advantage of this are reflection and class construction.

 

When you do deep-bandaged invocation via reflection (e.g. Type.InvokeMember or MethodInfo.Appeal), exceptions can occur in two places:

 

1)      The reflection infrastructure may make up one’s mind that it cannot fulfill your request, maybe because you led the incorrect number of arguments, or the member lookup neglected, or you are invoking on someone else’s secret members.  That last one sounds mistily muddy.

 

2)      The former-bandaged invocation might work out dead, but the target method you rang may switch an exception backward at you.  Reflection must dependably commit you that exception as the result of the call.  Turning back it as an outward-bound argument, kind of than fliping it at you, would be grave.  We would miss one of the rattling properties of exceptions, which is that they are difficult to neglect.  Error codes are invariably being got down or other than disregarded, leading to slight execution.

 

The problem is that these two sources of exceptions are equivocal.  There must be some way to state whether the invocation attempt neglected or whether the target of the invocation neglected.   Reflection disambiguates these cases by utilizing an instance of System.Reflection.TargetInvocationException for the case where the appealed method fliped an exception.  The InnerException property of this instance is the exception that was thrown by the appealed method.  If you catch any exceptions from a previous-bandaged invocation other than TargetInvocationException, those other exceptions argue problems with the previous-bandaged dispatch attempt itself.

 

Something like happens with TypeInitializationException.  If a class constructor (.cctor) method neglects, we catch that exception as the InnerException of a TypeInitializationException.  Subsequent attempts to utilize that class in this AppDomain from this or other threads will have that same TypeInitializationException instance thrown at them.

 

So what’s the difference between the tracing three constructs, where the surcharged constructor for MyExcep is puting its argument into InnerException:

 

try {…} get (Exception e) { if (expr) rethrow; …}

try {…} get (Exception e) { if (expr) switch fresh MyExcep(); …}

try {…} get (Exception e) { if (expr) flip novel MyExcep(e); …}

 

Well, the 2nd form is missing information.  The original exception has been missed.  It’s difficult to urge that approach.

 

Between the 1st and 3rd forms, I guess it bets on whether the intermediary can add up of import information by enwraping the original exception in a MyExcep instance.  Still if you are toting up value with MyExcep, it’s notwithstanding of import to conserve the original exception information in the InnerException so that advanced programs and developers can see the terminated cause of the error.

 

In all likelihood the magnanimousest impact from ceasing the first pass of exception handling too soon, as with the examples higher up, is on debugging.  Have you always sequestered a debugger to a process that has failed with an unhandled exception?  When everything plumps utterly, the debugger pops up up sitting in the context of the RaiseException or trap condition.

 

That’s so much better than sequestering the debugger and finishing up on a ‘rethrow’ statement.  What you in truth handle well-nigh is the state of the process when the initial exception was switched.  But the first pass has stoped and the original state of the world may have been missed.  It’s unmortgaged why this befalls, based on the two pass nature of exception handling.

 

In reality, the determination of whether or not the original state of the world has been missed or just befoged is rather elusive.  Sure as shooting the current instruction pointer is sitting in the rethrow kinda than on the original fault.  But recall how filter and at last clauses are executed with an EBP that assigns the comprising method’s locals in scope… and an ESP that nevertheless incorporates the original faulting method?  It turns out that the geting handler has some discretion on whether to pop up ESP before runing the catch clause or alternatively to hold up the pop until the catch clause is terminated.  The handled handler presently pops up the stack before phoning the catch clause, so the original state of the exception is truly missed.  I trust the unmanaged C++ handler holds up the pop until the catch fills out, therefore retrieving the state of the world for the original exception is slippery but potential.

 

Irrespective, every time you get and rethrow, you impose this virulent disappointment on everyone who debugs through your code.  Regrettably, there are a number of places in cared code where this disappointment is inescapable.

 

The most inauspicious place is at AppDomain boundaries.  I’ve already explained at http://blogs.gotdotnet.com/cbrumme/PermaLink.aspx/56dd7611-a199-4a1f-adae-6fac4019f11b why the Isolation requirement of AppDomains pressures us to summon most exceptions across the boundary.  And we’ve only talked over how reflection and class construction stop the first pass by enwraping exceptions as the InnerException of an outer exception.

 

One alternative is to trap on all first-chance exceptions.  That’s because debuggers can have first crack at exceptions before the vectored exception handler still gets wind the fault.  This surely devotes you the ability to debug each exception in the context in which it was fliped.  But you are probable to pick up a lot of exceptions in the debugger this way!

 

In fact, throughout V1 of the runtime, the ASP.NET team operated all their stress suites with a debugger confiscated and configured to trap on first-chance Access Violations (“sxe av”).  Ordinarily an AV in cared code is converted to a NullReferenceException and so dealt like any other dealt exception.  But ASP.NET’s settings made stress to trap in the debugger for any such AV.  So their team implemented a rule that all their suites (including all dependencies throughout FX) must forefend such faults.

 

It’s an approach that worked for them, but it’s difficult to get word it puzzling out more broadly speaking.

 

Alternatively, over time we involve to total novel hooks to our debuggers so they can ensnare on but the exceptions you deal most.  This might need ensnaring exceptions that are bunking your code or are being propagated into your code (for some definition of ‘your code’).  Or it might require snaring exceptions that scarper an AppDomain or that are propagated into an AppDomain.

 

The above text has distinguished a pretty terminated managed exception model.  But there’s one feature that’s prominently scatty.  There’s no way for an API to document the effectual set of exceptions that can shake off it.  Some languages, like C++, hold this feature.  Other languages, like Java, mandate it.  Of course, you could confiscate Custom-made Attributes to your methods to argue the foreboded exceptions, but the CLR would not apply this.  It would be an opt-in discipline that would be of in question value without spheric buy-in and warranted enforcement.

 

This is another of those spiritual language debates.  I don’t need to retrograde all the reasons for and against documenting switched exceptions.  I in person put on’t trust the discipline is deserving it, but I assume’t require to exchange the minds of any proponents.  It doesn’t matter.

 

What does matter is that disciplines like this must be used universally to have any value.  So we either demand to prescribe that everyone trace the discipline or we must therefore step down it that it is slimy still for proponents of it.  And since one of our goals is high-pitched productivity, we aren’t plumping to visit a discipline on people who put on’t believe in it – specially when that discipline is of moot value.  (It is arguable in the genuine sense, since there are many people on both sides of the argument).

 

To me, this is sort of like ‘const’ in C++.  People much expect why we haven’t bought into this notion and employed it broadly speaking throughout the dealt programming model and frameworks.  Once once again, ‘const’ is a spiritual issue.  Some developers are trigger-happy proponents of it and others discover that the small benefit doesn’t warrant the tremendous burden.  And, one time again, it must be utilised generally to have value.

 

Now in C++ it’s potential to ‘const-ify’ the low-pitched level runtime library and services, and and then permit client code to opt-in or not.  And when the client code runs into places where it must miss ‘const’ in order to telephone some non-const-ified code, it can merely transfer ‘const’ via a dingy cast.  We have all made this trick, and it is one reason that I’m not especially in favor of ‘const’ either.

 

But in a handled world, ‘const’ would simply have value if it were enforced by the CLR.  That thinks the verifier would keep you from missing ‘const’ unless you explicitly broke in type safety and were trusted by the security system to do thence.  Until more than 80% of developers are clamoring for an applyed ‘const’ model throughout the dealt environment, you aren’t plumping to hear us totaled it.

 

 

Foray into C++ Exceptions

 

C++ discovers its ain exception model, which is distinguishable from the __try / __except / __at long last exposure of SEH.  This is done through auto-destruction of stack-apportioned objects and through the ‘try’ and ‘get’ keywords.  Observe that there are no double-underbars and there is no support for filters other than through matching of exception types.  Of course, under the covers it’s even so SEH.  So there’s however an FS:[0] handler (on X86).  But the C++ compiler optimizes this by simply giving off a single SEH handler per method no matter of how many try/catch/ultimately clauses you utilise.  The compiler gives off a table to indicate to a mutual service in the C-runtime library where the assorted try, get and in the end clauses can be found in the method body.

 

Of course, one of the largest differences between SEH and the C++ exception model is that C++ permits you to flip and get objects of types defined in your application.  SEH only lets you switch 32-bit exception codes.  You can utilize _set_se_translator to map SEH codes into the appropriate C++ classes in your application.

 

A magnanimous part of the C++ exception model is inexplicit.  Kind of than use denotative try / in the end / catch clauses, this language furthers use of auto-destructed local variables.  Whether the method unwinds via a non-special return statement or an exception being fliped, that local object will auto-destruct.

 

This is fundamentally a ‘ultimately’ clause that’s been enfolded up in a more utile language build.  Auto-destruction occurs during the second pass of SEH, as you would ask.

 

Have you acknowledged that the C++ exception you switch is much a stack-apportioned local?  And that if you explicitly get it, this catch is as well with a stack-apportioned object?  Did you e’er awaken up at night in a inhuman sweat, questioning whether a C++ in-flight exception resides on a piece of stack that’s already been poped up?  Of course not.

 

In fact, we’ve today got a line enough of SEH to infer how the exception e’er remains in a section of the stack above ESP (i.e. within the bounds of the stack).  Prior to the throw, the exception is stack-allocated within the dynamic frame.  During the first pass of SEH, nothing catchs poped up.  When the filters run, they are advertised deeper on the stack than the fliping frame.

 

When a frame holds it will get the exception, the second pass commences.  Still here, the stack doesn’t unwind.  And then, before readjusting the stack pointer, the C++ handler can re-create-build the original exception from the piece of stack that will be popped into the activation frame that will be exposed.

 

If you are an expert in unmanaged C++ exceptions, you will belike be worryed to learn of the differences between cared C++ exceptions and unmanaged C++ exceptions.  There’s a well write-up of these differences at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vcmex/html/vccondifferencesinexceptionhandlingbehaviorundermanagedexceptionsforc.asp .

 

 

A Single Dealt Handler

 

We’ve already learnt how the C++ compiler can give off one SEH handler per method and recycle it for all the exception blocks in that method.  The handler can do this by confering a side table that argues how the assorted clauses map to instruction sequences within that method.

 

In the handled environment, we can read this even further.  We hold a boundary between handled and unmanaged code for many reasons, like synchronization with the garbage collector, to enable stack crawling through handled code, and to mobilize arguments decently.  We have altered this boundary to put up a single SEH handler at every unmanaged -> cared call up in.  For the most part, we must do this without compiler support since many of our transitions come through dynamically brought forth machine code.

 

The cost of altering the SEH chain during calls into dealt code is quick amortised as we call up freely between cared methods.  So the quick cost of advertising FS:[0] handlers on method entry is paltry for cared code.  But there is nonetheless an impact on the quality of the brought forth code.  We saw part of this impact in the discussion of register usage across exception clauses to continue GC-safe.

 

Of course, the largest cost of exceptions is when you in reality flip one.  I’ll return to this near the end of the blog.

 

 

Flow Control

Here’s an interesting scenario that did up lately.

 

Let’s suppose we labor the first pass of exception propagation all the way to the end of the handler chain and we progress to the unhandled exception backstop.  That backstop will in all likelihood crop up a dialog in the first pass, supposing that the application has put up an unhandled exception.  Depending on how the system is configured, the dialog may permit us to finish the process or debug it.  Let’s suppose we select End.

 

Today the 2nd pass leads off.  During the 2nd go, all our in the end clauses can run.

 

What if one of those 2nd pass ‘ultimately’ clauses switchs a fresh exception?  We’re plumping to commence a fresh exception propagation from this location – with a novel Exception instance.  When we push this fresh Exception up the chain, we may in reality discover a handler that will get down the second exception.

 

If this is the case, the process got ahead’t end referable to that first exception.  This is despite the fact that SEH stated the user we had an unhandled exception, and the user stated us to end the process.

 

This is surprising, to suppose the least.  And this behavior is potential, disregardless of whether dealt or unmanaged exceptions are asked.  The mechanism for SEH is easily-delineated and the exception model operates within those rules.  An application should ward off sure (ab)uses of this mechanism, to ward off confusion.

 

So, we have forbidden some of those confutative uses in handled code.

 

In unmanaged, you should ne’er return from a in the end.  In an particular execution of a at last, a return has the effect of ending the exception processing.  The catch handler ne’er learns its 2nd go and the exception is efficaciously got down.  Conversely, in a non-special execution of a at long last, a return has the effect of puting back the method’s return value with the return value from the ultimately.  This is probable to do developer confusion.

 

Thence in cared code we’ve progressed to it unsufferable for you to return from a ultimately clause.  The total rules for flow control taking cared exception clauses should be found at Section 12.4.2.8 of ECMA Partition I (http://msdn.microsoft.com/net/ecma/ ).

 

All the same, it is potential to throw from a dealt at long last clause.  (In universal, it’s very difficult to confidently key regions of dealt code where exceptions cannot be switched).  And this can have the effect of puting back the exception that was in flight with a fresh 1st and 2nd extend sweep, as keyed out higher up.  This is the ExceptionCollidedUnwind situation that is mentioned in the EXCEPTION_DISPOSITION enumeration.

 

The C++ language reads a unlike approach to exceptions thrown from the 2nd pass.  We’ve already discovered that C++ autodestructors execute during the 2nd pass of exception handling.  If you’ve e’er switched an exception from the destructor, when that destructor is run as part of an exception unwind, and so you have already discovered a sore lesson.  The C++ behavior for this situation is to end the process via a termination handler.

 

In unmanaged C++, this intends that developers must trace outstanding discipline in the implementation of their destructors.  Since finally those destructors might run in the context of exception backout, those destructors should ne’er let an exception to run away them.  That’s sore, but presumptively manageable.

 

In cared C++, I’ve already observed that it’s very difficult to distinguish regions where exceptions cannot come.  The ability to keep (asynchronous and resource) exceptions over trammeled ranges of code is something we would wish to enable at some point in the future, but it merely isn’t hardheaded in V1 and V1.1.  It’s way too comfortable for an out-of-memory or type-load or class-initialization or thread-abort or appdomain-unload or like exception to intrude.

 

Ultimately, it’s potential for exceptions to be thrown during execution of a filter.  When this happens in an OS SEH context, it results in the ExceptionNestedException situation that is mentioned in the EXCEPTION_DISPOSITION enumeration.  The handled exception model read a unlike approach hither.  We’ve already learnt that an MSVC filter clause has three sound returns values (take up execution, proceed search, and run handler).  If a handled filter switchs an exception, we incorporate that exception and view the filter to have answered “No more, I assume’t need to deal this one.  Proceed searching for a handler”.

 

This is a sensible interpretation in all cases, but it comes down out particularly easily for stack overflow.  With the historic OS support for stack overflow, it’s very difficult to dependably run backout code.  As I’ve mentioned in other blogs, you may but have one 4K page of stack usable for this purpose.  If you bollocks up that page, the process is ended.  It’s very difficult to run handled filters dependably within such a trammeled region.  So a sensible approach is to regard the filters to have themselves fliped a StackOverflowException and for us to render this as “No more, I put on’t require to care this one.”

 

In a next version, we would care to allow for a more defendable and utile mechanism for caring stack overflow from dealt code.

 

 

Error Handling without Exceptions

 

So we’ve learnt how SEH and C++ and handled exceptions all interoperate.  But not all error handling is based on exceptions.  When we reckon Windows, there are two other error handling systems that the CLR can interoperate with.  These are the Get/SetLastError mechanism used by the OS and the HRESULT / IErrorInfo mechanism used by COM.

 

Let’s consider the GetLastError mechanism firstly, because it’s relatively uncomplicated.  A number of OS APIs argue failure by regressing a sentinel value.  Ordinarily this sentinel value is -1 or 0 or 1, but the details vary depending on the API.  This sentinel value argues that the client can phone GetLastError() to regain a more detailed OS status code.  Regrettably, it’s sometimes difficult to cognize which APIs participate in the GetLastError protocol.  Theoretically this information is ever documented in MSDN and is coherent from one version of the OS to the next – including between the NT and Win95-founded OSes.

 

The substantial issue comes when you PInvoke to one of these methods.  The OS API latches any failure codes with SetLastError.  Today on the return path of the PInvoke, we may be ringing assorted OS services and dealt services to summon the outward-bound arguments.  We may be synchronizing with a pending GC, which could need a drawing a blank operation like WaitForSingleObject.  Someplace in hither, we may call up another OS API that itself latches an error code (or the absence of an error code) through its ain call to SetLastError.

 

Thence by the time we return to some cared code that can bring forth up a novel PInvoke stub to telephone GetLastError, you can be certain that the original error code is long plumped.  The solution is to chase after your PInvoke declaration to argue that it should participate in the GetLastError protocol.  This states the PInvoke phone to catch the error as part of the return path, before any other OS calls on this thread have an opportunity to wipe off it or put back it.

 

This protocol licks easily for PInvokes.  Regrettably, we do not have a way to tag IJW VTFixup stubs in the same way.  Hence when you get to cared -> unmanaged calls via MC++ IJW, there isn’t a commodious and dependable way to find a detailed OS status code on the return path.  Evidently this is something we would care to address in some succeeding version, though without blindly bringing down the cost of a GetLastError on all dealt -> unmanaged transitions through IJW.

 

 

COM Error Handling

 

To infer how the CLR interoperates with COM HRESULTs, we must firstly look back how PreserveSig is utilized to change the behavior of PInvoke and COM Interop.

 

Ordinarily, COM signatures regress an HRESULT error code.  If the method asks to pass some other result, this is typically expressed with an [out, retval] outward argument.  Of course, there are exceptions to this pattern.  For example, IUnknown::AddRef and Release both regress a count of the prominent references, sort of than an HRESULT.  More significantly, HRESULTs can be utilised to pass success codes as easily as error codes.  The two most distinctive success codes are S_OK and S_FALSE, though any HRESULT with the high-pitched bit reset is viewed a success code.

 

COM Interop unremarkably transmutes the unmanaged signature to make a dealt signature where the [out, retval] argument gos the cared return value.  If there is no [out, retval], and so the return type of the cared method is ‘void’.  And so the COM Interop layer maps between failure HRESULTs and dealt exceptions.  Here’s a unproblematic example:

 

COM:  HRESULT GetValue([out, retval] IUnknown **ppRet)

CLR:  IUnknown GetValue()

 

Still, the return value might be a DWORD-sized integer that should not be translated as an HRESULT.  Or it might be an HRESULT – but one which must sometimes distinguish between unlike success codes.  In these cases, PreserveSig can be specified on the signature and it will be preserved on the cared side as the traditional COM signature.

 

Of course, the same can happen with PInvoke signatures.  Usually a DLL export like Ole32.dll’s CoGetMalloc would have its signature reliably conserved.  Presumptively the transformation would be something like this:

 

DLL:  HRESULT CoGetMalloc(DWORD c, [out, retval] IMalloc **ppRet)

CLR:  DWORD   CoGetMalloc(DWORD c, ref IMalloc ppRet)

 

If OLE32 turns back some sort of failure HRESULT from this call, it will be returned to the handled caller.  If alternatively the application would opt to catch this error case mechanically converted to a cared Exception, it can utilise PreserveSig to argue this.

 

Huh?  In the COM case PreserveSig means “commit me the unconverted HRESULT signature”, but in the PInvoke case PreserveSig means “win over my HRESULTs into exceptions.”  Why would we utilise the same flag to argue just paired semantics for these two interop layers?  The reasons are, ahem, historic.  The betterest way to think of PreserveSig is “devote me the strange transformation of my signature, as opposed to what is distinctive for the kind of interop I am making.”

 

So nowadays we cognise how to get mappings between HRESULTs and cared exceptions for the typical COM Interop case (no PreserveSig) and the untypical PInvoke case (PreserveSig).  But what are the details of that mapping?

 

The exception subsystem in the CLR has mappings between COM errors, OS errors, and cared exception types.

 

Of course, sometimes we have a situation which doesn’t have a exact mapping.  In the case of an HRESULT that isn’t associated with a specific managed Exception class, we win over it to an instance of COMException.  In the case of an OS status code that isn’t associated with a specific managed Exception class, we convince it to an instance of SEHException.

 

Yet for cases where we have a correspondence between a dealt and unmanaged representation, the mapping gained ground’t of necessity roundtrip.  For instance, an AV in unmanaged code results in an SEH exception of code 0xC0000005.  If this is driven through handled code, it will be mapped to the like NullReferenceException class.  If the propagation of this exception continues through cared code and further up the stack to an unmanaged SEH handler, the unmanaged code will get word the original exception code of 0xC0000005.  Thence, when propagating through that sequence of handlers, we find out a gross roundtrip.

 

But let’s exchange the scenario somewhat, therefore that the original AccessViolation occurs in cared code.  Nowadays we have a NullReferenceException that is being broadcast out to an unmanaged SEH handler further backward on the stack.  But this time the NullReferenceException will be mapped to an SEH exception code of 0xE0434F4D.  This is the dealt exception code used for all cared exceptions.

 

Have you e’er questioned where these exception codes come from?  Well 0xE0434F4D is 0xE0+“COM”.  In the first place the CLR was rung COM+ 2.0.  When we interchanged the project name, we failed to interchange the exception code.  The unmanaged C++ exceptions utilise 0xE06D7363, which is 0xE0+“msc”.  You might too get a line 0xE0524F54 for 0xE0+“ROT” on Rotor builds up.

 

The current mapping between OS status codes and dealt exception types is quite restricted.  It incorporates received transformations like:

 

STATUS_FLOAT_INEXACT_RESULT

STATUS_FLOAT_INVALID_OPERATION

STATUS_FLOAT_STACK_CHECK

STATUS_FLOAT_UNDERFLOW                => ArithmeticException

 

STATUS_FLOAT_OVERFLOW

STATUS_INTEGER_OVERFLOW               => OverflowException

 

STATUS_FLOAT_DIVIDE_BY_ZERO

STATUS_INTEGER_DIVIDE_BY_ZERO      => DivideByZeroException

 

STATUS_FLOAT_DENORMAL_OPERAND  => FormatException

 

STATUS_ACCESS_VIOLATION                 => NullReferenceException

 

STATUS_ARRAY_BOUNDS_EXCEEDED     => IndexOutOfRangeException

 

STATUS_NO_MEMORY                           => OutOfMemoryException

 

STATUS_STACK_OVERFLOW                  => StackOverflowException

 

The HRESULT mappings are far more blanket.  They admit received mappings to the well-cognized HRESULT values like:

 

E_POINTER                                           => ArgumentNullException

 

And they admit mappings to CLR-delineated HRESULTs in the 0×8013???? range that you’ve doubtlessly witnessed during your development and debugging.  The dealt platform has its ain facility code for holding a range of HRESULTs for our sole use.

 

COR_E_ENTRYPOINTNOTFOUND            => EntryPointNotFoundException

 

And our mappings let in a gathering of like HRESULTs to a single cared exception.  Here’s a particularly broad gathering of 26 dissimilar HRESULTs to the FileLoadException class:

 

FUSION_E_REF_DEF_MISMATCH

FUSION_E_INVALID_PRIVATE_ASM_LOCATION

COR_E_ASSEMBLYEXPECTED

FUSION_E_SIGNATURE_CHECK_FAILED

FUSION_E_ASM_MODULE_MISSING

FUSION_E_INVALID_NAME

FUSION_E_PRIVATE_ASM_DISALLOWED

COR_E_MODULE_HASH_CHECK_FAILED

COR_E_FILELOAD

SECURITY_E_INCOMPATIBLE_SHARE

SECURITY_E_INCOMPATIBLE_EVIDENCE

SECURITY_E_UNVERIFIABLE

COR_E_FIXUPSINEXE

HRESULT_FROM_WIN32(ERROR_TOO_MANY_OPEN_FILES)

HRESULT_FROM_WIN32(ERROR_SHARING_VIOLATION)

HRESULT_FROM_WIN32(ERROR_LOCK_VIOLATION)

HRESULT_FROM_WIN32(ERROR_OPEN_FAILED)

HRESULT_FROM_WIN32(ERROR_DISK_CORRUPT)

HRESULT_FROM_WIN32(ERROR_UNRECOGNIZED_VOLUME)

HRESULT_FROM_WIN32(ERROR_FILE_INVALID)

HRESULT_FROM_WIN32(ERROR_DLL_INIT_FAILED)

HRESULT_FROM_WIN32(ERROR_FILE_CORRUPT)

FUSION_E_CODE_DOWNLOAD_DISABLED

CORSEC_E_MISSING_STRONGNAME

INIT_E_DOWNLOAD_FAILURE

MSEE_E_ASSEMBLYLOADINPROGRESS    => FileLoadException

 

There are some more observations we can make about the COM error handling approach.  First, it should be obvious that the 32-bits of an HRESULT cannot unambiguously delineate an arbitrary set of user-extensile error conditions.  COM deals with this, in part, by including the interface that turns back an HRESULT in the decision of how to render these 32-bits.  This thinks that 0xE3021051 returned from IMyInterface is not the same error code as 0xE3201051 returned from IYourInterface.  Regrettably, it likewise thinks that each interface must be strict about the bit patterns it regresss.  Specifically, it would be very spoilt if the implementation of IMyInterface::m() befalls to delegate to IYourInterface::n() and blindly revert ‘n’s HRESULTs.  Any HRESULTs returned from ‘n’ must in some manner be mapped to the bit patterns that are effectual to return from IMyInterface::m().  If ‘n’ retroverts a bit pattern that IMyInterface::m() cannot map, and then ‘m’ is obliged to convince the HRESULT to E_UNEXPECTED and return that.

 

In other words, the uniqueness constraint for HRESULTs forces a afflictive discipline on all COM implementations that retrovert HRESULTs.  And part of this discipline is to miss error information by maping out meaningful HRESULTs into E_UNEXPECTED if the context for translating those HRESULTs is being missed.  (There is a well-outlined set of system HRESULTs which are implicitly returnable from any interface.  The bit pattern for E_UNEXPECTED is needs part of this set.  The CLR facility code lets us to sleep in this inner world with our ain codes).

 

The fact that most COM developers are incognizant of this sore discipline and put on’t trace it, merely adds to the level of pain hither.

 

As luck would have it, COM supplements the restrained expressibility and uniqueness of HRESULTs by applying a second mechanism: IErrorInfo.  And the COM Interop layer applies this subsidiary mechanism when mapping to and from handled exception objects.  In fact, System.Exception implements the IErrorInfo interface.  When a cared exception is thrown to a COM client, the IErrorInfo of the Exception instance is useable for the COM client to query.

 

Adam Nathan’s first-class book “.NET and COM – The Ended Interoperability Guide” keies out how the IErrorInfo state is occupyed in from a handled exception in Chapter 16.

 

There’s one more detail of COM Interop HRESULT mapping that warrants discussion.  It’s well practice for all COM methods to revert an HRESULT.  But there are several noted violations of this rule, including IUnknown::AddRef and Release.  More significantly, every developer can pick out whether to trace this betterest practice.  Some select not to.  And there are some distinctive cases, like event gos down, where we much hear methods turning back ‘void’ or ‘bool’.

 

This stages the COM Interop error maping out layer with a problem.  If an exception occurs inside a dealt implementation of a method with one of these signatures, it’s difficult to bring the error information backward to the COM caller.  There are several choices useable to that layer – none of them well:

 

1)      Let the cared exception to move around backward through the COM caller, utilizing the underlying SEH mechanism.  This would lick dead, but is strictly illegal.  Well-did COM servers do not distribute exceptions out to their COM clients.

 

2)      Get down the cared exception.  Diffuse a return value with ‘0’ out to the COM client.  This 0 value might catch rendered as a turned back Boolean, integer, pUnk or other data type.  In the case of a ‘void’ signature, it will only be disregarded.

 

3)      Win over the exception object into an HRESULT value.  Propagate that HRESULT out as the return value to the COM client.  In the ‘void’ case, this will once again be neglected.  In the pUnk case, it will in all likelihood be dereferenced and afterwards do an AccessViolation.  (Failure HRESULTs have the high-pitched bit set.  On Win32 the high-pitched 2 GB of address space are reserved for the kernel and are unavailable unless you operate a /LARGEADDRESSAWARE process on a suitably booted system.  On Win64, the low-pitched couple of GB of address are booked and unavailable to discover this sort of mistake).

 

As you can get wind, all of these solutions are broken in.  Regrettably, the most broken of the three is the last one… and that’s the one we presently trace.  I surmise we will interchange our behavior hither at some point.  Until so, we depend upon the fact that AddRef & Release are especially managed and that the other cases are uncommon and are typically ‘void’ or ‘bool’ reverts.

 

 

Performance and Trends

 

Exceptions vs. error codes has ever been a controversial topic.  For the last 15 years, every team has contended whether their codebase should flip exceptions or return error codes.  Hopefully nobody debates whether their team should integrate both styles.  That’s ne’er worthy, though it much reads major surgery to migrate to a logical plan.

 

With any spiritual controversy, there are many arguments on either side.  Some of them are related to:

 

  • A philosophy of what errors intend and whether they should be stated out-of-band with the method contract.
  • Performance.  Exceptions have a unmediated cost when you actually switch and get an exception.  They may besides have an collateral cost associated with advertising handlers on method entry.  And they can a great deal have an subtle cost by limiting codegen opportunities.
  • It’s relatively well-fixed to block to check for a retroverted error code.  It’s much harder to unknowingly get down an exception without dealing it (though we even so discover developers making therefore!)
  • Exceptions be given to catch far more information about the cause and location of an error, though one could visualize an error code system that’s equally sinewy.  (IErrorInfo anybody?)

 

So what’s the proper answer hither?

 

Well if you are working up the kernel of an runing system, you should likely apply error codes.  You are a programming God who seldom gets to mistakes, so it’s less probable that you will blank out to determine your return codes.  And there are good bootstrapping and performance reasons for deflecting exceptions within the kernel.  In fact, some of the OS folks hither imagine that SEH should be reserved for frightening “read down the process” situations.  That may have been the original design point.  But SEH is such a elastic system, and it is so dug in as the basis for unmanaged C++ exceptions and cared exceptions, that it is no longer sensible to limit the mechanism to these decisive failures.

 

Hence, if you are not a programming God like those OS developers, you should view employing exceptions for your application errors.  They are more muscular, more expressive, and less prostrate to pervert than error codes.  They are one of the profound ways that we progress to dealt programming more generative and less error prostrate.  In fact, the CLR internally utilizes exceptions yet in the unmanaged portions of the engine.  Nonetheless, there is a good foresightful term performance problem with exceptions and this must be factored into your decision.

 

View some of the things that bechance when you flip an exception:

 

  • Take hold of a stack trace by rendering metadata emitted by the compiler to guide on our stack unwind.
  • Wipe out a chain of handlers up the stack, phoning each handler double. 
  • Compensate for mismatches between SEH, C++ and handled exceptions.
  • Apportion a handled Exception instance and operate its constructor.  Most probable, this needs calculating up resources for the assorted error messages.
  • Belike read a trip through the OS kernel.  A great deal read a hardware exception.
  • Apprize any seized debuggers, profilers, vectored exception handlers and other concerned parties.

 

This is unaccented years off from turning back a -1 from your function call.  Exceptions are inherently non-local, and if there’s an obvious and lasting trend for today’s architectures, it’s that you must stay on local for well performance.

 

Proportional to true-line local execution, exception performance will retain catching spoilt.  Surely, we might probe our current behavior and race it up a slight.  But the trend will unrelentingly get to exceptions do spoiled.

 

How do I accommodate the trend to spoiled performance with our recommendation that dealt code should utilise exceptions to pass errors?  By controling that error cases are exceedingly uncommon.  We utilized to suppose that exceptions should be used for especial cases, but folks advertised backward on that as tautologic.

 

If your API fails in 10% of all calls, you better not employ an exception.  Alternatively, exchange the API hence that it puts across its success or failure as part of the API (e.g. ‘bool TryParse(String s)’).  Still if the API neglects 1% of calls, this may be too high-pitched a rate for a service that’s to a great extent used in a server.  If 1% of calls neglect and we’re processing 1000 requests per second with 100 of these API calls per request, and then we are fliping 1000 times a second.  That’s a very perturbing rate of exceptions.  On the other hand, a 1% failure rate may be quite fair to middling in a client scenario, if the exception comes when a human user campaigns the incorrect button.

 

Sometimes you pulled ahead’t cognise whether your API will be used in a client or a server.  And it may be difficult for you to call failure rates when errors are triggered by spoiled data from the client.  If you’ve left a way for the client to find out his data without triggering an exception (like the TryParse() example in a higher place) and so you’ve made your part.

 

As usual, there’s so much more to suppose.  I even so haven’t spilt most unhandled exceptions.  Or about undeniable exception propagation (Thread.Abort).  Or how undeniable propagation interacts with propagation through unmanaged code via PInvoke, IJW or COM Interop.  And I cautiously obviated excusing why we didn’t trace our ain rules when delineating and utilizing the Exception class hierarchy.  And there’s plenty to say about our especial treatment of OutOfMemoryException and StackOverflowException.

 

If you are notwithstanding taking and in reality require to cognize more, peradventure you should but apply for a job on the CLR team.


A speedy update on me.

Posted in Technology | Comments Off

Create a free edublog to get your own comment avatar (and more!)

Comments are closed.