Static members of CLR types hold some specific issues for Yukon resident CLR code due to the way in which the AppDomain concept has been mapped into this environment.
Let's take a quick refresher as to what AppDomains are and how they are used within the CLR. All code that executes under the control of the CLR does so within an AppDomain. The AppDomain is analogous to a process in that it acts as a unit of isolation for code running within it. Code in different AppDomains have to perform a significant amount of work to talk to each other (they must use the .NET Remoting infrastructure). If code in one AppDomain encounters a serious problem (such as an unhandled exception), only that AppDomain is affected. Code running in other AppDomains continues execution without impact.
The result of this isolation is that:
-
Assemblies are loaded on a per-AppDomain basis unless they are specifically loaded domain neutral - which means there is a separate JITed copy of code for each AppDomain.
-
Static members of a type are specific to an AppDomain.
As of the current beta, the decision has been made to have a single AppDomain per database. This means that the CLRs basis of isolation cannot protect two pieces of code executing within a single database from each other - even if they are running in separate transactions. In other words, it is possible for code running in one transaction to make changes to static state that can been seen from another transaction even before the first has committed.
This breaks one of the golden rules of transactions - that of isolation. Therefore, code running from the SAFE or EXTERNAL_ACCESS CAS buckets cannot have non read-only static fields. Code running from the UNSAFE bucket does not have this restriction.
So does this resolve the problem? Unfortunately, it only resolves the problem partially. Ignoring code running from the UNSAFE bucket (it's inherently allowed to do potentially dangerous things), what is wrong with allowing read-only static fields? The problem stems from there being two distinct kinds of types within the CLR type system: value types and reference types. For a value type, a field is actually the data itself - the memory is allocated inline where the field is declared - this means a read-only value type is constant after construction. However, a reference type field is simply a reference to a block of memory allocated on the Garbage Collected heap. Having a read-only reference type field simply guarantees that the reference remains constant - it says nothing about the block of state of the object it refers to.
Therefore, it is still possible for code running within one transaction to see state from another - even if the code is from the SAFE bucket.
Given this, for Yukon resident code, it is imperative that any reference type that is held in a static read-only field be immutable once constructed - in other words its state must not be able to be changed. This is the only way we can guarantee that changes from one transaction are truly isolated from another concurrent one.