Browse Source

Make access to the underlying BuildEngine.Project is thread-safe. Should fix forum-13374 and similar problems.

git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/trunk@2067 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61
shortcuts
Daniel Grunwald 19 years ago
parent
commit
faf1f07bce
  1. 2
      src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooProject.cs
  2. 10
      src/AddIns/BackendBindings/WixBinding/Project/Src/Project/WixProject.cs
  3. 8
      src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs
  4. 2
      src/Main/Base/Project/Src/Internal/ConditionEvaluators/CompareProjectPropertyConditionEvaluator.cs
  5. 89
      src/Main/Base/Project/Src/Project/AbstractProject.cs
  6. 24
      src/Main/Base/Project/Src/Project/CompilableProject.cs
  7. 4
      src/Main/Base/Project/Src/Project/CustomTool.cs
  8. 30
      src/Main/Base/Project/Src/Project/IProject.cs
  9. 199
      src/Main/Base/Project/Src/Project/Items/ProjectItem.cs
  10. 210
      src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs
  11. 21
      src/Main/Base/Project/Src/Project/MSBuildInternals.cs
  12. 31
      src/Main/Base/Project/Src/Project/Solution/AbstractSolutionFolder.cs
  13. 19
      src/Main/Base/Project/Src/Project/Solution/ISolutionFolder.cs

2
src/AddIns/BackendBindings/Boo/BooBinding/Project/Src/BooProject.cs

@ -119,7 +119,7 @@ namespace Grunwald.BooBinding
public bool Ducky { public bool Ducky {
get { get {
bool val; bool val;
bool.TryParse(GetProperty("Ducky"), out val); bool.TryParse(GetEvaluatedProperty("Ducky"), out val);
return val; return val;
} }
} }

10
src/AddIns/BackendBindings/WixBinding/Project/Src/Project/WixProject.cs

