PageRenderTime 52ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 0ms

/mcs/class/System.ComponentModel.Composition/src/ComponentModel/System/ComponentModel/Composition/Hosting/AtomicComposition.cs

https://github.com/iainlane/mono
C# | 305 lines | 225 code | 39 blank | 41 comment | 44 complexity | 079f29fb825d91ee99011c4179a7676f MD5 | raw file
  1. using System;
  2. using System.Diagnostics;
  3. using System.Collections.Generic;
  4. using Microsoft.Internal;
  5. using System.Diagnostics.CodeAnalysis;
  6. namespace System.ComponentModel.Composition.Hosting
  7. {
  8. /// <summary>
  9. /// AtomicComposition provides lightweight atomicCompositional semantics to enable temporary
  10. /// state to be managed for a series of nested atomicCompositions. Each atomicComposition maintains
  11. /// queryable state along with a sequence of actions necessary to complete the state when
  12. /// the atomicComposition is no longer in danger of being rolled back. State is completed or
  13. /// rolled back when the atomicComposition is disposed, depending on the state of the
  14. /// CompleteOnDipose property which defaults to false. The using(...) pattern in C# is a
  15. /// convenient mechanism for defining atomicComposition scopes.
  16. ///
  17. /// The least obvious aspects of AtomicComposition deal with nesting.
  18. ///
  19. /// Firstly, no complete actions are actually performed until the outermost atomicComposition is
  20. /// completed. Completeting or rolling back nested atomicCompositions serves only to change which
  21. /// actions would be completed the outer atomicComposition.
  22. ///
  23. /// Secondly, state is added in the form of queries associated with an object key. The
  24. /// key represents a unique object the state is being held on behalf of. The quieries are
  25. /// accessed throught the Query methods which provide automatic chaining to execute queries
  26. /// across the target atomicComposition and its inner atomicComposition as appropriate.
  27. ///
  28. /// Lastly, when a nested atomicComposition is created for a given outer the outer atomicComposition is locked.
  29. /// It remains locked until the inner atomicComposition is disposed or completeed preventing the addition of
  30. /// state, actions or other inner atomicCompositions.
  31. /// </summary>
  32. public class AtomicComposition : IDisposable
  33. {
  34. private readonly AtomicComposition _outerAtomicComposition;
  35. private KeyValuePair<object, object>[] _values;
  36. private int _valueCount = 0;
  37. private List<Action> _completeActionList;
  38. private List<Action> _revertActionList;
  39. private bool _isDisposed = false;
  40. private bool _isCompleted = false;
  41. private bool _containsInnerAtomicComposition = false;
  42. public AtomicComposition()
  43. : this(null)
  44. {
  45. }
  46. public AtomicComposition(AtomicComposition outerAtomicComposition)
  47. {
  48. // Lock the inner atomicComposition so that we can assume nothing changes except on
  49. // the innermost scope, and thereby optimize the query path
  50. if (outerAtomicComposition != null)
  51. {
  52. this._outerAtomicComposition = outerAtomicComposition;
  53. this._outerAtomicComposition.ContainsInnerAtomicComposition = true;
  54. }
  55. }
  56. public void SetValue(object key, object value)
  57. {
  58. ThrowIfDisposed();
  59. ThrowIfCompleteed();
  60. ThrowIfContainsInnerAtomicComposition();
  61. Requires.NotNull(key, "key");
  62. SetValueInternal(key, value);
  63. }
  64. public bool TryGetValue<T>(object key, out T value)
  65. {
  66. return TryGetValue(key, false, out value);
  67. }
  68. [SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters")]
  69. public bool TryGetValue<T>(object key, bool localAtomicCompositionOnly, out T value)
  70. {
  71. ThrowIfDisposed();
  72. ThrowIfCompleteed();
  73. Requires.NotNull(key, "key");
  74. return TryGetValueInternal(key, localAtomicCompositionOnly, out value);
  75. }
  76. public void AddCompleteAction(Action completeAction)
  77. {
  78. ThrowIfDisposed();
  79. ThrowIfCompleteed();
  80. ThrowIfContainsInnerAtomicComposition();
  81. Requires.NotNull(completeAction, "completeAction");
  82. if (this._completeActionList == null)
  83. {
  84. this._completeActionList = new List<Action>();
  85. }
  86. this._completeActionList.Add(completeAction);
  87. }
  88. public void AddRevertAction(Action revertAction)
  89. {
  90. ThrowIfDisposed();
  91. ThrowIfCompleteed();
  92. ThrowIfContainsInnerAtomicComposition();
  93. Requires.NotNull(revertAction, "revertAction");
  94. if (this._revertActionList == null)
  95. {
  96. this._revertActionList = new List<Action>();
  97. }
  98. this._revertActionList.Add(revertAction);
  99. }
  100. public void Complete()
  101. {
  102. ThrowIfDisposed();
  103. ThrowIfCompleteed();
  104. if (this._outerAtomicComposition == null)
  105. { // Execute all the complete actions
  106. FinalComplete();
  107. }
  108. else
  109. { // Copy the actions and state to the outer atomicComposition
  110. CopyComplete();
  111. }
  112. this._isCompleted = true;
  113. }
  114. public void Dispose()
  115. {
  116. Dispose(true);
  117. GC.SuppressFinalize(this);
  118. }
  119. protected virtual void Dispose(bool disposing)
  120. {
  121. ThrowIfDisposed();
  122. this._isDisposed = true;
  123. if (this._outerAtomicComposition != null)
  124. {
  125. this._outerAtomicComposition.ContainsInnerAtomicComposition = false;
  126. }
  127. // Revert is always immediate and involves forgetting information and
  128. // exceuting any appropriate revert actions
  129. if (!this._isCompleted)
  130. {
  131. if (this._revertActionList != null)
  132. {
  133. // Execute the revert actions in reverse order to ensure
  134. // everything incrementally rollsback its state.
  135. for (int i = this._revertActionList.Count - 1; i >= 0; i--)
  136. {
  137. Action action = this._revertActionList[i];
  138. action();
  139. }
  140. this._revertActionList = null;
  141. }
  142. }
  143. }
  144. private void FinalComplete()
  145. {
  146. // Completeting the outer most scope is easy, just execute all the actions
  147. if (this._completeActionList != null)
  148. {
  149. foreach (Action action in this._completeActionList)
  150. {
  151. action();
  152. }
  153. this._completeActionList = null;
  154. }
  155. }
  156. private void CopyComplete()
  157. {
  158. Assumes.NotNull(this._outerAtomicComposition);
  159. this._outerAtomicComposition.ContainsInnerAtomicComposition = false;
  160. // Inner scopes are much odder, because completeting them means coalescing them into the
  161. // outer scope - the complete or revert actions are deferred until the outermost scope completes
  162. // or any intermediate rolls back
  163. if (this._completeActionList != null)
  164. {
  165. foreach (Action action in this._completeActionList)
  166. {
  167. this._outerAtomicComposition.AddCompleteAction(action);
  168. }
  169. }
  170. if (this._revertActionList != null)
  171. {
  172. foreach (Action action in this._revertActionList)
  173. {
  174. this._outerAtomicComposition.AddRevertAction(action);
  175. }
  176. }
  177. // We can copy over existing atomicComposition entries because they're either already chained or
  178. // overwrite by design and can now be completed or rolled back together
  179. for (var index = 0; index < this._valueCount; index++)
  180. {
  181. this._outerAtomicComposition.SetValueInternal(
  182. this._values[index].Key, this._values[index].Value);
  183. }
  184. }
  185. private bool ContainsInnerAtomicComposition
  186. {
  187. set
  188. {
  189. if (value == true && this._containsInnerAtomicComposition == true)
  190. {
  191. throw new InvalidOperationException(Strings.AtomicComposition_AlreadyNested);
  192. }
  193. this._containsInnerAtomicComposition = value;
  194. }
  195. }
  196. private bool TryGetValueInternal<T>(object key, bool localAtomicCompositionOnly, out T value)
  197. {
  198. for (var index = 0; index < this._valueCount; index++)
  199. {
  200. if (this._values[index].Key == key)
  201. {
  202. value = (T)this._values[index].Value;
  203. return true;
  204. }
  205. }
  206. // If there's no atomicComposition available then recurse until we hit the outermost
  207. // scope, where upon we go ahead and return null
  208. if (!localAtomicCompositionOnly && this._outerAtomicComposition != null)
  209. {
  210. return this._outerAtomicComposition.TryGetValueInternal<T>(key, localAtomicCompositionOnly, out value);
  211. }
  212. value = default(T);
  213. return false;
  214. }
  215. private void SetValueInternal(object key, object value)
  216. {
  217. // Handle overwrites quickly
  218. for (var index = 0; index < this._valueCount; index++)
  219. {
  220. if (this._values[index].Key == key)
  221. {
  222. this._values[index] = new KeyValuePair<object,object>(key, value);
  223. return;
  224. }
  225. }
  226. // Expand storage when needed
  227. if (this._values == null || this._valueCount == this._values.Length)
  228. {
  229. var newQueries = new KeyValuePair<object, object>[this._valueCount == 0 ? 5 : this._valueCount * 2];
  230. if (this._values != null)
  231. {
  232. Array.Copy(this._values, newQueries, this._valueCount);
  233. }
  234. this._values = newQueries;
  235. }
  236. // Store a new entry
  237. this._values[_valueCount] = new KeyValuePair<object, object>(key, value);
  238. this._valueCount++;
  239. return;
  240. }
  241. [DebuggerStepThrough]
  242. private void ThrowIfContainsInnerAtomicComposition()
  243. {
  244. if (this._containsInnerAtomicComposition)
  245. {
  246. throw new InvalidOperationException(Strings.AtomicComposition_PartOfAnotherAtomicComposition);
  247. }
  248. }
  249. [DebuggerStepThrough]
  250. private void ThrowIfCompleteed()
  251. {
  252. if (this._isCompleted)
  253. {
  254. throw new InvalidOperationException(Strings.AtomicComposition_AlreadyCompleted);
  255. }
  256. }
  257. [DebuggerStepThrough]
  258. private void ThrowIfDisposed()
  259. {
  260. if (this._isDisposed)
  261. {
  262. throw ExceptionBuilder.CreateObjectDisposed(this);
  263. }
  264. }
  265. }
  266. }