示例#1
0
        /// <summary>
        /// Add an event to the event queue.
        /// </summary>
        /// <param name="newEvent"></param>
        private LockableLock addEvent(ILockHolder newEvent)
        {
            LockableLock newLock = new LockableLock(newEvent, this);

            eventQueue.Add(newLock);
            return(newLock);
        }
示例#2
0
        /// <summary>
        /// Compare this event's priority to the other group's priority. Finds and returns the highest priority event.
        /// </summary>
        /// <param name="previousHighest"></param>
        /// <param name="otherGroup">MUST HAVE THE LOCK ON THIS GROUP'S MUTEX</param>
        /// <returns></returns>
        private LockableLock chooseHighestPriority(LockableLock previousHighest, LockableLockGroup otherGroup)
        {
            LockableLock otherHighest = otherGroup.highestPriority();
            int          priority     = previousHighest.comparePriority(otherHighest);

            if (priority == 0)
            {
                if (otherGroup.subSubPriority == -1)
                {
                    otherGroup.subSubPriority = Interlocked.Increment(ref LockableLockGroup.PriorityChooser);
                }
                if (previousHighest.group.subSubPriority == -1)
                {
                    //Previous event doesn't have a priority yet and will get a new value later, which must be higher than otherGroup's value.
                    //So the other event has priority.
                    priority = -1;
                }
                else
                {
                    priority = otherHighest.group.subSubPriority - previousHighest.group.subSubPriority;
                }
            }
            if (priority < 0)
            {
                return(otherHighest);
            }
            return(previousHighest);
        }
示例#3
0
 /// <summary>
 /// Make sure an event contains a subgroup. Does nothing if it already does.
 /// Handles interrupts for events in that subgroup, and also interrupts for other groups using that subgroup.
 /// </summary>
 /// <param name="currentEvent">Event to add the subgroup to. Must be part of this lock group.</param>
 /// <param name="newSubgroup">Subgroup to try to add to the event.</param>
 private void addToLockableLock(LockableLock currentEvent, LockableLockGroup newSubgroup)
 {
     lock (statusMutex)
     {
         if (currentEvent.ownedSubgroups == null)
         {
             currentEvent.ownedSubgroups = new List <LockableLockGroup>();
         }
         if (currentEvent.ownedSubgroups.Contains(newSubgroup))
         {
             return;
         }
         //Don't have the lock on other lock groups, but that's okay, because they already said they are waiting
         //and this will be the thread that wakes them up, so they won't modify themselves right now.
         currentEvent.ownedSubgroups.Add(newSubgroup);
     }
     foreach (LockableLock otherEvent in newSubgroup.eventQueue)
     {
         currentEvent.holder.InterruptOtherEvent(otherEvent.holder, false);
         otherEvent.holder.RespondToInterrupt(currentEvent.holder, false);
     }
     foreach (LockableLockGroup interruptedGroup in containedGroups(true))
     {
         interruptedGroup.interruptAllWithGroup(newSubgroup, currentEvent.holder);
     }
 }
示例#4
0
        /// <summary>
        /// Get the highest priority lock in this lock group.
        /// </summary>
        /// <returns></returns>
        private LockableLock highestPriority()
        {
            LockableLock highestPriority = this.eventQueue[0];

            for (int i = 0; i < eventQueue.Count; i++)
            {
                LockableLock otherEvent = eventQueue[i];
                if (highestPriority.comparePriority(otherEvent) < 0)
                {
                    highestPriority = otherEvent;
                }
            }
            return(highestPriority);
        }
示例#5
0
        /// <summary>
        /// </summary>
        /// <param name="otherEvent"></param>
        /// <returns>Positive if this event has priority. Negative if other event has priority. 0 if the events have the same priority.</returns>
        internal int comparePriority(LockableLock otherEvent)
        {
            if (otherEvent == this)
            {
                return(0);
            }
            int priority      = holder.LockPriority;
            int otherPriority = otherEvent.holder.LockPriority;

            if (priority != otherPriority)
            {
                return(priority - otherPriority);
            }
            long ticks = (otherEvent.subPriority - subPriority).Ticks;

            if (ticks != 0)
            {
                return(ticks > 0 ? 1 : -1);
            }
            return(0);
        }
示例#6
0
        /// <summary>
        /// Dispose of this lock.
        /// </summary>
        internal void DisposeOf(LockableLock oldLock)
        {
            lock (statusMutex)
            {
                if (LockableLockGroup.CurrentLockGroup != this)
                {
                    throw new SynchronizationLockException("This thread is not currently using this lock group.");
                }

                if (!eventQueue.Remove(oldLock))
                {
                    throw new SynchronizationLockException("This thread is not currently using this lock group.");
                }

                if (eventQueue.Count == 0)
                {
                    LockableLockGroup.CurrentLockGroup = null;
                    Monitor.PulseAll(statusMutex);
                }
            }
        }