@ -111,9 +111,9 @@ namespace ICSharpCode.WixBinding
/// </summary> /// </summary>
public string InstallerFullPath { public string InstallerFullPath {
get { get {
string outputPath = GetProperty("OutputPath") ?? String.Empty; string outputPath = GetEvaluatedProperty("OutputPath") ?? String.Empty;
string outputType = GetProperty("OutputType") ?? String.Empty; string outputType = GetEvaluatedProperty("OutputType") ?? String.Empty;
string outputName = GetProperty("OutputName") ?? String.Empty; string outputName = GetEvaluatedProperty("OutputName") ?? String.Empty;
string fileName = String.Concat(outputName, GetInstallerExtension(outputType)); string fileName = String.Concat(outputName, GetInstallerExtension(outputType));
return Path.Combine(Path.Combine(Directory, outputPath), fileName); return Path.Combine(Path.Combine(Directory, outputPath), fileName);
} }
@ -195,7 +195,7 @@ namespace ICSharpCode.WixBinding
/// <returns>An empty string if the name cannot be found.</returns> /// <returns>An empty string if the name cannot be found.</returns>
public string GetVariable(string name) public string GetVariable(string name)
{ {
string constants = GetProperty("DefineConstants") ?? String.Empty; string constants = GetEvaluatedProperty("DefineConstants") ?? String.Empty;
NameValuePairCollection nameValuePairs = new NameValuePairCollection(constants); NameValuePairCollection nameValuePairs = new NameValuePairCollection(constants);
return WixPropertyParser.Parse(nameValuePairs.GetValue(name), this); return WixPropertyParser.Parse(nameValuePairs.GetValue(name), this);
} }
@ -249,7 +249,7 @@ namespace ICSharpCode.WixBinding
/// AssemblyName must be implemented correctly - used when renaming projects. /// AssemblyName must be implemented correctly - used when renaming projects.
/// </summary> /// </summary>
public override string AssemblyName { public override string AssemblyName {
get { return GetProperty("OutputName") ?? Name; } get { return GetEvaluatedProperty("OutputName") ?? Name; }
set { SetProperty("OutputName", value); } set { SetProperty("OutputName", value); }
} }

8
src/Main/Base/Project/Src/Gui/WorkbenchSingleton.cs

@ -122,10 +122,16 @@ namespace ICSharpCode.SharpDevelop.Gui
public static bool InvokeRequired { public static bool InvokeRequired {
get { get {
return ((Form)workbench).InvokeRequired; if (workbench == null)
return false; // unit test mode, don't crash
else
return ((Form)workbench).InvokeRequired;
} }
} }
/// <summary>
/// Throws an exception if the current thread is not the main thread.
/// </summary>
internal static void AssertMainThread() internal static void AssertMainThread()
{ {
if (InvokeRequired) { if (InvokeRequired) {

2
src/Main/Base/Project/Src/Internal/ConditionEvaluators/CompareProjectPropertyConditionEvaluator.cs

@ -50,7 +50,7 @@ namespace ICSharpCode.SharpDevelop
else else
comparisonType = (StringComparison)Enum.Parse(typeof(StringComparison), comparisonTypeText); comparisonType = (StringComparison)Enum.Parse(typeof(StringComparison), comparisonTypeText);
return string.Equals(project.GetProperty(StringParser.Parse(condition.Properties["property"])), return string.Equals(project.GetEvaluatedProperty(StringParser.Parse(condition.Properties["property"])),
StringParser.Parse(condition.Properties["equals"]), StringParser.Parse(condition.Properties["equals"]),
comparisonType); comparisonType);
} }

89
src/Main/Base/Project/Src/Project/AbstractProject.cs

@ -52,6 +52,8 @@ namespace ICSharpCode.SharpDevelop.Project
public virtual void Dispose() public virtual void Dispose()
{ {
WorkbenchSingleton.AssertMainThread();
isDisposed = true; isDisposed = true;
if (Disposed != null) { if (Disposed != null) {
Disposed(this, EventArgs.Empty); Disposed(this, EventArgs.Empty);
@ -68,6 +70,8 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
public virtual Properties CreateMemento() public virtual Properties CreateMemento()
{ {
WorkbenchSingleton.AssertMainThread();
Properties properties = new Properties(); Properties properties = new Properties();
properties.Set("bookmarks", ICSharpCode.SharpDevelop.Bookmarks.BookmarkManager.GetProjectBookmarks(this).ToArray()); properties.Set("bookmarks", ICSharpCode.SharpDevelop.Bookmarks.BookmarkManager.GetProjectBookmarks(this).ToArray());
List<string> files = new List<string>(); List<string> files = new List<string>();
@ -85,6 +89,8 @@ namespace ICSharpCode.SharpDevelop.Project
public virtual void SetMemento(Properties memento) public virtual void SetMemento(Properties memento)
{ {
WorkbenchSingleton.AssertMainThread();
foreach (ICSharpCode.SharpDevelop.Bookmarks.SDBookmark mark in memento.Get("bookmarks", new ICSharpCode.SharpDevelop.Bookmarks.SDBookmark[0])) { foreach (ICSharpCode.SharpDevelop.Bookmarks.SDBookmark mark in memento.Get("bookmarks", new ICSharpCode.SharpDevelop.Bookmarks.SDBookmark[0])) {
ICSharpCode.SharpDevelop.Bookmarks.BookmarkManager.AddMark(mark); ICSharpCode.SharpDevelop.Bookmarks.BookmarkManager.AddMark(mark);
} }
@ -95,32 +101,51 @@ namespace ICSharpCode.SharpDevelop.Project
#endregion #endregion
#region Filename / Directory #region Filename / Directory
string fileName; volatile string fileName;
string cachedDirectoryName; string cachedDirectoryName;
/// <summary>
/// Gets the name of the project file.
/// (Full file name, example: @"D:\Serralongue\SharpDevelop\samples\CustomPad\CustomPad.csproj")
///
/// Only the getter is thread-safe.
/// </summary>
[ReadOnly(true)] [ReadOnly(true)]
public string FileName { public string FileName {
get { get {
return fileName ?? ""; return fileName ?? "";
} }
set { set {
WorkbenchSingleton.AssertMainThread();
Debug.Assert(Path.IsPathRooted(value)); Debug.Assert(Path.IsPathRooted(value));
fileName = value;
cachedDirectoryName = null; lock (SyncRoot) { // locking still required for Directory
fileName = value;
cachedDirectoryName = null;
}
} }
} }
/// <summary>
/// Gets the directory of the project file.
/// This is equivalent to Path.GetDirectoryName(project.FileName);
/// (Example: @"D:\Serralongue\SharpDevelop\samples\CustomPad")
///
/// This member is thread-safe.
/// </summary>
[Browsable(false)] [Browsable(false)]
public string Directory { public string Directory {
get { get {
if (cachedDirectoryName == null) { lock (SyncRoot) {
try { if (cachedDirectoryName == null) {
cachedDirectoryName = Path.GetDirectoryName(this.FileName); try {
} catch (Exception) { cachedDirectoryName = Path.GetDirectoryName(this.FileName);
cachedDirectoryName = ""; } catch (Exception) {
cachedDirectoryName = "";
}
} }
return cachedDirectoryName;
} }
return cachedDirectoryName;
} }
} }
#endregion #endregion
@ -131,6 +156,7 @@ namespace ICSharpCode.SharpDevelop.Project
[Browsable(false)] [Browsable(false)]
public List<ProjectSection> ProjectSections { public List<ProjectSection> ProjectSections {
get { get {
WorkbenchSingleton.AssertMainThread();
return projectSections; return projectSections;
} }
} }
@ -158,9 +184,11 @@ namespace ICSharpCode.SharpDevelop.Project
[ReadOnly(true)] [ReadOnly(true)]
[LocalizedProperty("${res:Dialog.ProjectOptions.Configuration}")] [LocalizedProperty("${res:Dialog.ProjectOptions.Configuration}")]
public virtual string ActiveConfiguration { public string ActiveConfiguration {
get { return activeConfiguration; } get { return activeConfiguration; }
set { set {
WorkbenchSingleton.AssertMainThread();
if (activeConfiguration != value) { if (activeConfiguration != value) {
activeConfiguration = value; activeConfiguration = value;
@ -180,9 +208,11 @@ namespace ICSharpCode.SharpDevelop.Project
[ReadOnly(true)] [ReadOnly(true)]
[LocalizedProperty("${res:Dialog.ProjectOptions.Platform}")] [LocalizedProperty("${res:Dialog.ProjectOptions.Platform}")]
public virtual string ActivePlatform { public string ActivePlatform {
get { return activePlatform; } get { return activePlatform; }
set { set {
WorkbenchSingleton.AssertMainThread();
if (activePlatform != value) { if (activePlatform != value) {
activePlatform = value; activePlatform = value;
@ -227,7 +257,7 @@ namespace ICSharpCode.SharpDevelop.Project
#endregion #endregion
/// <summary> /// <summary>
/// Gets the list of available file item types. /// Gets the list of available file item types. This member is thread-safe.
/// </summary> /// </summary>
public virtual ICollection<ItemType> AvailableFileItemTypes { public virtual ICollection<ItemType> AvailableFileItemTypes {
get { get {
@ -235,6 +265,11 @@ namespace ICSharpCode.SharpDevelop.Project
} }
} }
/// <summary>
/// Gets the list of items in the project. This member is thread-safe.
/// The returned collection is guaranteed not to change - adding new items or removing existing items
/// will create a new collection.
/// </summary>
[Browsable(false)] [Browsable(false)]
public virtual ReadOnlyCollection<ProjectItem> Items { public virtual ReadOnlyCollection<ProjectItem> Items {
get { get {
@ -242,6 +277,10 @@ namespace ICSharpCode.SharpDevelop.Project
} }
} }
/// <summary>
/// Gets all items in the project that have the specified item type.
/// This member is thread-safe.
/// </summary>
public virtual IEnumerable<ProjectItem> GetItemsOfType(ItemType itemType) public virtual IEnumerable<ProjectItem> GetItemsOfType(ItemType itemType)
{ {
foreach (ProjectItem item in this.Items) { foreach (ProjectItem item in this.Items) {
@ -312,9 +351,31 @@ namespace ICSharpCode.SharpDevelop.Project
throw new NotSupportedException(); throw new NotSupportedException();
} }
public virtual bool IsFileInProject(string fileName) /// <summary>
/// Returns true, if a specific file (given by it's name) is inside this project.
/// This member is thread-safe.
/// </summary>
/// <param name="fileName">The <b>fully qualified</b> file name of the file</param>
public bool IsFileInProject(string fileName)
{
return FindFile(fileName) != null;
}
/// <summary>
/// Returns the project item for a specific file; or null if the file is not found in the project.
/// This member is thread-safe.
/// </summary>
/// <param name="fileName">The <b>fully qualified</b> file name of the file</param>
public FileProjectItem FindFile(string fileName)
{ {
return false; lock (SyncRoot) {
return Linq.Find(Linq.OfType<FileProjectItem>(this.Items),
delegate(FileProjectItem item) {
return FileUtility.IsEqualFileName(item.FileName, fileName);
});
// return this.Items.OfType<FileProjectItem>().Find(
// item => FileUtility.IsEqualFileName(item.FileName, outputFileName));
}
} }
ParseProjectContent IProject.CreateProjectContent() ParseProjectContent IProject.CreateProjectContent()

24
src/Main/Base/Project/Src/Project/CompilableProject.cs

@ -97,9 +97,9 @@ namespace ICSharpCode.SharpDevelop.Project
[Browsable(false)] [Browsable(false)]
public string IntermediateOutputFullPath { public string IntermediateOutputFullPath {
get { get {
string outputPath = GetProperty("IntermediateOutputPath"); string outputPath = GetEvaluatedProperty("IntermediateOutputPath");
if (string.IsNullOrEmpty(outputPath)) { if (string.IsNullOrEmpty(outputPath)) {
outputPath = GetProperty("BaseIntermediateOutputPath"); outputPath = GetEvaluatedProperty("BaseIntermediateOutputPath");
if (string.IsNullOrEmpty(outputPath)) { if (string.IsNullOrEmpty(outputPath)) {
outputPath = "obj"; outputPath = "obj";
} }
@ -116,7 +116,7 @@ namespace ICSharpCode.SharpDevelop.Project
[Browsable(false)] [Browsable(false)]
public string DocumentationFileFullPath { public string DocumentationFileFullPath {
get { get {
string file = GetProperty("DocumentationFile"); string file = GetEvaluatedProperty("DocumentationFile");
if (string.IsNullOrEmpty(file)) if (string.IsNullOrEmpty(file))
return null; return null;
return Path.Combine(Directory, file); return Path.Combine(Directory, file);
@ -134,18 +134,18 @@ namespace ICSharpCode.SharpDevelop.Project
} }
public override string AssemblyName { public override string AssemblyName {
get { return GetProperty("AssemblyName") ?? Name; } get { return GetEvaluatedProperty("AssemblyName") ?? Name; }
set { SetProperty("AssemblyName", value); } set { SetProperty("AssemblyName", value); }
} }
public override string RootNamespace { public override string RootNamespace {
get { return GetProperty("RootNamespace") ?? ""; } get { return GetEvaluatedProperty("RootNamespace") ?? ""; }
set { SetProperty("RootNamespace", value); } set { SetProperty("RootNamespace", value); }
} }
public override string OutputAssemblyFullPath { public override string OutputAssemblyFullPath {
get { get {
string outputPath = GetProperty("OutputPath") ?? ""; string outputPath = GetEvaluatedProperty("OutputPath") ?? "";
return Path.Combine(Path.Combine(Directory, outputPath), AssemblyName + GetExtension(OutputType)); return Path.Combine(Path.Combine(Directory, outputPath), AssemblyName + GetExtension(OutputType));
} }
} }
@ -154,7 +154,7 @@ namespace ICSharpCode.SharpDevelop.Project
public OutputType OutputType { public OutputType OutputType {
get { get {
try { try {
return (OutputType)Enum.Parse(typeof(OutputType), GetProperty("OutputType") ?? "Exe"); return (OutputType)Enum.Parse(typeof(OutputType), GetEvaluatedProperty("OutputType") ?? "Exe");
} catch (ArgumentException) { } catch (ArgumentException) {
return OutputType.Exe; return OutputType.Exe;
} }
@ -233,7 +233,7 @@ namespace ICSharpCode.SharpDevelop.Project
[Browsable(false)] [Browsable(false)]
public string StartProgram { public string StartProgram {
get { get {
return GetProperty("StartProgram") ?? ""; return GetEvaluatedProperty("StartProgram") ?? "";
} }
set { set {
SetProperty("StartProgram", string.IsNullOrEmpty(value) ? null : value); SetProperty("StartProgram", string.IsNullOrEmpty(value) ? null : value);
@ -243,7 +243,7 @@ namespace ICSharpCode.SharpDevelop.Project
[Browsable(false)] [Browsable(false)]
public string StartUrl { public string StartUrl {
get { get {
return GetProperty("StartURL") ?? ""; return GetEvaluatedProperty("StartURL") ?? "";
} }
set { set {
SetProperty("StartURL", string.IsNullOrEmpty(value) ? null : value); SetProperty("StartURL", string.IsNullOrEmpty(value) ? null : value);
@ -254,7 +254,7 @@ namespace ICSharpCode.SharpDevelop.Project
public StartAction StartAction { public StartAction StartAction {
get { get {
try { try {
return (StartAction)Enum.Parse(typeof(StartAction), GetProperty("StartAction") ?? "Project"); return (StartAction)Enum.Parse(typeof(StartAction), GetEvaluatedProperty("StartAction") ?? "Project");
} catch (ArgumentException) { } catch (ArgumentException) {
return StartAction.Project; return StartAction.Project;
} }
@ -267,7 +267,7 @@ namespace ICSharpCode.SharpDevelop.Project
[Browsable(false)] [Browsable(false)]
public string StartArguments { public string StartArguments {
get { get {
return GetProperty("StartArguments") ?? ""; return GetEvaluatedProperty("StartArguments") ?? "";
} }
set { set {
SetProperty("StartArguments", string.IsNullOrEmpty(value) ? null : value); SetProperty("StartArguments", string.IsNullOrEmpty(value) ? null : value);
@ -277,7 +277,7 @@ namespace ICSharpCode.SharpDevelop.Project
[Browsable(false)] [Browsable(false)]
public string StartWorkingDirectory { public string StartWorkingDirectory {
get { get {
return GetProperty("StartWorkingDirectory") ?? ""; return GetEvaluatedProperty("StartWorkingDirectory") ?? "";
} }
set { set {
SetProperty("StartWorkingDirectory", string.IsNullOrEmpty(value) ? null : value); SetProperty("StartWorkingDirectory", string.IsNullOrEmpty(value) ? null : value);

4
src/Main/Base/Project/Src/Project/CustomTool.cs

@ -139,7 +139,7 @@ namespace ICSharpCode.SharpDevelop.Project
public FileProjectItem EnsureOutputFileIsInProject(FileProjectItem baseItem, string outputFileName) public FileProjectItem EnsureOutputFileIsInProject(FileProjectItem baseItem, string outputFileName)
{ {
WorkbenchSingleton.AssertMainThread(); WorkbenchSingleton.AssertMainThread();
FileProjectItem outputItem = MSBuildBasedProject.FindProjectItem(project, outputFileName); FileProjectItem outputItem = project.FindFile(outputFileName);
if (outputItem == null) { if (outputItem == null) {
outputItem = new FileProjectItem(project, ItemType.Compile); outputItem = new FileProjectItem(project, ItemType.Compile);
outputItem.FileName = outputFileName; outputItem.FileName = outputFileName;
@ -331,7 +331,7 @@ namespace ICSharpCode.SharpDevelop.Project
if (solution == null) return; if (solution == null) return;
IProject project = solution.FindProjectContainingFile(e.FileName); IProject project = solution.FindProjectContainingFile(e.FileName);
if (project == null) return; if (project == null) return;
FileProjectItem item = MSBuildBasedProject.FindProjectItem(project, e.FileName); FileProjectItem item = project.FindFile(e.FileName);
if (item == null) return; if (item == null) return;
if (!string.IsNullOrEmpty(item.CustomTool)) { if (!string.IsNullOrEmpty(item.CustomTool)) {
RunCustomTool(item, false); RunCustomTool(item, false);

30
src/Main/Base/Project/Src/Project/IProject.cs

@ -17,12 +17,15 @@ namespace ICSharpCode.SharpDevelop.Project
{ {
/// <summary> /// <summary>
/// Base interface for projects. /// Base interface for projects.
/// Thread-safe members lock on the SyncRoot. Non-thread-safe members may only be called from the main thread.
/// </summary> /// </summary>
public interface IProject public interface IProject
: ISolutionFolder, IDisposable, IMementoCapable, ICanBeDirty : ISolutionFolder, IDisposable, IMementoCapable, ICanBeDirty
{ {
/// <summary> /// <summary>
/// Gets a list of items in the project. /// Gets the list of items in the project. This member is thread-safe.
/// The returned collection is guaranteed not to change - adding new items or removing existing items
/// will create a new collection.
/// </summary> /// </summary>
ReadOnlyCollection<ProjectItem> Items { ReadOnlyCollection<ProjectItem> Items {
get; get;
@ -30,6 +33,7 @@ namespace ICSharpCode.SharpDevelop.Project
/// <summary> /// <summary>
/// Gets all items in the project that have the specified item type. /// Gets all items in the project that have the specified item type.
/// This member is thread-safe.
/// </summary> /// </summary>
IEnumerable<ProjectItem> GetItemsOfType(ItemType type); IEnumerable<ProjectItem> GetItemsOfType(ItemType type);
@ -40,7 +44,7 @@ namespace ICSharpCode.SharpDevelop.Project
ItemType GetDefaultItemType(string fileName); ItemType GetDefaultItemType(string fileName);
/// <summary> /// <summary>
/// Gets the list of available file item types. /// Gets the list of available file item types. This member is thread-safe.
/// </summary> /// </summary>
ICollection<ItemType> AvailableFileItemTypes { ICollection<ItemType> AvailableFileItemTypes {
get; get;
@ -53,10 +57,16 @@ namespace ICSharpCode.SharpDevelop.Project
get; get;
} }
/// <summary>
/// Gets the language properties used for this project. This member is thread-safe.
/// </summary>
ICSharpCode.SharpDevelop.Dom.LanguageProperties LanguageProperties { ICSharpCode.SharpDevelop.Dom.LanguageProperties LanguageProperties {
get; get;
} }
/// <summary>
/// Gets the ambience used for the project. This member is thread-safe.
/// </summary>
ICSharpCode.SharpDevelop.Dom.IAmbience Ambience { ICSharpCode.SharpDevelop.Dom.IAmbience Ambience {
get; get;
} }
@ -64,6 +74,8 @@ namespace ICSharpCode.SharpDevelop.Project
/// <summary> /// <summary>
/// Gets the name of the project file. /// Gets the name of the project file.
/// (Full file name, example: @"D:\Serralongue\SharpDevelop\samples\CustomPad\CustomPad.csproj") /// (Full file name, example: @"D:\Serralongue\SharpDevelop\samples\CustomPad\CustomPad.csproj")
///
/// Only the getter is thread-safe.
/// </summary> /// </summary>
string FileName { string FileName {
get; get;
@ -73,6 +85,8 @@ namespace ICSharpCode.SharpDevelop.Project
/// Gets the directory of the project file. /// Gets the directory of the project file.
/// This is equivalent to Path.GetDirectoryName(project.FileName); /// This is equivalent to Path.GetDirectoryName(project.FileName);
/// (Example: @"D:\Serralongue\SharpDevelop\samples\CustomPad") /// (Example: @"D:\Serralongue\SharpDevelop\samples\CustomPad")
///
/// This member is thread-safe.
/// </summary> /// </summary>
string Directory { string Directory {
get; get;
@ -155,11 +169,19 @@ namespace ICSharpCode.SharpDevelop.Project
void Save(); void Save();
/// <summary> /// <summary>
/// Returns true, if a specific file (given by it's name) /// Returns true, if a specific file (given by it's name) is inside this project.
/// is inside this project. /// This member is thread-safe.
/// </summary> /// </summary>
/// <param name="fileName">The <b>fully qualified</b> file name of the file</param>
bool IsFileInProject(string fileName); bool IsFileInProject(string fileName);
/// <summary>
/// Returns the project item for a specific file; or null if the file is not found in the project.
/// This member is thread-safe.
/// </summary>
/// <param name="fileName">The <b>fully qualified</b> file name of the file</param>
FileProjectItem FindFile(string fileName);
/// <summary> /// <summary>
/// Gets if the project can be started. /// Gets if the project can be started.
/// </summary> /// </summary>

199
src/Main/Base/Project/Src/Project/Items/ProjectItem.cs

@ -30,7 +30,7 @@ namespace ICSharpCode.SharpDevelop.Project
public abstract class ProjectItem : LocalizedObject, IDisposable, ICloneable public abstract class ProjectItem : LocalizedObject, IDisposable, ICloneable
{ {
IProject project; IProject project;
string fileNameCache; volatile string fileNameCache;
// either use: (bound mode) // either use: (bound mode)
BuildItem buildItem; BuildItem buildItem;
@ -61,6 +61,26 @@ namespace ICSharpCode.SharpDevelop.Project
this.virtualMetadata = new Dictionary<string, string>(); this.virtualMetadata = new Dictionary<string, string>();
} }
[Browsable(false)]
public IProject Project {
get {
return project;
}
}
/// <summary>
/// Gets the object used for synchronization. This is project.SyncRoot for items inside a project; or
/// virtualMetadata for items without project.
/// </summary>
object SyncRoot {
get {
if (project != null)
return project.SyncRoot;
else
return virtualMetadata;
}
}
/// <summary> /// <summary>
/// Gets if the item is added to it's owner project. /// Gets if the item is added to it's owner project.
/// </summary> /// </summary>
@ -91,53 +111,56 @@ namespace ICSharpCode.SharpDevelop.Project
} }
} }
[Browsable(false)]
public IProject Project {
get {
return project;
}
}
[Browsable(false)] [Browsable(false)]
public ItemType ItemType { public ItemType ItemType {
get { get {
if (buildItem != null) lock (SyncRoot) {
return new ItemType(buildItem.Name); if (buildItem != null)
else return new ItemType(buildItem.Name);
return virtualItemType; else
return virtualItemType;
}
} }
set { set {
if (buildItem != null) lock (SyncRoot) {
buildItem.Name = value.ToString(); if (buildItem != null)
else buildItem.Name = value.ToString();
virtualItemType = value; else
virtualItemType = value;
}
} }
} }
[Browsable(false)] [Browsable(false)]
public string Include { public string Include {
get { get {
if (buildItem != null) lock (SyncRoot) {
return buildItem.Include; if (buildItem != null)
else return buildItem.Include;
return virtualInclude; else
return virtualInclude;
}
} }
set { set {
if (buildItem != null) lock (SyncRoot) {
buildItem.Include = value; if (buildItem != null)
else buildItem.Include = value;
virtualInclude = value ?? ""; else
fileNameCache = null; virtualInclude = value ?? "";
fileNameCache = null;
}
} }
} }
#region Metadata access #region Metadata access
public bool HasMetadata(string metadataName) public bool HasMetadata(string metadataName)
{ {
if (buildItem != null) lock (SyncRoot) {
return buildItem.HasMetadata(metadataName); if (buildItem != null)
else return buildItem.HasMetadata(metadataName);
return virtualMetadata.ContainsKey(metadataName); else
return virtualMetadata.ContainsKey(metadataName);
}
} }
/// <summary> /// <summary>
@ -146,15 +169,17 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
public string GetEvaluatedMetadata(string metadataName) public string GetEvaluatedMetadata(string metadataName)
{ {
if (buildItem != null) { lock (SyncRoot) {
return buildItem.GetEvaluatedMetadata(metadataName) ?? ""; if (buildItem != null) {
} else { return buildItem.GetEvaluatedMetadata(metadataName) ?? "";
string val; } else {
virtualMetadata.TryGetValue(metadataName, out val); string val;
if (val == null) virtualMetadata.TryGetValue(metadataName, out val);
return ""; if (val == null)
else return "";
return MSBuildInternals.Unescape(val); else
return MSBuildInternals.Unescape(val);
}
} }
} }
@ -173,12 +198,14 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
public string GetMetadata(string metadataName) public string GetMetadata(string metadataName)
{ {
if (buildItem != null) { lock (SyncRoot) {
return buildItem.GetMetadata(metadataName) ?? ""; if (buildItem != null) {
} else { return buildItem.GetMetadata(metadataName) ?? "";
string val; } else {
virtualMetadata.TryGetValue(metadataName, out val); string val;
return val ?? ""; virtualMetadata.TryGetValue(metadataName, out val);
return val ?? "";
}
} }
} }
@ -192,10 +219,12 @@ namespace ICSharpCode.SharpDevelop.Project
if (string.IsNullOrEmpty(value)) { if (string.IsNullOrEmpty(value)) {
RemoveMetadata(metadataName); RemoveMetadata(metadataName);
} else { } else {
if (buildItem != null) lock (SyncRoot) {
buildItem.SetMetadata(metadataName, value, true); if (buildItem != null)
else buildItem.SetMetadata(metadataName, value, true);
virtualMetadata[metadataName] = MSBuildInternals.Escape(value); else
virtualMetadata[metadataName] = MSBuildInternals.Escape(value);
}
} }
} }
@ -217,10 +246,12 @@ namespace ICSharpCode.SharpDevelop.Project
if (string.IsNullOrEmpty(value)) { if (string.IsNullOrEmpty(value)) {
RemoveMetadata(metadataName); RemoveMetadata(metadataName);
} else { } else {
if (buildItem != null) lock (SyncRoot) {
buildItem.SetMetadata(metadataName, value); if (buildItem != null)
else buildItem.SetMetadata(metadataName, value);
virtualMetadata[metadataName] = value; else
virtualMetadata[metadataName] = value;
}
} }
} }
@ -229,22 +260,27 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
public void RemoveMetadata(string metadataName) public void RemoveMetadata(string metadataName)
{ {
if (buildItem != null) lock (SyncRoot) {
buildItem.RemoveMetadata(metadataName); if (buildItem != null)
else buildItem.RemoveMetadata(metadataName);
virtualMetadata.Remove(metadataName); else
virtualMetadata.Remove(metadataName);
}
} }
/// <summary> /// <summary>
/// Gets the names of all existing meta data items on this project item. /// Gets the names of all existing meta data items on this project item. The resulting collection
/// is a copy that will not be affected by future changes to the project item.
/// </summary> /// </summary>
[Browsable(false)] [Browsable(false)]
public IEnumerable<string> MetadataNames { public IEnumerable<string> MetadataNames {
get { get {
if (buildItem != null) lock (SyncRoot) {
return MSBuildInternals.GetCustomMetadataNames(buildItem); if (buildItem != null)
else return MSBuildInternals.GetCustomMetadataNames(buildItem);
return virtualMetadata.Keys; else
return Linq.ToArray(virtualMetadata.Keys);
}
} }
} }
#endregion #endregion
@ -254,11 +290,15 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
public virtual void CopyMetadataTo(ProjectItem targetItem) public virtual void CopyMetadataTo(ProjectItem targetItem)
{ {
if (this.buildItem != null && targetItem.buildItem != null) { lock (SyncRoot) {
this.buildItem.CopyCustomMetadataTo(targetItem.buildItem); lock (targetItem.SyncRoot) {
} else { if (this.buildItem != null && targetItem.buildItem != null) {
foreach (string name in this.MetadataNames) { this.buildItem.CopyCustomMetadataTo(targetItem.buildItem);
targetItem.SetMetadata(name, this.GetMetadata(name)); } else {
foreach (string name in this.MetadataNames) {
targetItem.SetMetadata(name, this.GetMetadata(name));
}
}
} }
} }
} }
@ -285,14 +325,16 @@ namespace ICSharpCode.SharpDevelop.Project
BuildItem CloneBuildItem() BuildItem CloneBuildItem()
{ {
if (buildItem != null) { lock (SyncRoot) {
return buildItem.Clone(); if (buildItem != null) {
} else { return buildItem.Clone();
BuildItem dummyItem = new BuildItem(this.ItemType.ToString(), this.Include); } else {
foreach (string name in this.MetadataNames) { BuildItem dummyItem = new BuildItem(this.ItemType.ToString(), this.Include);
dummyItem.SetMetadata(name, this.GetMetadata(name)); foreach (string name in this.MetadataNames) {
dummyItem.SetMetadata(name, this.GetMetadata(name));
}
return dummyItem;
} }
return dummyItem;
} }
} }
@ -307,9 +349,14 @@ namespace ICSharpCode.SharpDevelop.Project
if (project == null) { if (project == null) {
throw new NotSupportedException("Not supported for items without project."); throw new NotSupportedException("Not supported for items without project.");
} }
if (fileNameCache == null) string fileName = this.fileNameCache;
fileNameCache = Path.Combine(project.Directory, this.Include); if (fileName == null) {
return fileNameCache; lock (SyncRoot) {
fileName = Path.Combine(project.Directory, this.Include);
fileNameCache = fileName;
}
}
return fileName;
} }
set { set {
if (project == null) { if (project == null) {

210
src/Main/Base/Project/Src/Project/MSBuildBasedProject.cs

@ -158,13 +158,16 @@ namespace ICSharpCode.SharpDevelop.Project
#region Get Property #region Get Property
/// <summary> /// <summary>
/// Retrieves the evaluated property '<paramref name="propertyName"/>' from the /// Retrieves the evaluated property '<paramref name="propertyName"/>' from the
/// active configuration/platform. /// active configuration/platform. This method can retrieve any MSBuild property, including those
/// defined in imported .target files.
/// </summary> /// </summary>
/// <param name="propertyName">The name of the MSBuild property to read.</param> /// <param name="propertyName">The name of the MSBuild property to read.</param>
/// <returns>The evaluated value of the property, or null if the property doesn't exist</returns> /// <returns>The evaluated value of the property, or null if the property doesn't exist</returns>
public string GetProperty(string propertyName) public string GetEvaluatedProperty(string propertyName)
{ {
return project.GetEvaluatedProperty(propertyName); lock (SyncRoot) {
return project.GetEvaluatedProperty(propertyName);
}
} }
MSBuild.Project evaluatingTempProject; MSBuild.Project evaluatingTempProject;
@ -195,13 +198,15 @@ namespace ICSharpCode.SharpDevelop.Project
public string GetProperty(string configuration, string platform, string propertyName, public string GetProperty(string configuration, string platform, string propertyName,
out PropertyStorageLocations location) out PropertyStorageLocations location)
{ {
MSBuild.BuildPropertyGroup group; lock (SyncRoot) {
MSBuild.BuildProperty prop = FindPropertyObject(configuration, platform, propertyName, MSBuild.BuildPropertyGroup group;
out group, out location); MSBuild.BuildProperty prop = FindPropertyObject(configuration, platform, propertyName,
if (prop == null) out group, out location);
return null; if (prop == null)
else return null;
return prop.FinalValue; else
return prop.FinalValue;
}
} }
/// <summary> /// <summary>
@ -243,13 +248,15 @@ namespace ICSharpCode.SharpDevelop.Project
string propertyName, string propertyName,
out PropertyStorageLocations location) out PropertyStorageLocations location)
{ {
MSBuild.BuildPropertyGroup group; lock (SyncRoot) {
MSBuild.BuildProperty prop = FindPropertyObject(configuration, platform, propertyName, MSBuild.BuildPropertyGroup group;
out group, out location); MSBuild.BuildProperty prop = FindPropertyObject(configuration, platform, propertyName,
if (prop == null) out group, out location);
return null; if (prop == null)
else return null;
return prop.Value; else
return prop.Value;
}
} }
/// <summary> /// <summary>
@ -428,6 +435,18 @@ namespace ICSharpCode.SharpDevelop.Project
string propertyName, string newValue, string propertyName, string newValue,
PropertyStorageLocations location, PropertyStorageLocations location,
bool treatPropertyValueAsLiteral) bool treatPropertyValueAsLiteral)
{
ProjectPropertyChangedEventArgs args;
lock (SyncRoot) {
args = SetPropertyInternal(configuration, platform, propertyName, newValue, location, treatPropertyValueAsLiteral);
}
OnPropertyChanged(args);
}
ProjectPropertyChangedEventArgs SetPropertyInternal(string configuration, string platform,
string propertyName, string newValue,
PropertyStorageLocations location,
bool treatPropertyValueAsLiteral)
{ {
PropertyStorageLocations oldLocation; PropertyStorageLocations oldLocation;
MSBuild.BuildPropertyGroup existingPropertyGroup; MSBuild.BuildPropertyGroup existingPropertyGroup;
@ -562,7 +581,7 @@ namespace ICSharpCode.SharpDevelop.Project
propertyInsertionPosition, propertyInsertionPosition,
treatPropertyValueAsLiteral); treatPropertyValueAsLiteral);
} }
OnPropertyChanged(args); return args;
} }
/// <summary> /// <summary>
@ -611,17 +630,29 @@ namespace ICSharpCode.SharpDevelop.Project
#region IProjectItemListProvider interface #region IProjectItemListProvider interface
List<ProjectItem> items = new List<ProjectItem>(); List<ProjectItem> items = new List<ProjectItem>();
ReadOnlyCollection<ProjectItem> itemsReadOnly; volatile ReadOnlyCollection<ProjectItem> itemsReadOnly;
ICollection<ItemType> availableFileItemTypes = ItemType.DefaultFileItems; volatile ICollection<ItemType> availableFileItemTypes = ItemType.DefaultFileItems;
/// <summary>
/// Gets the list of items in the project. This member is thread-safe.
/// The returned collection is guaranteed not to change - adding new items or removing existing items
/// will create a new collection.
/// </summary>
public override ReadOnlyCollection<ProjectItem> Items { public override ReadOnlyCollection<ProjectItem> Items {
get { get {
return itemsReadOnly ?? (itemsReadOnly = items.AsReadOnly()); ReadOnlyCollection<ProjectItem> c = itemsReadOnly;
if (c == null) {
lock (SyncRoot) {
c = Array.AsReadOnly(items.ToArray());
}
itemsReadOnly = c;
}
return c;
} }
} }
/// <summary> /// <summary>
/// Gets the list of available file item types. /// Gets the list of available file item types. This member is thread-safe.
/// </summary> /// </summary>
public override ICollection<ItemType> AvailableFileItemTypes { public override ICollection<ItemType> AvailableFileItemTypes {
get { get {
@ -634,22 +665,27 @@ namespace ICSharpCode.SharpDevelop.Project
/// </summary> /// </summary>
internal void CreateItemsListFromMSBuild() internal void CreateItemsListFromMSBuild()
{ {
foreach (ProjectItem item in items) { WorkbenchSingleton.AssertMainThread();
item.Dispose();
}
items.Clear();
Set<ItemType> availableFileItemTypes = new Set<ItemType>(); lock (SyncRoot) {
availableFileItemTypes.AddRange(ItemType.DefaultFileItems); foreach (ProjectItem item in items) {
foreach (MSBuild.BuildItem item in project.GetEvaluatedItemsByName("AvailableItemName")) { item.Dispose();
availableFileItemTypes.Add(new ItemType(item.Include)); }
} items.Clear();
this.availableFileItemTypes = availableFileItemTypes.AsReadOnly(); itemsReadOnly = null; // remove readonly variant of item list - will regenerate on next Items call
foreach (MSBuild.BuildItem item in project.EvaluatedItems) { Set<ItemType> availableFileItemTypes = new Set<ItemType>();
if (item.IsImported) continue; availableFileItemTypes.AddRange(ItemType.DefaultFileItems);
foreach (MSBuild.BuildItem item in project.GetEvaluatedItemsByName("AvailableItemName")) {
availableFileItemTypes.Add(new ItemType(item.Include));
}
this.availableFileItemTypes = availableFileItemTypes.AsReadOnly();
items.Add(CreateProjectItem(item)); foreach (MSBuild.BuildItem item in project.EvaluatedItems) {
if (item.IsImported) continue;
items.Add(CreateProjectItem(item));
}
} }
} }
@ -662,30 +698,35 @@ namespace ICSharpCode.SharpDevelop.Project
if (item.IsAddedToProject) if (item.IsAddedToProject)
throw new ArgumentException("item is already added to project", "item"); throw new ArgumentException("item is already added to project", "item");
items.Add(item); WorkbenchSingleton.AssertMainThread();
foreach (MSBuild.BuildItemGroup g in project.ItemGroups) {
if (g.IsImported || !string.IsNullOrEmpty(g.Condition) || g.Count == 0) lock (SyncRoot) {
continue; items.Add(item);
if (g[0].Name == item.ItemType.ItemName) { itemsReadOnly = null; // remove readonly variant of item list - will regenerate on next Items call
MSBuildInternals.AddItemToGroup(g, item); foreach (MSBuild.BuildItemGroup g in project.ItemGroups) {
return; if (g.IsImported || !string.IsNullOrEmpty(g.Condition) || g.Count == 0)
} continue;
if (g[0].Name == "Reference") if (g[0].Name == item.ItemType.ItemName) {
continue;
if (ItemType.DefaultFileItems.Contains(new ItemType(g[0].Name))) {
if (ItemType.DefaultFileItems.Contains(item.ItemType)) {
MSBuildInternals.AddItemToGroup(g, item); MSBuildInternals.AddItemToGroup(g, item);
return; return;
} else { }
if (g[0].Name == "Reference")
continue; continue;
if (ItemType.DefaultFileItems.Contains(new ItemType(g[0].Name))) {
if (ItemType.DefaultFileItems.Contains(item.ItemType)) {
MSBuildInternals.AddItemToGroup(g, item);
return;
} else {
continue;
}
} }
MSBuildInternals.AddItemToGroup(g, item);
return;
} }
MSBuild.BuildItemGroup newGroup = project.AddNewItemGroup();
MSBuildInternals.AddItemToGroup(g, item); MSBuildInternals.AddItemToGroup(newGroup, item);
return;
} }
MSBuild.BuildItemGroup newGroup = project.AddNewItemGroup();
MSBuildInternals.AddItemToGroup(newGroup, item);
} }
bool IProjectItemListProvider.RemoveProjectItem(ProjectItem item) bool IProjectItemListProvider.RemoveProjectItem(ProjectItem item)
@ -697,39 +738,24 @@ namespace ICSharpCode.SharpDevelop.Project
if (!item.IsAddedToProject) if (!item.IsAddedToProject)
return false; return false;
if (items.Remove(item)) { WorkbenchSingleton.AssertMainThread();
project.RemoveItem(item.BuildItem);
item.BuildItem = null; // make the item free again lock (SyncRoot) {
return true; if (items.Remove(item)) {
} else { itemsReadOnly = null; // remove readonly variant of item list - will regenerate on next Items call
throw new InvalidOperationException("Expected that the item is added to this project!"); project.RemoveItem(item.BuildItem);
item.BuildItem = null; // make the item free again
return true;
} else {
throw new InvalidOperationException("Expected that the item is added to this project!");
}
} }
} }
public override bool IsFileInProject(string fileName)
{
return FindProjectItem(this, fileName) != null;
}
public FileProjectItem FindFile(string fileName)
{
return FindProjectItem(this, fileName);
}
internal static FileProjectItem FindProjectItem(IProject project, string fileName)
{
return Linq.Find(Linq.OfType<FileProjectItem>(project.Items),
delegate(FileProjectItem item) {
return FileUtility.IsEqualFileName(item.FileName, fileName);
});
// return project.Items.OfType<FileProjectItem>().Find(
// item => FileUtility.IsEqualFileName(item.FileName, outputFileName));
}
#endregion #endregion
#region Wrapped MSBuild Properties #region Wrapped MSBuild Properties
public override string AppDesignerFolder { public override string AppDesignerFolder {
get { return GetProperty("AppDesignerFolder"); } get { return GetEvaluatedProperty("AppDesignerFolder"); }
set { SetProperty("AppDesignerFolder", value); } set { SetProperty("AppDesignerFolder", value); }
} }
#endregion #endregion
@ -773,12 +799,12 @@ namespace ICSharpCode.SharpDevelop.Project
InitializeMSBuildProject(); InitializeMSBuildProject();
project.Load(fileName); project.Load(fileName);
this.ActiveConfiguration = GetProperty("Configuration") ?? this.ActiveConfiguration; this.ActiveConfiguration = GetEvaluatedProperty("Configuration") ?? this.ActiveConfiguration;
this.ActivePlatform = GetProperty("Platform") ?? this.ActivePlatform; this.ActivePlatform = GetEvaluatedProperty("Platform") ?? this.ActivePlatform;
CreateItemsListFromMSBuild(); CreateItemsListFromMSBuild();
LoadConfigurationPlatformNamesFromMSBuild(); LoadConfigurationPlatformNamesFromMSBuild();
IdGuid = GetProperty("ProjectGuid"); IdGuid = GetEvaluatedProperty("ProjectGuid");
} finally { } finally {
isLoading = false; isLoading = false;
} }
@ -788,7 +814,9 @@ namespace ICSharpCode.SharpDevelop.Project
#region Saving #region Saving
public override void Save(string fileName) public override void Save(string fileName)
{ {
project.Save(fileName); lock (SyncRoot) {
project.Save(fileName);
}
} }
#endregion #endregion
@ -796,8 +824,10 @@ namespace ICSharpCode.SharpDevelop.Project
protected override void OnActiveConfigurationChanged(EventArgs e) protected override void OnActiveConfigurationChanged(EventArgs e)
{ {
if (!isLoading) { if (!isLoading) {
project.GlobalProperties.SetProperty("Configuration", this.ActiveConfiguration, true); lock (SyncRoot) {
CreateItemsListFromMSBuild(); project.GlobalProperties.SetProperty("Configuration", this.ActiveConfiguration, true);
CreateItemsListFromMSBuild();
}
} }
base.OnActiveConfigurationChanged(e); base.OnActiveConfigurationChanged(e);
} }
@ -805,8 +835,10 @@ namespace ICSharpCode.SharpDevelop.Project
protected override void OnActivePlatformChanged(EventArgs e) protected override void OnActivePlatformChanged(EventArgs e)
{ {
if (!isLoading) { if (!isLoading) {
project.GlobalProperties.SetProperty("Platform", this.ActivePlatform, true); lock (SyncRoot) {
CreateItemsListFromMSBuild(); project.GlobalProperties.SetProperty("Platform", this.ActivePlatform, true);
CreateItemsListFromMSBuild();
}
} }
base.OnActivePlatformChanged(e); base.OnActivePlatformChanged(e);
} }

21
src/Main/Base/Project/Src/Project/MSBuildInternals.cs

@ -246,17 +246,22 @@ namespace ICSharpCode.SharpDevelop.Project
if (newRawPath == null) if (newRawPath == null)
throw new ArgumentNullException("newRawPath"); throw new ArgumentNullException("newRawPath");
XmlAttribute a = (XmlAttribute)typeof(MSBuild.Import).InvokeMember( lock (project.SyncRoot) {
"ProjectPathAttribute", XmlAttribute a = (XmlAttribute)typeof(MSBuild.Import).InvokeMember(
BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic, "ProjectPathAttribute",
null, import, null BindingFlags.GetProperty | BindingFlags.Instance | BindingFlags.NonPublic,
); null, import, null
a.Value = newRawPath; );
EndXmlManipulation(project.MSBuildProject); a.Value = newRawPath;
EndXmlManipulation(project.MSBuildProject);
}
project.CreateItemsListFromMSBuild(); project.CreateItemsListFromMSBuild();
} }
public static IEnumerable<string> GetCustomMetadataNames(MSBuild.BuildItem item) /// <summary>
/// Gets an array containing all custom metadata names.
/// </summary>
public static string[] GetCustomMetadataNames(MSBuild.BuildItem item)
{ {
ArrayList a = (ArrayList)typeof(MSBuild.BuildItem).InvokeMember( ArrayList a = (ArrayList)typeof(MSBuild.BuildItem).InvokeMember(
"GetAllCustomMetadataNames", "GetAllCustomMetadataNames",

31
src/Main/Base/Project/Src/Project/Solution/AbstractSolutionFolder.cs

@ -12,23 +12,40 @@ using ICSharpCode.SharpDevelop.Gui;
namespace ICSharpCode.SharpDevelop.Project namespace ICSharpCode.SharpDevelop.Project
{ {
/// <summary> /// <summary>
/// Description of ISolutionFolderContainer. /// Default implementation for ISolutionFolderContainer. Thread-safe.
/// </summary> /// </summary>
public abstract class AbstractSolutionFolder : LocalizedObject, ISolutionFolder public abstract class AbstractSolutionFolder : LocalizedObject, ISolutionFolder
{ {
readonly object syncRoot = new object();
ISolutionFolderContainer parent = null; ISolutionFolderContainer parent = null;
string typeGuid = null; string typeGuid = null;
string idGuid = null; string idGuid = null;
string location = null; string location = null;
string name = null; string name = null;
/// <summary>
/// Gets the object used for thread-safe synchronization.
/// All members lock on this object, but if you manipulate underlying structures
/// (such as the MSBuild project for MSBuildBasedProjects) directly, you will have to lock on this object.
/// </summary>
public object SyncRoot {
get { return syncRoot; }
}
/// <summary>
/// Gets the solution this project belongs to. Returns null for projects that are not (yet) added to
/// any solution; or are added to a solution folder that is not added to any solution.
/// </summary>
[Browsable(false)] [Browsable(false)]
public virtual Solution ParentSolution { public virtual Solution ParentSolution {
get { get {
if (parent != null) lock (syncRoot) {
return parent.ParentSolution; if (parent != null)
else return parent.ParentSolution;
return null; else
return null;
}
} }
} }
@ -68,7 +85,9 @@ namespace ICSharpCode.SharpDevelop.Project
return parent; return parent;
} }
set { set {
parent = value; lock (syncRoot) {
parent = value;
}
} }
} }

19
src/Main/Base/Project/Src/Project/Solution/ISolutionFolder.cs

@ -10,15 +10,32 @@ using System;
namespace ICSharpCode.SharpDevelop.Project namespace ICSharpCode.SharpDevelop.Project
{ {
/// <summary> /// <summary>
/// Description of ISolutionFolderContainer. /// An project-like entry in a solution, for example a solution folder or a project.
/// Implementing classes are required to implement this interface in a thread-safe manner.
/// Thread-safe members lock on the SyncRoot. Non-thread-safe members may only be called from the main thread.
/// </summary> /// </summary>
public interface ISolutionFolder public interface ISolutionFolder
{ {
/// <summary>
/// Gets the object used for thread-safe synchronization.
/// Thread-safe members lock on this object, but if you manipulate underlying structures
/// (such as the MSBuild project for MSBuildBasedProjects) directly, you will have to lock on this object.
/// </summary>
object SyncRoot {
get;
}
/// <summary>
/// Gets/Sets the container that contains this folder. This member is thread-safe.
/// </summary>
ISolutionFolderContainer Parent { ISolutionFolderContainer Parent {
get; get;
set; set;
} }
/// <summary>
/// Gets the solution the solution folder/project belongs to. This member is thread-safe.
/// </summary>
Solution ParentSolution { Solution ParentSolution {
get; get;
} }

Loading…
Cancel
Save