/mcs/class/referencesource/System.Runtime.DurableInstancing/System/Runtime/PersistencePipeline.cs

https://github.com/pruiz/mono · C# · 567 lines · 489 code · 73 blank · 5 comment · 64 complexity · b844030a09fb7f3f8fae10864f90cc0c MD5 · raw file

  1. //-----------------------------------------------------------------------------
  2. // Copyright (c) Microsoft Corporation. All rights reserved.
  3. //-----------------------------------------------------------------------------
  4. namespace System.Runtime
  5. {
  6. using System;
  7. using System.Collections;
  8. using System.Collections.Generic;
  9. using System.Collections.ObjectModel;
  10. using System.Linq;
  11. using System.Runtime.DurableInstancing;
  12. using System.Threading;
  13. using System.Xml.Linq;
  14. class PersistencePipeline
  15. {
  16. readonly IEnumerable<IPersistencePipelineModule> modules;
  17. Stage expectedStage;
  18. IDictionary<XName, InstanceValue> values;
  19. ReadOnlyDictionaryInternal<XName, InstanceValue> readOnlyView;
  20. ValueDictionaryView readWriteView;
  21. ValueDictionaryView writeOnlyView;
  22. // Used for the save pipeline.
  23. public PersistencePipeline(IEnumerable<IPersistencePipelineModule> modules, Dictionary<XName, InstanceValue> initialValues)
  24. {
  25. Fx.Assert(modules != null, "Null modules collection provided to persistence pipeline.");
  26. this.expectedStage = Stage.Collect;
  27. this.modules = modules;
  28. this.values = initialValues;
  29. this.readOnlyView = new ReadOnlyDictionaryInternal<XName, InstanceValue>(this.values);
  30. this.readWriteView = new ValueDictionaryView(this.values, false);
  31. this.writeOnlyView = new ValueDictionaryView(this.values, true);
  32. }
  33. // Used for the load pipeline.
  34. public PersistencePipeline(IEnumerable<IPersistencePipelineModule> modules)
  35. {
  36. Fx.Assert(modules != null, "Null modules collection provided to persistence pipeline.");
  37. this.expectedStage = Stage.Load;
  38. this.modules = modules;
  39. }
  40. public ReadOnlyDictionaryInternal<XName, InstanceValue> Values
  41. {
  42. get
  43. {
  44. return this.readOnlyView;
  45. }
  46. }
  47. public bool IsSaveTransactionRequired
  48. {
  49. get
  50. {
  51. return this.modules.FirstOrDefault(value => value.IsSaveTransactionRequired) != null;
  52. }
  53. }
  54. public bool IsLoadTransactionRequired
  55. {
  56. get
  57. {
  58. return this.modules.FirstOrDefault(value => value.IsLoadTransactionRequired) != null;
  59. }
  60. }
  61. public void Collect()
  62. {
  63. Fx.AssertAndThrow(this.expectedStage == Stage.Collect, "Collect called at the wrong time.");
  64. this.expectedStage = Stage.None;
  65. foreach (IPersistencePipelineModule module in modules)
  66. {
  67. IDictionary<XName, object> readWriteValues;
  68. IDictionary<XName, object> writeOnlyValues;
  69. module.CollectValues(out readWriteValues, out writeOnlyValues);
  70. if (readWriteValues != null)
  71. {
  72. foreach (KeyValuePair<XName, object> value in readWriteValues)
  73. {
  74. try
  75. {
  76. this.values.Add(value.Key, new InstanceValue(value.Value));
  77. }
  78. catch (ArgumentException exception)
  79. {
  80. throw Fx.Exception.AsError(new InvalidOperationException(SRCore.NameCollisionOnCollect(value.Key, module.GetType().Name), exception));
  81. }
  82. }
  83. }
  84. if (writeOnlyValues != null)
  85. {
  86. foreach (KeyValuePair<XName, object> value in writeOnlyValues)
  87. {
  88. try
  89. {
  90. this.values.Add(value.Key, new InstanceValue(value.Value, InstanceValueOptions.Optional | InstanceValueOptions.WriteOnly));
  91. }
  92. catch (ArgumentException exception)
  93. {
  94. throw Fx.Exception.AsError(new InvalidOperationException(SRCore.NameCollisionOnCollect(value.Key, module.GetType().Name), exception));
  95. }
  96. }
  97. }
  98. }
  99. this.expectedStage = Stage.Map;
  100. }
  101. public void Map()
  102. {
  103. Fx.AssertAndThrow(this.expectedStage == Stage.Map, "Map called at the wrong time.");
  104. this.expectedStage = Stage.None;
  105. List<Tuple<IPersistencePipelineModule, IDictionary<XName, object>>> pendingValues = null;
  106. foreach (IPersistencePipelineModule module in modules)
  107. {
  108. IDictionary<XName, object> mappedValues = module.MapValues(this.readWriteView, this.writeOnlyView);
  109. if (mappedValues != null)
  110. {
  111. if (pendingValues == null)
  112. {
  113. pendingValues = new List<Tuple<IPersistencePipelineModule, IDictionary<XName, object>>>();
  114. }
  115. pendingValues.Add(new Tuple<IPersistencePipelineModule, IDictionary<XName, object>>(module, mappedValues));
  116. }
  117. }
  118. if (pendingValues != null)
  119. {
  120. foreach (Tuple<IPersistencePipelineModule, IDictionary<XName, object>> writeOnlyValues in pendingValues)
  121. {
  122. foreach (KeyValuePair<XName, object> value in writeOnlyValues.Item2)
  123. {
  124. try
  125. {
  126. this.values.Add(value.Key, new InstanceValue(value.Value, InstanceValueOptions.Optional | InstanceValueOptions.WriteOnly));
  127. }
  128. catch (ArgumentException exception)
  129. {
  130. throw Fx.Exception.AsError(new InvalidOperationException(SRCore.NameCollisionOnMap(value.Key, writeOnlyValues.Item1.GetType().Name), exception));
  131. }
  132. }
  133. }
  134. this.writeOnlyView.ResetCaches();
  135. }
  136. this.expectedStage = Stage.Save;
  137. }
  138. public IAsyncResult BeginSave(TimeSpan timeout, AsyncCallback callback, object state)
  139. {
  140. Fx.AssertAndThrow(this.expectedStage == Stage.Save, "Save called at the wrong time.");
  141. this.expectedStage = Stage.None;
  142. return new IOAsyncResult(this, false, timeout, callback, state);
  143. }
  144. public void EndSave(IAsyncResult result)
  145. {
  146. IOAsyncResult.End(result);
  147. }
  148. public void SetLoadedValues(IDictionary<XName, InstanceValue> values)
  149. {
  150. Fx.AssertAndThrow(this.expectedStage == Stage.Load, "SetLoadedValues called at the wrong time.");
  151. Fx.Assert(values != null, "Null values collection provided to SetLoadedValues.");
  152. this.values = values;
  153. this.readOnlyView = values as ReadOnlyDictionaryInternal<XName, InstanceValue> ?? new ReadOnlyDictionaryInternal<XName, InstanceValue>(values);
  154. this.readWriteView = new ValueDictionaryView(this.values, false);
  155. }
  156. public IAsyncResult BeginLoad(TimeSpan timeout, AsyncCallback callback, object state)
  157. {
  158. Fx.Assert(this.values != null, "SetLoadedValues not called.");
  159. Fx.AssertAndThrow(this.expectedStage == Stage.Load, "Load called at the wrong time.");
  160. this.expectedStage = Stage.None;
  161. return new IOAsyncResult(this, true, timeout, callback, state);
  162. }
  163. public void EndLoad(IAsyncResult result)
  164. {
  165. IOAsyncResult.End(result);
  166. this.expectedStage = Stage.Publish;
  167. }
  168. public void Publish()
  169. {
  170. Fx.AssertAndThrow(this.expectedStage == Stage.Publish || this.expectedStage == Stage.Load, "Publish called at the wrong time.");
  171. this.expectedStage = Stage.None;
  172. foreach (IPersistencePipelineModule module in modules)
  173. {
  174. module.PublishValues(this.readWriteView);
  175. }
  176. }
  177. public void Abort()
  178. {
  179. foreach (IPersistencePipelineModule module in modules)
  180. {
  181. try
  182. {
  183. module.Abort();
  184. }
  185. catch (Exception exception)
  186. {
  187. if (Fx.IsFatal(exception))
  188. {
  189. throw;
  190. }
  191. throw Fx.Exception.AsError(new CallbackException(SRCore.PersistencePipelineAbortThrew(module.GetType().Name), exception));
  192. }
  193. }
  194. }
  195. enum Stage
  196. {
  197. None,
  198. Collect,
  199. Map,
  200. Save,
  201. Load,
  202. Publish,
  203. }
  204. class ValueDictionaryView : IDictionary<XName, object>
  205. {
  206. IDictionary<XName, InstanceValue> basis;
  207. bool writeOnly;
  208. List<XName> keys;
  209. List<object> values;
  210. public ValueDictionaryView(IDictionary<XName, InstanceValue> basis, bool writeOnly)
  211. {
  212. this.basis = basis;
  213. this.writeOnly = writeOnly;
  214. }
  215. public ICollection<XName> Keys
  216. {
  217. get
  218. {
  219. if (this.keys == null)
  220. {
  221. this.keys = new List<XName>(this.basis.Where(value => value.Value.IsWriteOnly() == this.writeOnly).Select(value => value.Key));
  222. }
  223. return this.keys;
  224. }
  225. }
  226. public ICollection<object> Values
  227. {
  228. get
  229. {
  230. if (this.values == null)
  231. {
  232. this.values = new List<object>(this.basis.Where(value => value.Value.IsWriteOnly() == this.writeOnly).Select(value => value.Value.Value));
  233. }
  234. return this.values;
  235. }
  236. }
  237. public object this[XName key]
  238. {
  239. get
  240. {
  241. object value;
  242. if (TryGetValue(key, out value))
  243. {
  244. return value;
  245. }
  246. throw Fx.Exception.AsError(new KeyNotFoundException());
  247. }
  248. set
  249. {
  250. throw Fx.Exception.AsError(CreateReadOnlyException());
  251. }
  252. }
  253. public int Count
  254. {
  255. get
  256. {
  257. return Keys.Count;
  258. }
  259. }
  260. public bool IsReadOnly
  261. {
  262. get
  263. {
  264. return true;
  265. }
  266. }
  267. public void Add(XName key, object value)
  268. {
  269. throw Fx.Exception.AsError(CreateReadOnlyException());
  270. }
  271. public bool ContainsKey(XName key)
  272. {
  273. object dummy;
  274. return TryGetValue(key, out dummy);
  275. }
  276. public bool Remove(XName key)
  277. {
  278. throw Fx.Exception.AsError(CreateReadOnlyException());
  279. }
  280. public bool TryGetValue(XName key, out object value)
  281. {
  282. InstanceValue realValue;
  283. if (!this.basis.TryGetValue(key, out realValue) || realValue.IsWriteOnly() != this.writeOnly)
  284. {
  285. value = null;
  286. return false;
  287. }
  288. value = realValue.Value;
  289. return true;
  290. }
  291. public void Add(KeyValuePair<XName, object> item)
  292. {
  293. throw Fx.Exception.AsError(CreateReadOnlyException());
  294. }
  295. public void Clear()
  296. {
  297. throw Fx.Exception.AsError(CreateReadOnlyException());
  298. }
  299. public bool Contains(KeyValuePair<XName, object> item)
  300. {
  301. object value;
  302. if (!TryGetValue(item.Key, out value))
  303. {
  304. return false;
  305. }
  306. return EqualityComparer<object>.Default.Equals(value, item.Value);
  307. }
  308. public void CopyTo(KeyValuePair<XName, object>[] array, int arrayIndex)
  309. {
  310. foreach (KeyValuePair<XName, object> entry in this)
  311. {
  312. array[arrayIndex++] = entry;
  313. }
  314. }
  315. public bool Remove(KeyValuePair<XName, object> item)
  316. {
  317. throw Fx.Exception.AsError(CreateReadOnlyException());
  318. }
  319. public IEnumerator<KeyValuePair<XName, object>> GetEnumerator()
  320. {
  321. return this.basis.Where(value => value.Value.IsWriteOnly() == this.writeOnly).Select(value => new KeyValuePair<XName, object>(value.Key, value.Value.Value)).GetEnumerator();
  322. }
  323. IEnumerator IEnumerable.GetEnumerator()
  324. {
  325. return GetEnumerator();
  326. }
  327. internal void ResetCaches()
  328. {
  329. this.keys = null;
  330. this.values = null;
  331. }
  332. Exception CreateReadOnlyException()
  333. {
  334. return new InvalidOperationException(InternalSR.DictionaryIsReadOnly);
  335. }
  336. }
  337. class IOAsyncResult : AsyncResult
  338. {
  339. PersistencePipeline pipeline;
  340. bool isLoad;
  341. IPersistencePipelineModule[] pendingModules;
  342. int remainingModules;
  343. Exception exception;
  344. public IOAsyncResult(PersistencePipeline pipeline, bool isLoad, TimeSpan timeout, AsyncCallback callback, object state)
  345. : base(callback, state)
  346. {
  347. this.pipeline = pipeline;
  348. this.isLoad = isLoad;
  349. this.pendingModules = this.pipeline.modules.Where(value => value.IsIOParticipant).ToArray();
  350. this.remainingModules = this.pendingModules.Length;
  351. bool completeSelf = false;
  352. if (this.pendingModules.Length == 0)
  353. {
  354. completeSelf = true;
  355. }
  356. else
  357. {
  358. for (int i = 0; i < this.pendingModules.Length; i++)
  359. {
  360. Fx.Assert(!completeSelf, "Shouldn't have been completed yet.");
  361. IPersistencePipelineModule module = this.pendingModules[i];
  362. IAsyncResult result = null;
  363. try
  364. {
  365. if (this.isLoad)
  366. {
  367. result = module.BeginOnLoad(this.pipeline.readWriteView, timeout, Fx.ThunkCallback(new AsyncCallback(OnIOComplete)), i);
  368. }
  369. else
  370. {
  371. result = module.BeginOnSave(this.pipeline.readWriteView, this.pipeline.writeOnlyView, timeout, Fx.ThunkCallback(new AsyncCallback(OnIOComplete)), i);
  372. }
  373. }
  374. catch (Exception exception)
  375. {
  376. if (Fx.IsFatal(exception))
  377. {
  378. throw;
  379. }
  380. this.pendingModules[i] = null;
  381. ProcessException(exception);
  382. }
  383. if (result == null)
  384. {
  385. if (CompleteOne())
  386. {
  387. completeSelf = true;
  388. }
  389. }
  390. else if (result.CompletedSynchronously)
  391. {
  392. this.pendingModules[i] = null;
  393. if (IOComplete(result, module))
  394. {
  395. completeSelf = true;
  396. }
  397. }
  398. }
  399. }
  400. if (completeSelf)
  401. {
  402. Complete(true, this.exception);
  403. }
  404. }
  405. void OnIOComplete(IAsyncResult result)
  406. {
  407. if (result.CompletedSynchronously)
  408. {
  409. return;
  410. }
  411. int i = (int)result.AsyncState;
  412. IPersistencePipelineModule module = this.pendingModules[i];
  413. Fx.Assert(module != null, "There should be a pending result for this result");
  414. this.pendingModules[i] = null;
  415. if (IOComplete(result, module))
  416. {
  417. Complete(false, this.exception);
  418. }
  419. }
  420. bool IOComplete(IAsyncResult result, IPersistencePipelineModule module)
  421. {
  422. try
  423. {
  424. if (this.isLoad)
  425. {
  426. module.EndOnLoad(result);
  427. }
  428. else
  429. {
  430. module.EndOnSave(result);
  431. }
  432. }
  433. catch (Exception exception)
  434. {
  435. if (Fx.IsFatal(exception))
  436. {
  437. throw;
  438. }
  439. ProcessException(exception);
  440. }
  441. return CompleteOne();
  442. }
  443. void ProcessException(Exception exception)
  444. {
  445. if (exception != null)
  446. {
  447. bool abortNeeded = false;
  448. lock (this.pendingModules)
  449. {
  450. if (this.exception == null)
  451. {
  452. this.exception = exception;
  453. abortNeeded = true;
  454. }
  455. }
  456. if (abortNeeded)
  457. {
  458. Abort();
  459. }
  460. }
  461. }
  462. bool CompleteOne()
  463. {
  464. return Interlocked.Decrement(ref this.remainingModules) == 0;
  465. }
  466. void Abort()
  467. {
  468. for (int j = 0; j < this.pendingModules.Length; j++)
  469. {
  470. IPersistencePipelineModule module = this.pendingModules[j];
  471. if (module != null)
  472. {
  473. try
  474. {
  475. module.Abort();
  476. }
  477. catch (Exception exception)
  478. {
  479. if (Fx.IsFatal(exception))
  480. {
  481. throw;
  482. }
  483. throw Fx.Exception.AsError(new CallbackException(SRCore.PersistencePipelineAbortThrew(module.GetType().Name), exception));
  484. }
  485. }
  486. }
  487. }
  488. public static void End(IAsyncResult result)
  489. {
  490. AsyncResult.End<IOAsyncResult>(result);
  491. }
  492. }
  493. }
  494. }