// -----------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
// -----------------------------------------------------------------------
#define SINGLETHREADEDLOCKENFORCEMENT
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition.Primitives;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using Microsoft.Internal;
using Microsoft.Internal.Collections;
using System.Threading;
namespace System.ComponentModel.Composition.Hosting{
// This a 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 Lock _stateLock = null;
// wide lock
private static object _compositionLock = new object();
private int _isDisposed = 0;
private bool _isThreadSafe = false;
private static readonly EmptyLockHolder _EmptyLockHolder = new EmptyLockHolder();
public CompositionLock(bool isThreadSafe)
{
this._isThreadSafe = isThreadSafe;
if (isThreadSafe)
{
this._stateLock = new Lock();
}
}
public void Dispose()
{
if (this._isThreadSafe)
{
if (Interlocked.CompareExchange(ref this._isDisposed, 1, 0) == 0)
{
this._stateLock.Dispose();
}
}
}
public bool IsThreadSafe
{
get
{
return this._isThreadSafe;
}
}
private void EnterCompositionLock()
{
#pragma warning disable 618
if (this._isThreadSafe)
{
Monitor.Enter(_compositionLock);
}
#pragma warning restore 618
}
private void ExitCompositionLock()
{
if (this._isThreadSafe)
{
Monitor.Exit(_compositionLock);
}
}
public IDisposable LockComposition()
{
if (this._isThreadSafe)
{
return new CompositionLockHolder(this);
}
else
{
return _EmptyLockHolder;
}
}
public IDisposable LockStateForRead()
{
if (this._isThreadSafe)
{
return new ReadLock(this._stateLock);
}
else
{
return _EmptyLockHolder;
}
}
public IDisposable LockStateForWrite()
{
if (this._isThreadSafe)
{
return new WriteLock(this._stateLock);
}
else
{
return _EmptyLockHolder;
}
}
// NOTE : this should NOT be changed to a struct as ImportEngine relies on it
public sealed class CompositionLockHolder : IDisposable
{
private CompositionLock _lock;
private int _isDisposed;
public CompositionLockHolder(CompositionLock @lock)
{
this._lock = @lock;
this._isDisposed = 0;
this._lock.EnterCompositionLock();
}
public void Dispose()
{
if (Interlocked.CompareExchange(ref this._isDisposed, 1, 0) == 0)
{
this._lock.ExitCompositionLock();
}
}
}
private sealed class EmptyLockHolder : IDisposable
{
public void Dispose()
{
}
}
}
}
|