[corlib] CoreRT System.Threading.Tasks (#6672)
[mono-project.git] / mcs / class / System.ComponentModel.Composition.4.5 / src / ComponentModel / System / ComponentModel / Composition / Hosting / ComposablePartCatalogCollection.cs
blobad2c78f52d732894d1a2c000da8527bcdb364ac0
1 // -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
4 using System;
5 using System.Collections.ObjectModel;
6 using System.Collections.Generic;
7 using System.ComponentModel.Composition.Primitives;
8 using System.Linq;
9 using System.Text;
10 using System.Threading;
11 using System.Diagnostics;
12 using Microsoft.Internal;
13 using Microsoft.Internal.Collections;
14 using System.Collections;
15 using Lock = Microsoft.Internal.Lock;
17 namespace System.ComponentModel.Composition.Hosting
19 /// <summary>
20 /// This class implements a threadsafe ICollection{T} of ComposablePartCatalog.
21 /// It is exposed as an ICollection(ComposablePartCatalog)
22 /// It is threadsafe, notifications are not marshalled using a SynchronizationContext.
23 /// It is Disposable.
24 /// </summary>
25 internal class ComposablePartCatalogCollection : ICollection<ComposablePartCatalog>, INotifyComposablePartCatalogChanged, IDisposable
27 private readonly Lock _lock = new Lock();
28 private Action<ComposablePartCatalogChangeEventArgs> _onChanged;
29 private Action<ComposablePartCatalogChangeEventArgs> _onChanging;
30 private List<ComposablePartCatalog> _catalogs = new List<ComposablePartCatalog>();
31 private volatile bool _isCopyNeeded = false;
32 private volatile bool _isDisposed = false;
33 private bool _hasChanged = false;
35 public ComposablePartCatalogCollection(
36 IEnumerable<ComposablePartCatalog> catalogs,
37 Action<ComposablePartCatalogChangeEventArgs> onChanged,
38 Action<ComposablePartCatalogChangeEventArgs> onChanging)
40 catalogs = catalogs ?? Enumerable.Empty<ComposablePartCatalog>();
41 this._catalogs = new List<ComposablePartCatalog>(catalogs);
42 this._onChanged = onChanged;
43 this._onChanging = onChanging;
45 SubscribeToCatalogNotifications(catalogs);
48 public void Add(ComposablePartCatalog item)
50 Requires.NotNull(item, "item");
52 this.ThrowIfDisposed();
54 var addedParts = new Lazy<IEnumerable<ComposablePartDefinition>>(() => item.ToArray(), LazyThreadSafetyMode.PublicationOnly);
56 using (var atomicComposition = new AtomicComposition())
58 this.RaiseChangingEvent(addedParts, null, atomicComposition);
60 using (new WriteLock(this._lock))
62 if (this._isCopyNeeded)
64 this._catalogs = new List<ComposablePartCatalog>(this._catalogs);
65 this._isCopyNeeded = false;
67 this._hasChanged = true;
68 this._catalogs.Add(item);
71 this.SubscribeToCatalogNotifications(item);
73 // Complete after the catalog changes are written
74 atomicComposition.Complete();
77 this.RaiseChangedEvent(addedParts, null);
80 /// <summary>
81 /// Notify when the contents of the Catalog has changed.
82 /// </summary>
83 public event EventHandler<ComposablePartCatalogChangeEventArgs> Changed;
85 /// <summary>
86 /// Notify when the contents of the Catalog has changing.
87 /// </summary>
88 public event EventHandler<ComposablePartCatalogChangeEventArgs> Changing;
90 public void Clear()
92 this.ThrowIfDisposed();
94 // No action is required if we are already empty
95 ComposablePartCatalog[] catalogs = null;
96 using (new ReadLock(this._lock))
98 if (this._catalogs.Count == 0)
100 return;
102 catalogs = this._catalogs.ToArray();
105 //TODO-MT: This is pretty suspect - we can easily eliminate catalogs that aren't listed as being
106 // removed. Then again, the idea of trying to mutate the catalog on two threads at the same time is pretty
107 // suspect to begin with. When would that ever result in a meaningful composition?
109 // We are doing this outside of the lock, so it's possible that the catalog will continute propagating events from things
110 // we are about to unsubscribe from. Given the non-specificity of our event, in the worst case scenario we would simply fire
111 // unnecessary events.
113 var removedParts = new Lazy<IEnumerable<ComposablePartDefinition>>(() => catalogs.SelectMany(catalog => catalog).ToArray(), LazyThreadSafetyMode.PublicationOnly);
115 // Validate the changes before applying them
116 using (var atomicComposition = new AtomicComposition())
118 this.RaiseChangingEvent(null, removedParts, atomicComposition);
119 this.UnsubscribeFromCatalogNotifications(catalogs);
121 using (new WriteLock(this._lock))
123 this._catalogs = new List<ComposablePartCatalog>();
125 this._isCopyNeeded = false;
126 this._hasChanged = true;
129 // Complete after the catalog changes are written
130 atomicComposition.Complete();
133 this.RaiseChangedEvent(null, removedParts);
136 public bool Contains(ComposablePartCatalog item)
138 Requires.NotNull(item, "item");
140 this.ThrowIfDisposed();
142 using (new ReadLock(this._lock))
144 return this._catalogs.Contains(item);
148 public void CopyTo(ComposablePartCatalog[] array, int arrayIndex)
150 this.ThrowIfDisposed();
152 using (new ReadLock(this._lock))
154 this._catalogs.CopyTo(array, arrayIndex);
158 public int Count
160 get
162 this.ThrowIfDisposed();
164 using (new ReadLock(this._lock))
166 return this._catalogs.Count;
171 public bool IsReadOnly
175 this.ThrowIfDisposed();
177 return false;
181 public bool Remove(ComposablePartCatalog item)
183 Requires.NotNull(item, "item");
185 this.ThrowIfDisposed();
187 using (new ReadLock(this._lock))
189 if (!this._catalogs.Contains(item))
191 return false;
195 bool isSuccessfulRemoval = false;
197 var removedParts = new Lazy<IEnumerable<ComposablePartDefinition>>(() => item.ToArray(), LazyThreadSafetyMode.PublicationOnly);
198 using (var atomicComposition = new AtomicComposition())
200 this.RaiseChangingEvent(null, removedParts, atomicComposition);
202 using (new WriteLock(this._lock))
204 if (_isCopyNeeded)
206 this._catalogs = new List<ComposablePartCatalog>(this._catalogs);
207 this._isCopyNeeded = false;
210 isSuccessfulRemoval = this._catalogs.Remove(item);
211 if (isSuccessfulRemoval)
213 this._hasChanged = true;
217 this.UnsubscribeFromCatalogNotifications(item);
219 // Complete after the catalog changes are written
220 atomicComposition.Complete();
223 this.RaiseChangedEvent(null, removedParts);
225 return isSuccessfulRemoval;
228 internal bool HasChanged
232 this.ThrowIfDisposed();
234 using (new ReadLock(this._lock))
236 return this._hasChanged;
241 public IEnumerator<ComposablePartCatalog> GetEnumerator()
243 this.ThrowIfDisposed();
245 using (new WriteLock(this._lock))
247 IEnumerator<ComposablePartCatalog> enumerator = this._catalogs.GetEnumerator();
248 this._isCopyNeeded = true;
249 return enumerator;
253 IEnumerator IEnumerable.GetEnumerator()
255 return this.GetEnumerator();
258 public void Dispose()
260 Dispose(true);
261 GC.SuppressFinalize(this);
264 protected virtual void Dispose(bool disposing)
266 if (disposing)
268 if (!this._isDisposed)
270 bool disposeLock = false;
271 IEnumerable<ComposablePartCatalog> catalogs = null;
274 using (new WriteLock(this._lock))
276 if (!this._isDisposed)
278 disposeLock = true;
280 catalogs = this._catalogs;
281 this._catalogs = null;
283 this._isDisposed = true;
287 finally
289 if (catalogs != null)
291 this.UnsubscribeFromCatalogNotifications(catalogs);
292 catalogs.ForEach(catalog => catalog.Dispose());
295 if (disposeLock)
297 this._lock.Dispose();
304 private void RaiseChangedEvent(
305 Lazy<IEnumerable<ComposablePartDefinition>> addedDefinitions,
306 Lazy<IEnumerable<ComposablePartDefinition>> removedDefinitions)
308 if (this._onChanged == null || this.Changed == null)
310 return;
313 var added = (addedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : addedDefinitions.Value);
314 var removed = (removedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : removedDefinitions.Value);
316 this._onChanged.Invoke(new ComposablePartCatalogChangeEventArgs(added, removed, null));
319 public void OnChanged(object sender, ComposablePartCatalogChangeEventArgs e)
321 var changedEvent = this.Changed;
322 if (changedEvent != null)
324 changedEvent(sender, e);
328 private void RaiseChangingEvent(
329 Lazy<IEnumerable<ComposablePartDefinition>> addedDefinitions,
330 Lazy<IEnumerable<ComposablePartDefinition>> removedDefinitions,
331 AtomicComposition atomicComposition)
333 if (this._onChanging == null || this.Changing == null)
335 return;
337 var added = (addedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : addedDefinitions.Value);
338 var removed = (removedDefinitions == null ? Enumerable.Empty<ComposablePartDefinition>() : removedDefinitions.Value);
340 this._onChanging.Invoke(new ComposablePartCatalogChangeEventArgs(added, removed, atomicComposition));
343 public void OnChanging(object sender, ComposablePartCatalogChangeEventArgs e)
345 var changingEvent = this.Changing;
346 if (changingEvent != null)
348 changingEvent(sender, e);
352 private void OnContainedCatalogChanged(object sender, ComposablePartCatalogChangeEventArgs e)
354 if (this._onChanged == null || this.Changed == null)
356 return;
359 this._onChanged.Invoke(e);
362 private void OnContainedCatalogChanging(object sender, ComposablePartCatalogChangeEventArgs e)
364 if (this._onChanging == null || this.Changing == null)
366 return;
369 this._onChanging.Invoke(e);
372 private void SubscribeToCatalogNotifications(ComposablePartCatalog catalog)
374 INotifyComposablePartCatalogChanged notifyCatalog = catalog as INotifyComposablePartCatalogChanged;
375 if (notifyCatalog != null)
377 notifyCatalog.Changed += this.OnContainedCatalogChanged;
378 notifyCatalog.Changing += this.OnContainedCatalogChanging;
382 private void SubscribeToCatalogNotifications(IEnumerable<ComposablePartCatalog> catalogs)
384 foreach (var catalog in catalogs)
386 SubscribeToCatalogNotifications(catalog);
390 private void UnsubscribeFromCatalogNotifications(ComposablePartCatalog catalog)
392 INotifyComposablePartCatalogChanged notifyCatalog = catalog as INotifyComposablePartCatalogChanged;
393 if (notifyCatalog != null)
395 notifyCatalog.Changed -= this.OnContainedCatalogChanged;
396 notifyCatalog.Changing -= this.OnContainedCatalogChanging;
400 private void UnsubscribeFromCatalogNotifications(IEnumerable<ComposablePartCatalog> catalogs)
402 foreach (var catalog in catalogs)
404 UnsubscribeFromCatalogNotifications(catalog);
408 private void ThrowIfDisposed()
410 if (this._isDisposed)
412 throw ExceptionBuilder.CreateObjectDisposed(this);