From 8014afcc3ee44c4068265fbbf293024f90a22ee2 Mon Sep 17 00:00:00 2001 From: John Hurliman Date: Sat, 16 May 2009 01:34:41 +0000 Subject: [PATCH] * Added ReaderWriterLockSlim.cs to OpenMetaverseTypes from the Mono SVN * Prebuild will now define the VISUAL_STUDIO symbol in .csproj files while leaving NAnt files alone * A conditional was added to use the .NET ReaderWriterLockSlim for VS compiles and the one in OpenMetaverseTypes for Mono compiles. This is a kludge until everyone is running Mono 2.6 or later (hah) git-svn-id: http://libopenmetaverse.googlecode.com/svn/libopenmetaverse/trunk@2769 52acb1d6-8a22-11de-b505-999d5b087335 --- OpenMetaverse/UDPBase.cs | 26 +- OpenMetaverseTypes/ReaderWriterLockSlim.cs | 618 ++++++++++++++++++ .../src/Core/Targets/VSGenericTarget.cs | 6 +- bin/Prebuild.exe | Bin 262144 -> 262144 bytes 4 files changed, 636 insertions(+), 14 deletions(-) create mode 100644 OpenMetaverseTypes/ReaderWriterLockSlim.cs diff --git a/OpenMetaverse/UDPBase.cs b/OpenMetaverse/UDPBase.cs index 5b83b7b2..10bc2da5 100644 --- a/OpenMetaverse/UDPBase.cs +++ b/OpenMetaverse/UDPBase.cs @@ -30,6 +30,12 @@ using System.Net; using System.Net.Sockets; using System.Threading; +#if VISUAL_STUDIO +using ReaderWriterLockImpl = System.Threading.ReaderWriterLockSlim; +#else +using ReaderWriterLockImpl = OpenMetaverse.ReaderWriterLockSlim; +#endif + namespace OpenMetaverse { /// @@ -56,7 +62,7 @@ namespace OpenMetaverse // wait until all outstanding operations are completed before shutting down. // this avoids the problem of closing the socket with outstanding operations // and trying to catch the inevitable ObjectDisposedException. - private ReaderWriterLockSlim rwLock = new ReaderWriterLockSlim(); + private ReaderWriterLockImpl rwLock = new ReaderWriterLockImpl(); // number of outstanding operations. This is a reference count // which we use to ensure that the threads exit cleanly. Note that @@ -219,17 +225,14 @@ namespace OpenMetaverse private void AsyncEndReceive(IAsyncResult iar) { - bool unlock = false; + bool dolock = false; // Asynchronous receive operations will complete here through the call // to AsyncBeginReceive // aquire a reader lock - if (rwLock.RecursiveReadCount == 0) - { - rwLock.EnterReadLock(); - unlock = true; - } + dolock = rwLock.IsReadLockHeld; + if (dolock) rwLock.EnterReadLock(); if (!shutdownFlag) { @@ -252,8 +255,7 @@ namespace OpenMetaverse Interlocked.Decrement(ref rwOperationCount); // we're done with the socket, release the reader lock - if (unlock) - rwLock.ExitReadLock(); + if (dolock) rwLock.ExitReadLock(); // call the abstract method PacketReceived(), passing the buffer that // has just been filled from the socket read. @@ -265,8 +267,7 @@ namespace OpenMetaverse Interlocked.Decrement(ref rwOperationCount); // we're done with the socket for now, release the reader lock. - if (unlock) - rwLock.ExitReadLock(); + if (dolock) rwLock.ExitReadLock(); } finally { @@ -278,8 +279,7 @@ namespace OpenMetaverse // nothing bad happened, but we are done with the operation // decrement the reference count and release the reader lock Interlocked.Decrement(ref rwOperationCount); - if (unlock) - rwLock.ExitReadLock(); + if (dolock) rwLock.ExitReadLock(); } } diff --git a/OpenMetaverseTypes/ReaderWriterLockSlim.cs b/OpenMetaverseTypes/ReaderWriterLockSlim.cs new file mode 100644 index 00000000..601ac5d2 --- /dev/null +++ b/OpenMetaverseTypes/ReaderWriterLockSlim.cs @@ -0,0 +1,618 @@ +// +// System.Threading.ReaderWriterLockSlim.cs +// +// Authors: +// Miguel de Icaza (miguel@novell.com) +// Dick Porter (dick@ximian.com) +// Jackson Harper (jackson@ximian.com) +// Lluis Sanchez Gual (lluis@ximian.com) +// Marek Safar (marek.safar@gmail.com) +// +// Copyright 2004-2008 Novell, Inc (http://www.novell.com) +// Copyright 2003, Ximian, Inc. +// +// NoRecursion code based on the blog post from Vance Morrison: +// http://blogs.msdn.com/vancem/archive/2006/03/28/563180.aspx +// +// Recursion code based on Mono's implementation of ReaderWriterLock. +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Security.Permissions; +using System.Diagnostics; +using System.Runtime.Serialization; +using System.Threading; + +namespace OpenMetaverse +{ + [Serializable] + public class LockRecursionException : Exception + { + public LockRecursionException() + : base() + { + } + + public LockRecursionException(string message) + : base(message) + { + } + + public LockRecursionException(string message, Exception e) + : base(message, e) + { + } + + protected LockRecursionException(SerializationInfo info, StreamingContext sc) + : base(info, sc) + { + } + } + + // + // This implementation is based on the light-weight + // Reader/Writer lock sample from Vance Morrison's blog: + // + // http://blogs.msdn.com/vancem/archive/2006/03/28/563180.aspx + // + // And in Mono's ReaderWriterLock + // + [HostProtectionAttribute(SecurityAction.LinkDemand, MayLeakOnAbort = true)] + [HostProtectionAttribute(SecurityAction.LinkDemand, Synchronization = true, ExternalThreading = true)] + public class ReaderWriterLockSlim : IDisposable + { + sealed class LockDetails + { + public int ThreadId; + public int ReadLocks; + } + + // Are we on a multiprocessor? + static readonly bool smp; + + // Lock specifiation for myLock: This lock protects exactly the local fields associted + // instance of MyReaderWriterLock. It does NOT protect the memory associted with the + // the events that hang off this lock (eg writeEvent, readEvent upgradeEvent). + int myLock; + + // Who owns the lock owners > 0 => readers + // owners = -1 means there is one writer, Owners must be >= -1. + int owners; + Thread upgradable_thread; + Thread write_thread; + + // These variables allow use to avoid Setting events (which is expensive) if we don't have to. + uint numWriteWaiters; // maximum number of threads that can be doing a WaitOne on the writeEvent + uint numReadWaiters; // maximum number of threads that can be doing a WaitOne on the readEvent + uint numUpgradeWaiters; // maximum number of threads that can be doing a WaitOne on the upgradeEvent (at most 1). + + // conditions we wait on. + EventWaitHandle writeEvent; // threads waiting to aquire a write lock go here. + EventWaitHandle readEvent; // threads waiting to aquire a read lock go here (will be released in bulk) + EventWaitHandle upgradeEvent; // thread waiting to upgrade a read lock to a write lock go here (at most one) + + //int lock_owner; + + // Only set if we are a recursive lock + //Dictionary reader_locks; + + LockDetails[] read_locks = new LockDetails[8]; + + static ReaderWriterLockSlim() + { + smp = Environment.ProcessorCount > 1; + } + + public ReaderWriterLockSlim() + { + // NoRecursion (0) is the default value + } + + public void EnterReadLock() + { + TryEnterReadLock(-1); + } + + public bool TryEnterReadLock(int millisecondsTimeout) + { + if (millisecondsTimeout < Timeout.Infinite) + throw new ArgumentOutOfRangeException("millisecondsTimeout"); + + if (read_locks == null) + throw new ObjectDisposedException(null); + + if (Thread.CurrentThread == write_thread) + throw new LockRecursionException("Read lock cannot be acquired while write lock is held"); + + EnterMyLock(); + + LockDetails ld = GetReadLockDetails(Thread.CurrentThread.ManagedThreadId, true); + if (ld.ReadLocks != 0) + { + ExitMyLock(); + throw new LockRecursionException("Recursive read lock can only be aquired in SupportsRecursion mode"); + } + ++ld.ReadLocks; + + while (true) + { + // Easy case, no contention + // owners >= 0 means there might be readers (but no writer) + if (owners >= 0 && numWriteWaiters == 0) + { + owners++; + break; + } + + // If the request is to probe. + if (millisecondsTimeout == 0) + { + ExitMyLock(); + return false; + } + + // We need to wait. Mark that we have waiters and wait. + if (readEvent == null) + { + LazyCreateEvent(ref readEvent, false); + // since we left the lock, start over. + continue; + } + + if (!WaitOnEvent(readEvent, ref numReadWaiters, millisecondsTimeout)) + return false; + } + ExitMyLock(); + + return true; + } + + public bool TryEnterReadLock(TimeSpan timeout) + { + return TryEnterReadLock(CheckTimeout(timeout)); + } + + // + // TODO: What to do if we are releasing a ReadLock and we do not own it? + // + public void ExitReadLock() + { + EnterMyLock(); + + if (owners < 1) + { + ExitMyLock(); + throw new SynchronizationLockException("Releasing lock and no read lock taken"); + } + + --owners; + --GetReadLockDetails(Thread.CurrentThread.ManagedThreadId, false).ReadLocks; + + ExitAndWakeUpAppropriateWaiters(); + } + + public void EnterWriteLock() + { + TryEnterWriteLock(-1); + } + + public bool TryEnterWriteLock(int millisecondsTimeout) + { + if (millisecondsTimeout < Timeout.Infinite) + throw new ArgumentOutOfRangeException("millisecondsTimeout"); + + if (read_locks == null) + throw new ObjectDisposedException(null); + + if (IsWriteLockHeld) + throw new LockRecursionException(); + + EnterMyLock(); + + LockDetails ld = GetReadLockDetails(Thread.CurrentThread.ManagedThreadId, false); + if (ld != null && ld.ReadLocks > 0) + { + ExitMyLock(); + throw new LockRecursionException("Write lock cannot be acquired while read lock is held"); + } + + while (true) + { + // There is no contention, we are done + if (owners == 0) + { + // Indicate that we have a writer + owners = -1; + write_thread = Thread.CurrentThread; + break; + } + + // If we are the thread that took the Upgradable read lock + if (owners == 1 && upgradable_thread == Thread.CurrentThread) + { + owners = -1; + write_thread = Thread.CurrentThread; + break; + } + + // If the request is to probe. + if (millisecondsTimeout == 0) + { + ExitMyLock(); + return false; + } + + // We need to wait, figure out what kind of waiting. + + if (upgradable_thread == Thread.CurrentThread) + { + // We are the upgradable thread, register our interest. + + if (upgradeEvent == null) + { + LazyCreateEvent(ref upgradeEvent, false); + + // since we left the lock, start over. + continue; + } + + if (numUpgradeWaiters > 0) + { + ExitMyLock(); + throw new ApplicationException("Upgrading lock to writer lock already in process, deadlock"); + } + + if (!WaitOnEvent(upgradeEvent, ref numUpgradeWaiters, millisecondsTimeout)) + return false; + } + else + { + if (writeEvent == null) + { + LazyCreateEvent(ref writeEvent, true); + + // since we left the lock, retry + continue; + } + if (!WaitOnEvent(writeEvent, ref numWriteWaiters, millisecondsTimeout)) + return false; + } + } + + Debug.Assert(owners == -1, "Owners is not -1"); + ExitMyLock(); + return true; + } + + public bool TryEnterWriteLock(TimeSpan timeout) + { + return TryEnterWriteLock(CheckTimeout(timeout)); + } + + public void ExitWriteLock() + { + EnterMyLock(); + + if (owners != -1) + { + ExitMyLock(); + throw new SynchronizationLockException("Calling ExitWriterLock when no write lock is held"); + } + + //Debug.Assert (numUpgradeWaiters > 0); + write_thread = upgradable_thread = null; + owners = 0; + ExitAndWakeUpAppropriateWaiters(); + } + + public void EnterUpgradeableReadLock() + { + TryEnterUpgradeableReadLock(-1); + } + + // + // Taking the Upgradable read lock is like taking a read lock + // but we limit it to a single upgradable at a time. + // + public bool TryEnterUpgradeableReadLock(int millisecondsTimeout) + { + if (millisecondsTimeout < Timeout.Infinite) + throw new ArgumentOutOfRangeException("millisecondsTimeout"); + + if (read_locks == null) + throw new ObjectDisposedException(null); + + if (IsUpgradeableReadLockHeld) + throw new LockRecursionException(); + + if (IsWriteLockHeld) + throw new LockRecursionException(); + + EnterMyLock(); + while (true) + { + if (owners == 0 && numWriteWaiters == 0 && upgradable_thread == null) + { + owners++; + upgradable_thread = Thread.CurrentThread; + break; + } + + // If the request is to probe + if (millisecondsTimeout == 0) + { + ExitMyLock(); + return false; + } + + if (readEvent == null) + { + LazyCreateEvent(ref readEvent, false); + // since we left the lock, start over. + continue; + } + + if (!WaitOnEvent(readEvent, ref numReadWaiters, millisecondsTimeout)) + return false; + } + + ExitMyLock(); + return true; + } + + public bool TryEnterUpgradeableReadLock(TimeSpan timeout) + { + return TryEnterUpgradeableReadLock(CheckTimeout(timeout)); + } + + public void ExitUpgradeableReadLock() + { + EnterMyLock(); + Debug.Assert(owners > 0, "Releasing an upgradable lock, but there was no reader!"); + --owners; + upgradable_thread = null; + ExitAndWakeUpAppropriateWaiters(); + } + + public void Dispose() + { + read_locks = null; + } + + public bool IsReadLockHeld + { + get { return RecursiveReadCount != 0; } + } + + public bool IsWriteLockHeld + { + get { return RecursiveWriteCount != 0; } + } + + public bool IsUpgradeableReadLockHeld + { + get { return RecursiveUpgradeCount != 0; } + } + + public int CurrentReadCount + { + get { return owners & 0xFFFFFFF; } + } + + public int RecursiveReadCount + { + get + { + EnterMyLock(); + LockDetails ld = GetReadLockDetails(Thread.CurrentThread.ManagedThreadId, false); + int count = ld == null ? 0 : ld.ReadLocks; + ExitMyLock(); + return count; + } + } + + public int RecursiveUpgradeCount + { + get { return upgradable_thread == Thread.CurrentThread ? 1 : 0; } + } + + public int RecursiveWriteCount + { + get { return write_thread == Thread.CurrentThread ? 1 : 0; } + } + + public int WaitingReadCount + { + get { return (int)numReadWaiters; } + } + + public int WaitingUpgradeCount + { + get { return (int)numUpgradeWaiters; } + } + + public int WaitingWriteCount + { + get { return (int)numWriteWaiters; } + } + + #region Private methods + void EnterMyLock() + { + if (Interlocked.CompareExchange(ref myLock, 1, 0) != 0) + EnterMyLockSpin(); + } + + void EnterMyLockSpin() + { + + for (int i = 0; ; i++) + { + if (i < 3 && smp) + Thread.SpinWait(20); // Wait a few dozen instructions to let another processor release lock. + else + Thread.Sleep(0); // Give up my quantum. + + if (Interlocked.CompareExchange(ref myLock, 1, 0) == 0) + return; + } + } + + void ExitMyLock() + { + Debug.Assert(myLock != 0, "Exiting spin lock that is not held"); + myLock = 0; + } + + bool MyLockHeld { get { return myLock != 0; } } + + /// + /// Determines the appropriate events to set, leaves the locks, and sets the events. + /// + private void ExitAndWakeUpAppropriateWaiters() + { + Debug.Assert(MyLockHeld); + + // First a writing thread waiting on being upgraded + if (owners == 1 && numUpgradeWaiters != 0) + { + // Exit before signaling to improve efficiency (wakee will need the lock) + ExitMyLock(); + // release all upgraders (however there can be at most one). + upgradeEvent.Set(); + // + // TODO: What does the following comment mean? + // two threads upgrading is a guarenteed deadlock, so we throw in that case. + } + else if (owners == 0 && numWriteWaiters > 0) + { + // Exit before signaling to improve efficiency (wakee will need the lock) + ExitMyLock(); + // release one writer. + writeEvent.Set(); + } + else if (owners >= 0 && numReadWaiters != 0) + { + // Exit before signaling to improve efficiency (wakee will need the lock) + ExitMyLock(); + // release all readers. + readEvent.Set(); + } + else + ExitMyLock(); + } + + /// + /// A routine for lazily creating a event outside the lock (so if errors + /// happen they are outside the lock and that we don't do much work + /// while holding a spin lock). If all goes well, reenter the lock and + /// set 'waitEvent' + /// + void LazyCreateEvent(ref EventWaitHandle waitEvent, bool makeAutoResetEvent) + { + Debug.Assert(MyLockHeld); + Debug.Assert(waitEvent == null); + + ExitMyLock(); + EventWaitHandle newEvent; + if (makeAutoResetEvent) + newEvent = new AutoResetEvent(false); + else + newEvent = new ManualResetEvent(false); + + EnterMyLock(); + + // maybe someone snuck in. + if (waitEvent == null) + waitEvent = newEvent; + } + + /// + /// Waits on 'waitEvent' with a timeout of 'millisceondsTimeout. + /// Before the wait 'numWaiters' is incremented and is restored before leaving this routine. + /// + bool WaitOnEvent(EventWaitHandle waitEvent, ref uint numWaiters, int millisecondsTimeout) + { + Debug.Assert(MyLockHeld); + + waitEvent.Reset(); + numWaiters++; + + bool waitSuccessful = false; + + // Do the wait outside of any lock + ExitMyLock(); + try + { + waitSuccessful = waitEvent.WaitOne(millisecondsTimeout, false); + } + finally + { + EnterMyLock(); + --numWaiters; + if (!waitSuccessful) + ExitMyLock(); + } + return waitSuccessful; + } + + static int CheckTimeout(TimeSpan timeout) + { + try + { + return checked((int)timeout.TotalMilliseconds); + } + catch (System.OverflowException) + { + throw new ArgumentOutOfRangeException("timeout"); + } + } + + LockDetails GetReadLockDetails(int threadId, bool create) + { + int i; + LockDetails ld; + for (i = 0; i < read_locks.Length; ++i) + { + ld = read_locks[i]; + if (ld == null) + break; + + if (ld.ThreadId == threadId) + return ld; + } + + if (!create) + return null; + + if (i == read_locks.Length) + Array.Resize(ref read_locks, read_locks.Length * 2); + + ld = read_locks[i] = new LockDetails(); + ld.ThreadId = threadId; + return ld; + } + #endregion + } +} diff --git a/Programs/Prebuild/src/Core/Targets/VSGenericTarget.cs b/Programs/Prebuild/src/Core/Targets/VSGenericTarget.cs index 0a57d33f..6b9308c7 100644 --- a/Programs/Prebuild/src/Core/Targets/VSGenericTarget.cs +++ b/Programs/Prebuild/src/Core/Targets/VSGenericTarget.cs @@ -249,6 +249,10 @@ namespace Prebuild.Core.Targets foreach (ConfigurationNode conf in project.Configurations) { + string compilerDefines = conf.Options["CompilerDefines"].ToString(); + if (compilerDefines != String.Empty) compilerDefines += ";"; + compilerDefines += "VISUAL_STUDIO"; + ps.Write(" ", conf.Name); ps.WriteLine(" {0}", conf.Options["AllowUnsafe"]); @@ -256,7 +260,7 @@ namespace Prebuild.Core.Targets ps.WriteLine(" {0}", conf.Options["CheckUnderflowOverflow"]); ps.WriteLine(" "); ps.WriteLine(" "); - ps.WriteLine(" {0}", conf.Options["CompilerDefines"]); + ps.WriteLine(" {0}", compilerDefines); ps.WriteLine(" {0}", Helper.NormalizePath(conf.Options["XmlDocFile"].ToString())); ps.WriteLine(" {0}", conf.Options["DebugInformation"]); ps.WriteLine(" {0}", conf.Options["FileAlignment"]); diff --git a/bin/Prebuild.exe b/bin/Prebuild.exe index ed424a195362ca1226d372deff186414a065ce95..dcd1f342fd6ba2974a6481821d994b59eca6bbb4 100644 GIT binary patch delta 16206 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 qr$_^mO)|+--s%T=#lXt;N