134 lines
4.4 KiB
C#
134 lines
4.4 KiB
C#
|
// Copyright (c) Microsoft Corp., 2004. All rights reserved.
|
||
|
#region Using directives
|
||
|
|
||
|
using System;
|
||
|
using System.Threading;
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
namespace System.Workflow.Runtime.DebugEngine
|
||
|
{
|
||
|
//
|
||
|
// IMPORTANT: Do not edit this file without consulting Break Safe Synchronization.doc!
|
||
|
//
|
||
|
internal abstract class BreakSafeBase<T> where T : ICloneable, new()
|
||
|
{
|
||
|
#region Data members
|
||
|
|
||
|
private volatile object currentData; // object because type parameters instances cannot be volatile.
|
||
|
private T nonEEDataClone;
|
||
|
private volatile bool nonEEDataConsistent;
|
||
|
private volatile bool nonEEIgnoreUpdate;
|
||
|
private Mutex nonEELock;
|
||
|
private object controllerUpdateObject;
|
||
|
private int controllerManagedThreadId;
|
||
|
|
||
|
#endregion
|
||
|
|
||
|
#region Methods and properties
|
||
|
|
||
|
protected BreakSafeBase(int controllerManagedThreadId)
|
||
|
{
|
||
|
this.currentData = new T();
|
||
|
this.nonEEDataClone = default(T);
|
||
|
this.nonEEDataConsistent = false;
|
||
|
this.nonEEIgnoreUpdate = false;
|
||
|
this.nonEELock = new Mutex(false);
|
||
|
this.controllerManagedThreadId = controllerManagedThreadId;
|
||
|
}
|
||
|
|
||
|
private bool IsEECall
|
||
|
{
|
||
|
get
|
||
|
{
|
||
|
return Thread.CurrentThread.ManagedThreadId == this.controllerManagedThreadId;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected object GetControllerUpdateObject()
|
||
|
{
|
||
|
return this.controllerUpdateObject;
|
||
|
}
|
||
|
|
||
|
protected void SetControllerUpdateObject(object updateObject)
|
||
|
{
|
||
|
// Ensure that the access to the variable this.controllerUpdateObject is exactly one instruction - StFld in this case.
|
||
|
this.controllerUpdateObject = updateObject;
|
||
|
}
|
||
|
|
||
|
protected T GetReaderData()
|
||
|
{
|
||
|
// Ensure that the access to the variable this.currentData is exactly one instruction - LdFld in this case.
|
||
|
object data = this.currentData;
|
||
|
return (T)data;
|
||
|
}
|
||
|
|
||
|
protected T GetWriterData()
|
||
|
{
|
||
|
if (IsEECall)
|
||
|
{
|
||
|
if (this.nonEEDataConsistent && this.nonEEIgnoreUpdate == false)
|
||
|
{
|
||
|
// Modify the object referred to by this.currentData directly.
|
||
|
return (T)this.currentData;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Clone and discard any non-EE update.
|
||
|
this.nonEEIgnoreUpdate = true;
|
||
|
return (T)((T)this.currentData).Clone();
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
// Reset the flag so that we can keep track of any concurrent EE updates.
|
||
|
this.nonEEIgnoreUpdate = false;
|
||
|
|
||
|
// Ensure that the access to the variable this.currentData is exactly one instruction - LdFld in this case.
|
||
|
object data = this.currentData;
|
||
|
return (T)((T)data).Clone();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void SaveData(T data)
|
||
|
{
|
||
|
if (IsEECall)
|
||
|
this.currentData = data;
|
||
|
else
|
||
|
{
|
||
|
// The non-EE clone is now in a consistent state.
|
||
|
this.nonEEDataClone = data;
|
||
|
this.nonEEDataConsistent = true;
|
||
|
|
||
|
this.controllerUpdateObject = null;
|
||
|
|
||
|
// If an EE call has already modified the data, it would have also performed current non-EE update
|
||
|
// when the debugger entered break mode. So discard the update. Asl ensure that the access to the
|
||
|
// variable this.currentData is exactly one instruction - StFld in this case.
|
||
|
if (this.nonEEIgnoreUpdate == false)
|
||
|
this.currentData = data;
|
||
|
|
||
|
// Clear the flag because we will clear the this.nonEEDataClone.
|
||
|
this.nonEEDataConsistent = false;
|
||
|
this.nonEEDataClone = default(T);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
protected void Lock()
|
||
|
{
|
||
|
// Serialize non-EE calls and do not invoke synchronization primitives during FuncEval.
|
||
|
if (!IsEECall)
|
||
|
this.nonEELock.WaitOne();
|
||
|
}
|
||
|
|
||
|
protected void Unlock()
|
||
|
{
|
||
|
// Serialize non-EE calls and do not invoke synchronization primitives during FuncEval.
|
||
|
if (!IsEECall)
|
||
|
this.nonEELock.ReleaseMutex();
|
||
|
}
|
||
|
|
||
|
#endregion
|
||
|
}
|
||
|
}
|