1
// -----------------------------------------------------------------------
2 // Copyright (c) Microsoft Corporation. All rights reserved.
3 // -----------------------------------------------------------------------
4 #define SINGLETHREADEDLOCKENFORCEMENT
6 using System
.Collections
.Generic
;
7 using System
.ComponentModel
.Composition
.Primitives
;
8 using System
.Diagnostics
;
10 using System
.Runtime
.CompilerServices
;
11 using Microsoft
.Internal
;
12 using Microsoft
.Internal
.Collections
;
13 using System
.Threading
;
14 using Lock
= Microsoft
.Internal
.Lock
;
16 namespace System
.ComponentModel
.Composition
.Hosting
18 // This a a lock class that needs to be held in order to perform any mutation of the parts/parts state in the composition
19 // Today's implementation relies on the AppDomain-wide re-entrant lock for changes on the composition, and a narrow lock for changes in
20 // the state of the specific ImportEngine
21 // Today we make several assumptions to ensure thread-safety:
22 // 1. Each composition doesn't change lock affinity
23 // 2. Every part of the system that updates the status of the parts (in our case ImportEngine) needs to hold the same wide - lock
24 // 3. State of the import engine that gets accessed outside of the wide lock needs to be accessed in the context of the narrow lock
25 // 4. Narrow lock CAN be taken inside the wide lock
26 // 5. Wide lock CANNOT be taken inside the narrow lock
27 // 6. No 3rd party code will EVER get called inside the narrow lock
28 // Sadly, this means that we WILL be calling 3rd party code under a lock, but as long as the lock is re-entrant and they can't invoke us on anotehr thread
29 // we have no issue, other than potential overlocking
30 internal sealed class CompositionLock
: IDisposable
33 private readonly Lock _stateLock
= null;
35 private static object _compositionLock
= new object();
37 private int _isDisposed
= 0;
38 private bool _isThreadSafe
= false;
40 private static readonly EmptyLockHolder _EmptyLockHolder
= new EmptyLockHolder();
42 public CompositionLock(bool isThreadSafe
)
44 this._isThreadSafe
= isThreadSafe
;
47 this._stateLock
= new Lock();
53 if (this._isThreadSafe
)
55 if (Interlocked
.CompareExchange(ref this._isDisposed
, 1, 0) == 0)
57 this._stateLock
.Dispose();
62 public bool IsThreadSafe
66 return this._isThreadSafe
;
70 private void EnterCompositionLock()
72 #pragma warning disable 618
73 if (this._isThreadSafe
)
75 Monitor
.Enter(_compositionLock
);
77 #pragma warning restore 618
80 private void ExitCompositionLock()
82 if (this._isThreadSafe
)
84 Monitor
.Exit(_compositionLock
);
88 public IDisposable
LockComposition()
90 if (this._isThreadSafe
)
92 return new CompositionLockHolder(this);
96 return _EmptyLockHolder
;
100 public IDisposable
LockStateForRead()
102 if (this._isThreadSafe
)
104 return new ReadLock(this._stateLock
);
108 return _EmptyLockHolder
;
112 public IDisposable
LockStateForWrite()
114 if (this._isThreadSafe
)
116 return new WriteLock(this._stateLock
);
120 return _EmptyLockHolder
;
124 // NOTE : this should NOT be changed to a struct as ImportEngine relies on it
125 public sealed class CompositionLockHolder
: IDisposable
127 private CompositionLock _lock
;
128 private int _isDisposed
;
130 public CompositionLockHolder(CompositionLock
@lock)
134 this._isDisposed
= 0;
135 this._lock
.EnterCompositionLock();
138 public void Dispose()
140 if (Interlocked
.CompareExchange(ref this._isDisposed
, 1, 0) == 0)
142 this._lock
.ExitCompositionLock();
147 private sealed class EmptyLockHolder
: IDisposable
149 public void Dispose()