ReliabilityContract and IComparer (or other injected code)

452 views Asked by At

In writing some code to mimic an as-complete-as-possible emulation of System.Array, I have come across something that I find confusing and dangerous.

With the following method signature:

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public static int BinarySearch<T>(T[] array, T value, IComparer<T> comparer)

A method is declared with a contract saying that it will definitely not corrupt any state, even itself, if there is a failure. It also implies that some code external to the contract will be called via the comparer value, and IComparer<T>.Compare does not require a reliability contract; a call to comparer.Compare(x,y) can certainly corrupt state and violate the contract.

How is this valid? I'm not an expert on the uses of constrained execution... will a constrained execution environment inspect the Compare method at runtime, and cause an exception if the contract is expected to be enforced? Is this a hole in the reliability contract?

Or, are constrained execution regions CIL voodoo that I should avoid in non-framework code? I want to keep as close as possible behavior to native System.Array, and so I would like to maintain the same ReliabilityContract attributes where the underlying implementation supports it; but it seems to me that even the internals can't guarantee the contract, so I am quite puzzled how I could guarantee the contract.

1

There are 1 answers

0
svick On BEST ANSWER

AFAIK, CERs are a very advanced feature and are used very rarely. Unless you really want your class to be usable in a CER (or if you're not sure what CERs actually are), you shouldn't specify any ReliabilityContract.

If you still want to investigate CERs, read Stephen Toub's Keep Your Code Running with the Reliability Features of the .NET Framework. I think the most relevant part of that article is (emphasis mine):

Reliability contracts also have some interesting interpretation issues. For example, one of the problems the CLR designers ran into is that most well-implemented methods check their parameters and throw exceptions for input that is not valid. Thus, an approach of simply outlawing all allocations from within CERs would have been next to useless (some methods may also allocate and then recover from any out of memory exceptions). Reliability contracts are an attempt to abstract away that type of detail from the runtime, allowing the code author to clearly state whether callers need to worry about failures, and if so, how much state may need to be thrown away. As a consequence, reliability contracts are considered null and void if the arguments passed to the method are illegal or the method is used incorrectly. Additionally, there may be concepts that aren't easily expressed through the contracts. Imagine a method that accepts an Object as a parameter and calls its Equals method. This method may be reliable, but only if you pass in an object that has a reliable Equals override. There is currently no built-in way to express that concept.

I think the highlighted section means that BinarySearch shouldn't have been marked as WillNotCorruptState. Or it's possible that the case with a delegate is more transparent than calling Equals() of a passed in object, so the attribute is actually okay here.

In any case, I think that to be on the safe side, you shouldn't specify any ReliabilityContracts, unless you really know what you're doing and you really need it.