From 02b0d47d154bf7e1cc33e184ae7a97ce050900a6 Mon Sep 17 00:00:00 2001 From: Daniel Grunwald Date: Mon, 27 Apr 2009 13:58:26 +0000 Subject: [PATCH] Use a Mutex when accessing SharpDevelopProperties.xml Prevents IOException when shutting down multiple SharpDevelop instances at the same time. git-svn-id: svn://svn.sharpdevelop.net/sharpdevelop/branches/3.0@4029 1ccf3a8d-04fe-1044-b7c0-cef0b8235c61 --- src/Main/Core/Project/ICSharpCode.Core.csproj | 1 + .../PropertyService/PropertyService.cs | 42 +++++++++++++++---- .../Project/Src/Util/CallbackOnDispose.cs | 35 ++++++++++++++++ 3 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 src/Main/Core/Project/Src/Util/CallbackOnDispose.cs diff --git a/src/Main/Core/Project/ICSharpCode.Core.csproj b/src/Main/Core/Project/ICSharpCode.Core.csproj index 3e96226f01..fbffc31f55 100644 --- a/src/Main/Core/Project/ICSharpCode.Core.csproj +++ b/src/Main/Core/Project/ICSharpCode.Core.csproj @@ -99,6 +99,7 @@ + diff --git a/src/Main/Core/Project/Src/Services/PropertyService/PropertyService.cs b/src/Main/Core/Project/Src/Services/PropertyService/PropertyService.cs index cc8256f370..2aa3555754 100644 --- a/src/Main/Core/Project/Src/Services/PropertyService/PropertyService.cs +++ b/src/Main/Core/Project/Src/Services/PropertyService/PropertyService.cs @@ -8,6 +8,7 @@ using System; using System.IO; using System.Text; +using System.Threading; using System.Xml; namespace ICSharpCode.Core @@ -90,12 +91,14 @@ namespace ICSharpCode.Core return false; } try { - using (XmlTextReader reader = new XmlTextReader(fileName)) { - while (reader.Read()){ - if (reader.IsStartElement()) { - if (reader.LocalName == propertyXmlRootNodeName) { - properties.ReadProperties(reader, propertyXmlRootNodeName); - return true; + using (LockPropertyFile()) { + using (XmlTextReader reader = new XmlTextReader(fileName)) { + while (reader.Read()){ + if (reader.IsStartElement()) { + if (reader.LocalName == propertyXmlRootNodeName) { + properties.ReadProperties(reader, propertyXmlRootNodeName); + return true; + } } } } @@ -108,15 +111,38 @@ namespace ICSharpCode.Core public static void Save() { - string fileName = Path.Combine(configDirectory, propertyFileName); - using (XmlTextWriter writer = new XmlTextWriter(fileName, Encoding.UTF8)) { + using (MemoryStream ms = new MemoryStream()) { + XmlTextWriter writer = new XmlTextWriter(ms, Encoding.UTF8); writer.Formatting = Formatting.Indented; writer.WriteStartElement(propertyXmlRootNodeName); properties.WriteProperties(writer); writer.WriteEndElement(); + writer.Flush(); + + ms.Position = 0; + string fileName = Path.Combine(configDirectory, propertyFileName); + using (LockPropertyFile()) { + using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write, FileShare.None)) { + ms.WriteTo(fs); + } + } } } + /// + /// Acquires an exclusive lock on the properties file so that it can be opened safely. + /// + public static IDisposable LockPropertyFile() + { + Mutex mutex = new Mutex(false, "PropertyServiceSave-30F32619-F92D-4BC0-BF49-AA18BF4AC313"); + mutex.WaitOne(); + return new CallbackOnDispose( + delegate { + mutex.ReleaseMutex(); + mutex.Close(); + }); + } + static void PropertiesPropertyChanged(object sender, PropertyChangedEventArgs e) { if (PropertyChanged != null) { diff --git a/src/Main/Core/Project/Src/Util/CallbackOnDispose.cs b/src/Main/Core/Project/Src/Util/CallbackOnDispose.cs new file mode 100644 index 0000000000..1db64aa893 --- /dev/null +++ b/src/Main/Core/Project/Src/Util/CallbackOnDispose.cs @@ -0,0 +1,35 @@ +// +// +// +// +// $Revision$ +// + +using System; +using System.Threading; + +namespace ICSharpCode.Core +{ + /// + /// Invokes a callback when this class is dispsed. + /// + sealed class CallbackOnDispose : IDisposable + { + // TODO: in 4.0, use System.Action and make this class public + System.Threading.ThreadStart callback; + + public CallbackOnDispose(System.Threading.ThreadStart callback) + { + if (callback == null) + throw new ArgumentNullException("callback"); + this.callback = callback; + } + + public void Dispose() + { + System.Threading.ThreadStart action = Interlocked.Exchange(ref callback, null); + if (action != null) + action(); + } + } +}