From 7609daca388d1acbe8da7eaf407c0b3f4da1fd80 Mon Sep 17 00:00:00 2001 From: "Justin Clark-Casey (justincc)" Date: Tue, 31 Jul 2012 23:57:57 +0100 Subject: [PATCH] Resolve a deadlock between INPCModule and SensorRepeat by replacing the SensorRepeat list with a new list on add/removes rather than locking it for the duration of the sensor sweep. A deadlock was observed today where NPC removal on a script thread would lock the NPC list and then try to lock the sensor list via scripted attachment removal. Concurrently, the sensor sweep thread would lock the sensor list and then try to lock the NPC list to check NPC status. This commit resolves the deadlock by replacing the sensor list on update rather than locking it for the duration of the sweep. --- .../Implementation/Plugins/SensorRepeat.cs | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs index f2c8b6085a..06495bb1ba 100644 --- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs +++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/Plugins/SensorRepeat.cs @@ -51,8 +51,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins { get { - lock (SenseRepeatListLock) - return SenseRepeaters.Count; + return SenseRepeaters.Count; } } @@ -116,6 +115,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins public double distance; } + /// + /// Sensors to process. + /// + /// + /// Do not add or remove sensors from this list directly. Instead, copy the list and substitute the updated + /// copy. This is to avoid locking the list for the duration of the sensor sweep, which increases the danger + /// of deadlocks with future code updates. + /// + /// Always lock SenseRepeatListLock when updating this list. + /// private List SenseRepeaters = new List(); private object SenseRepeatListLock = new object(); @@ -125,6 +134,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins { // Always remove first, in case this is a re-set UnSetSenseRepeaterEvents(m_localID, m_itemID); + if (sec == 0) // Disabling timer return; @@ -144,9 +154,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins ts.host = host; ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); + + AddSenseRepeater(ts); + } + + private void AddSenseRepeater(SenseRepeatClass senseRepeater) + { lock (SenseRepeatListLock) { - SenseRepeaters.Add(ts); + List newSenseRepeaters = new List(SenseRepeaters); + newSenseRepeaters.Add(senseRepeater); + SenseRepeaters = newSenseRepeaters; } } @@ -155,39 +173,32 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins // Remove from timer lock (SenseRepeatListLock) { - List NewSensors = new List(); + List newSenseRepeaters = new List(); foreach (SenseRepeatClass ts in SenseRepeaters) { if (ts.localID != m_localID || ts.itemID != m_itemID) { - NewSensors.Add(ts); + newSenseRepeaters.Add(ts); } } - SenseRepeaters.Clear(); - SenseRepeaters = NewSensors; + + SenseRepeaters = newSenseRepeaters; } } public void CheckSenseRepeaterEvents() { - lock (SenseRepeatListLock) + // Go through all timers + foreach (SenseRepeatClass ts in SenseRepeaters) { - // Nothing to do here? - if (SenseRepeaters.Count == 0) - return; - - // Go through all timers - foreach (SenseRepeatClass ts in SenseRepeaters) + // Time has passed? + if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime()) { - // Time has passed? - if (ts.next.ToUniversalTime() < DateTime.Now.ToUniversalTime()) - { - SensorSweep(ts); - // set next interval - ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); - } + SensorSweep(ts); + // set next interval + ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); } - } // lock + } } public void SenseOnce(uint m_localID, UUID m_itemID, @@ -615,21 +626,19 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins { List data = new List(); - lock (SenseRepeatListLock) + foreach (SenseRepeatClass ts in SenseRepeaters) { - foreach (SenseRepeatClass ts in SenseRepeaters) + if (ts.itemID == itemID) { - if (ts.itemID == itemID) - { - data.Add(ts.interval); - data.Add(ts.name); - data.Add(ts.keyID); - data.Add(ts.type); - data.Add(ts.range); - data.Add(ts.arc); - } + data.Add(ts.interval); + data.Add(ts.name); + data.Add(ts.keyID); + data.Add(ts.type); + data.Add(ts.range); + data.Add(ts.arc); } } + return data.ToArray(); } @@ -663,8 +672,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api.Plugins ts.next = DateTime.Now.ToUniversalTime().AddSeconds(ts.interval); - lock (SenseRepeatListLock) - SenseRepeaters.Add(ts); + AddSenseRepeater(ts); idx += 6; }