示例#7
0
        /// <summary>
        /// Get the lock for the requested resource. This may block if the resource is locked by another thread.
        /// This will avoid deadlocks in case of multiple threads locking the same resources; one thread will allow
        /// another thread to take its resources and interrupt it depending on event priority.
        /// </summary>
        /// <param name="resource">Resource to get the lock on</param>
        /// <param name="newLock">Returned lock that represents the event's lock on this resource.</param>
        internal void AddResource(ILockable resource, LockableLock newLock)
        {
            LockableLockGroup otherGroup;

            //LockableLock currentEvent = eventQueue[eventQueue.Count - 1];
restartAddResource:
            LockableLock highestPriority = null;
            bool wakeHighestPriority = false;

            //Check if we can trivially get the lock
            lock (resource.LockMutex)
            {
                otherGroup = resource.CurrentLock;
                if (otherGroup == null || otherGroup.eventQueue.Count == 0)
                {
                    resource.CurrentLock = this;
                    return;
                }
            }
            //Check if we already have the lock
            if (this.contains(otherGroup, false))
            {
                //Already have the lock somewhere. Make sure *this* event also has it marked as being used.
                addToLockableLock(newLock, otherGroup);
                return;
            }

            //Check if we're in a deadlock.
            List <LockableLockGroup> otherGroupQueue;

            lock (otherGroup.statusMutex)
            {
                if (otherGroup.eventQueue.Count == 0)
                {
                    goto restartAddResource;
                }
                otherGroupQueue = new List <LockableLockGroup>();
                otherGroupQueue.Add(otherGroup);
            }

            lock (this.statusMutex)
            {
                this.waitingOn = otherGroupQueue[0];
                this.willWait  = true;
            }
            try
            {
                lock (otherGroup.statusMutex)
                {
                    while (otherGroup.waitingOn == null)
                    {
                        if (otherGroup.eventQueue.Count == 0)
                        {
                            goto restartAddResource;
                        }
                        //Other group is active. Have to wait for that group first.
                        Monitor.Wait(otherGroup.statusMutex);
                    }
                    if (otherGroup.eventQueue.Count == 0)
                    {
                        goto restartAddResource;
                    }
                    highestPriority = this.highestPriority();
                    otherGroupQueue.Add(otherGroup.waitingOn);
                    highestPriority = chooseHighestPriority(highestPriority, otherGroup);
                }
                //Waiting on a lock that is not active. Check to see what we should do.
                while (true)
                {
                    LockableLockGroup nextGroup = otherGroupQueue[otherGroupQueue.Count - 1];
                    lock (nextGroup.statusMutex)
                    {
                        //Make sure lock is up to date.
                        if (nextGroup.eventQueue.Count == 0)
                        {
                            //This event is out of date, meaning there is an active thread. We can wait until the group we're waiting
                            //on finishes, or another thread tells us we're the highest priority thread.
                            break;
                        }
                        //Lock isn't out of date. Check which group this is.
                        else if (this.contains(nextGroup, true))
                        {
                            //There is a loop/deadlock, eventually otherGroup is waiting on this group.
                            if (this.contains(highestPriority.group, true))
                            {
                                //We are the highest priority thread found. We get to take a new subgroup and continue getting locks.
                                goto setupSubgroup;
                            }
                            //else wake up the highestPriority thread.
                            wakeHighestPriority = true;
                            break;
                        }
                        LockableLockGroup nextNextGroup = nextGroup.waitingOn;
                        if (nextNextGroup == null)
                        {
                            //Not currently in a deadlock. Continue and wait on otherGroup.
                            break;
                        }
                        foreach (LockableLockGroup otherWaitingGroup in otherGroupQueue)
                        {
                            if (otherWaitingGroup == nextNextGroup)
                            {
                                //There's a circular loop, but this thread's not in it. Consider it the same as an active thread
                                break;
                            }
                        }
                        //This group is also waiting on a thread. Check the next event/group it is waiting on.
                        highestPriority = chooseHighestPriority(highestPriority, nextGroup);
                        otherGroupQueue.Add(nextGroup.waitingOn);
                        continue;
                    }
                }

                //Need to wait for another thread.
                if (wakeHighestPriority)
                {
                    //We're in a deadlock and not the highest priority thread. Make sure the highest priority thread is woken up.
                    object mutex;
                    lock (highestPriority.group.statusMutex)
                    {
                        //Tell that thread to wake up
                        highestPriority.group.willWait = false;
                        mutex = highestPriority.group.waitingOn?.statusMutex;
                    }

                    // If this is out of date, the highest priority thread already did stuff and we didn't need to wake it up anyways.
                    if (mutex != null)
                    {
                        lock (mutex)
                        {
                            Monitor.PulseAll(mutex);
                        }
                    }
                    wakeHighestPriority = false;
                }
                lock (otherGroup.statusMutex)
                {
                    //This isn't the lock for willWait, but the thread that would wake this thread up would clear willWait, then try to get this same lock, then wake it up, so it works out.
                    if (willWait && otherGroup.eventQueue.Count > 0)
                    {
                        Monitor.Wait(otherGroup.statusMutex);
                    }
                }
                goto restartAddResource;
            }
            finally
            {
                lock (this.statusMutex)
                {
                    this.waitingOn = null;
                    this.willWait  = false;
                }
            }
setupSubgroup:
            //This is the highest priority thread. Setup subgroups.
            addToLockableLock(newLock, otherGroup);
            return;
        }