//private static MUDLock CreateMUDLock(object forObject) //{ // MUDLock newLock = new MUDLock(forObject); // lock (WaitUntilNoThreads) // { // List<MUDLock> threadLocks; // if(!Locks.TryGetValue(Thread.CurrentThread, out threadLocks)) // { // Locks[Thread.CurrentThread] = threadLocks = new List<MUDLock>(); // } // threadLocks.Add(newLock); // WaitUntilNoThreads.Set(); // } // return newLock; //} /// <summary> /// Provides a unique ReaderWriterLockSlim for any object, guaranteed. /// </summary> /// <param name="forObject">object to get an associated lock object for.</param> /// <returns></returns> //private static ReaderWriterLockSlim GetRWLock(object forObject) //{ // //if (forObject == null) return null; // ILockable asLockable = forObject as ILockable; // ReaderWriterLockSlim rwLock; // if (asLockable != null) // { // rwLock = asLockable.Lock; // if (rwLock == null) { asLockable.Lock = rwLock = new ReaderWriterLockSlim(); } // return rwLock; // } // //TODO: There should be a warning logged here. Ideally no object should need custom lock mutexes. Relying on this causes performance degredation. // lock (CustomLocks) // { // if (!CustomLocks.TryGetValue(forObject, out rwLock)) // { // rwLock = new ReaderWriterLockSlim(); // CustomLocks.Add(forObject, rwLock); // } // } // return rwLock; //} //private static MUDLock GetMUDLock(object forObject, int timeout, bool ignorePause) //{ // ReaderWriterLockSlim lockObject = GetRWLock(forObject); // if (ignorePause) // { // if (!lockObject.TryEnterWriteLock(timeout)) // return default(MUDLock); // return CreateMUDLock(forObject); // } // else // { // DateTime start = DateTime.Now; // int nextTimeout = timeout; // while (true) // { // if (!lockObject.TryEnterWriteLock(nextTimeout)) // return default(MUDLock); // lock (WaitUntilUnpaused) // { // if (MUDIsPaused) goto tryPause; // return CreateMUDLock(forObject); // } // tryPause: // //leave timeout to -1 if it was that before, or subtract the timespan used but not below 0. // nextTimeout = Math.Max(Math.Min(timeout, 0), timeout - (int)TimeSpan.FromTicks(DateTime.Now.Ticks - start.Ticks).TotalMilliseconds); // if (!WaitUntilUnpaused.WaitOne(nextTimeout)) // return default(MUDLock); // nextTimeout = Math.Max(Math.Min(timeout, 0), timeout - (int)TimeSpan.FromTicks(DateTime.Now.Ticks - start.Ticks).TotalMilliseconds); // } // } //} /// <summary> /// This should be called with a using block or a similar dispose pattern. /// Get a lock for a specific event and resources related to that event. Returns a RoomEvent if successful, or null if /// it failed to get the lock. /// </summary> /// <param name="forRoom"></param> /// <param name="baseEvent"></param> /// <param name="timeout">-1 to wait forever. 0 to not wait at all. Otherwise milliseconds to wait.</param> /// <param name="ignorePause">Allow this to get a lock even if the MUD is paused. This probably should never be used.</param> /// <returns></returns> public static RoomEvent StartEvent(Room forRoom, RoomEvent baseEvent, int timeout = ThreadManager.DefaultTimeout, bool?ignorePause = null) { DateTime startTime = DateTime.UtcNow; if (baseEvent == null) { throw new ArgumentNullException("baseEvent"); //TODO: Clean this up. } //baseEvent = new SimpleRoomEvent(); //Skip MUDIsPaused check if this thread is already modifying MUD state. bool skipThisPause = (ignorePause == null ? LockableLockGroup.HasALock() : ignorePause.Value); IDisposable MUDLock = null; LockableLock foundLock = null; try { MUDLock = skipThisPause ? GetMUDLockIgnorePause() : GetMUDLock(startTime.RemainingTimeout(timeout)); if (MUDLock != null) { foundLock = forRoom.EnterLock(baseEvent, startTime.RemainingTimeout(timeout)); if (foundLock == null) //Failed to get a lock. { return(null); } baseEvent.SetLockToDispose(foundLock); foundLock = null; //Important weird thing: This is basically 'passing off' the lock from this function to baseEvent. //Setting MUDLock to null prevents it being disposed of right now, and when baseEvent is disposed //of later it will do the same cleanup MUDLock would have done. MUDLock = null; return(baseEvent); } } finally { if (MUDLock != null) { MUDLock.Dispose(); } if (foundLock != null) { foundLock.Dispose(); } } return(null); }
/// <summary> /// Call this with a using() statement, for the first resource/Lockable an event needs. /// Get and set up the current DeadlockHandler for this Lockable. Caller is expected to continue collecting Lockables /// with AddResource. /// </summary> /// <param name="objectToLock"></param> /// <param name="forEvent"></param> /// <returns></returns> public static LockableLock EnterLock(this ILockable objectToLock, ILockHolder forEvent, int timeout = System.Threading.Timeout.Infinite) { return(LockableLockGroup.EnterLock(objectToLock, forEvent, timeout)); }