File: System\ComponentModel\Composition\Hosting\CompositionLock.cs
Web Access
Project: src\src\libraries\System.ComponentModel.Composition\src\System.ComponentModel.Composition.csproj (System.ComponentModel.Composition)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
#define SINGLETHREADEDLOCKENFORCEMENT
using System.Threading;
using Microsoft.Internal;
 
namespace System.ComponentModel.Composition.Hosting
{
    // This is a lock class that needs to be held in order to perform any mutation of the parts/parts state in the composition
    // Today's implementation relies on the AppDomain-wide re-entrant lock for changes on the composition, and a narrow lock for changes in
    // the state of the specific ImportEngine
    // Today we make several assumptions to ensure thread-safety:
    // 1. Each composition doesn't change lock affinity
    // 2. Every part of the system that updates the status of the parts (in our case ImportEngine) needs to hold the same wide - lock
    // 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
    // 4. Narrow lock CAN be taken inside the wide lock
    // 5. Wide lock CANNOT be taken inside the narrow lock
    // 6. No 3rd party code will EVER get called inside the narrow lock
    // 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
    // we have no issue, other than potential overlocking
    internal sealed class CompositionLock : IDisposable
    {
        // narrow lock
        private readonly ReadWriteLock? _stateLock;
        // wide lock
        private static readonly object _compositionLock = new object();
 
        private int _isDisposed;
        private readonly bool _isThreadSafe;
 
        private static readonly EmptyLockHolder _EmptyLockHolder = new EmptyLockHolder();
 
        public CompositionLock(bool isThreadSafe)
        {
            _isThreadSafe = isThreadSafe;
            if (isThreadSafe)
            {
                _stateLock = new ReadWriteLock();
            }
        }
 
        public void Dispose()
        {
            if (_isThreadSafe)
            {
                if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
                {
                    _stateLock!.Dispose();
                }
            }
        }
 
        public bool IsThreadSafe
        {
            get
            {
                return _isThreadSafe;
            }
        }
 
        private void EnterCompositionLock()
        {
#pragma warning disable 618
            if (_isThreadSafe)
            {
                Monitor.Enter(_compositionLock);
            }
#pragma warning restore 618
        }
 
        private void ExitCompositionLock()
        {
            if (_isThreadSafe)
            {
                Monitor.Exit(_compositionLock);
            }
        }
 
        public IDisposable LockComposition()
        {
            if (_isThreadSafe)
            {
                return new CompositionLockHolder(this);
            }
            else
            {
                return _EmptyLockHolder;
            }
        }
 
        public IDisposable LockStateForRead()
        {
            if (_isThreadSafe)
            {
                return new ReadLock(_stateLock!);
            }
            else
            {
                return _EmptyLockHolder;
            }
        }
 
        public IDisposable LockStateForWrite()
        {
            if (_isThreadSafe)
            {
                return new WriteLock(_stateLock!);
            }
            else
            {
                return _EmptyLockHolder;
            }
        }
 
        // NOTE : this should NOT be changed to a struct as ImportEngine relies on it
        public sealed class CompositionLockHolder : IDisposable
        {
            private readonly CompositionLock _lock;
            private int _isDisposed;
 
            public CompositionLockHolder(CompositionLock @lock)
            {
                _lock = @lock;
 
                _isDisposed = 0;
                _lock.EnterCompositionLock();
            }
 
            public void Dispose()
            {
                if (Interlocked.CompareExchange(ref _isDisposed, 1, 0) == 0)
                {
                    _lock.ExitCompositionLock();
                }
            }
        }
 
        private sealed class EmptyLockHolder : IDisposable
        {
            public void Dispose()
            {
            }
        }
    }
}