/// <summary> /// This removes the event from the queue and allows it to be fired again /// </summary> /// <param name="QIS"></param> public void EventComplete(QueueItemStruct QIS) { lock (QIS.ID.ScriptEventLock) { scriptEvents eventType = (scriptEvents) Enum.Parse(typeof (scriptEvents), QIS.functionName); switch (eventType) { case scriptEvents.timer: QIS.ID.TimerInQueue = false; break; case scriptEvents.control: if (QIS.ID.ControlEventsInQueue > 0) QIS.ID.ControlEventsInQueue--; break; case scriptEvents.collision: QIS.ID.CollisionInQueue = false; break; case scriptEvents.collision_end: QIS.ID.CollisionInQueue = false; break; case scriptEvents.moving_end: QIS.ID.MovingInQueue = false; break; case scriptEvents.touch: QIS.ID.TouchInQueue = false; break; case scriptEvents.touch_end: QIS.ID.TouchInQueue = false; break; case scriptEvents.land_collision: QIS.ID.LandCollisionInQueue = false; break; case scriptEvents.land_collision_end: QIS.ID.LandCollisionInQueue = false; break; case scriptEvents.sensor: QIS.ID.SensorInQueue = false; break; case scriptEvents.no_sensor: QIS.ID.NoSensorInQueue = false; break; case scriptEvents.at_target: QIS.ID.AtTargetInQueue = false; break; case scriptEvents.not_at_target: QIS.ID.NotAtTargetInQueue = false; break; case scriptEvents.at_rot_target: QIS.ID.AtRotTargetInQueue = false; break; case scriptEvents.not_at_rot_target: QIS.ID.NotAtRotTargetInQueue = false; break; case scriptEvents.changed: Changed changed; if (QIS.param[0] is Changed) { changed = (Changed) QIS.param[0]; } else { changed = (Changed) (((LSL_Types.LSLInteger) QIS.param[0]).value); } QIS.ID.ChangedInQueue.Remove(changed); break; } } }
public bool CheckAddEventToQueue(QueueItemStruct itm) { if (funcsToDrop.Contains(itm.functionName)) return false;//Drop them, don't enqueue lock (m_eventQueueLock) { if (m_eventQueueTimer == null)//Have it try again in 5ms m_eventQueueTimer = new Timer(EventQueuePoke, null, 5, 5); m_eventQueue.Enqueue(itm); } return false; }
public bool SetEventParams(QueueItemStruct itm) { if (Suspended || !Running) return false; //No suspended scripts... if (itm.llDetectParams.Length > 0) LastDetectParams = itm.llDetectParams; if (itm.functionName == "state_entry" || itm.functionName == "state_exit" || itm.functionName == "on_rez") return true; long NowTicks = Util.EnvironmentTickCount(); if (EventDelayTicks != 0) { if (NowTicks < NextEventTimeTicks) return false; NextEventTimeTicks = NowTicks + EventDelayTicks; } switch (itm.functionName) { //Times pulled from http://wiki.secondlife.com/wiki/LSL_Delay case "touch": //Limits for 0.1 seconds case "touch_start": case "touch_end": if (NowTicks < NextEventDelay[itm.functionName]) return CheckAddEventToQueue(itm); NextEventDelay[itm.functionName] = NowTicks + (long) (TouchEventDelayTicks*TicksPerMillisecond); break; case "timer": //Settable timer limiter if (NowTicks < NextEventDelay[itm.functionName]) return CheckAddEventToQueue(itm); NextEventDelay[itm.functionName] = NowTicks + (long) (TimerEventDelayTicks*TicksPerMillisecond); break; case "collision": case "collision_start": case "collision_end": case "land_collision": case "land_collision_start": case "land_collision_end": if (NowTicks < NextEventDelay[itm.functionName]) return CheckAddEventToQueue(itm); NextEventDelay[itm.functionName] = NowTicks + (long) (CollisionEventDelayTicks*TicksPerMillisecond); break; case "control": if (NowTicks < NextEventDelay[itm.functionName]) return CheckAddEventToQueue(itm); NextEventDelay[itm.functionName] = NowTicks + (long) (0.05f*TicksPerMillisecond); break; default: //Default is 0.05 seconds for event limiting if (!NextEventDelay.ContainsKey(itm.functionName)) break; //If it doesn't exist, we don't limit it if (NowTicks < NextEventDelay[itm.functionName]) return CheckAddEventToQueue(itm); NextEventDelay[itm.functionName] = NowTicks + (long) (DefaultEventDelayTicks*TicksPerMillisecond); break; } //Add the event to the stats ScriptScore++; m_ScriptEngine.ScriptEPS++; return true; }
public bool EventSchProcessQIS(ref QueueItemStruct QIS) { try { Exception ex = null; EnumeratorInfo Running = QIS.ID.Script.ExecuteEvent(QIS.State, QIS.functionName, QIS.param, QIS.CurrentlyAt, out ex); if (ex != null) { //Check exceptions, some are ours to deal with, and others are to be logged if (ex.Message.Contains("SelfDeleteException")) { if (QIS.ID.Part != null && QIS.ID.Part.ParentEntity != null) { IBackupModule backup = QIS.ID.Part.ParentEntity.Scene.RequestModuleInterface<IBackupModule>(); if (backup != null) backup.DeleteSceneObjects( new ISceneEntity[1] {QIS.ID.Part.ParentEntity}, true, true); } } else if (ex.Message.Contains("ScriptDeleteException")) { if (QIS.ID.Part != null && QIS.ID.Part.ParentEntity != null) QIS.ID.Part.Inventory.RemoveInventoryItem(QIS.ID.ItemID); } //Log it for the user else if (!(ex.Message.Contains("EventAbortException")) && !(ex.Message.Contains("MinEventDelayException"))) QIS.ID.DisplayUserNotification(ex.ToString(), "executing", false, true); EventManager.EventComplete(QIS); return false; } else if (Running != null) { //Did not finish so requeue it QIS.CurrentlyAt = Running; QIS.RunningNumber++; return true; //Do the return... otherwise we open the queue for this event back up } } catch (Exception ex) { //Error, tell the user QIS.ID.DisplayUserNotification(ex.ToString(), "executing", false, true); } //Tell the event manager about it so that the events will be removed from the queue EventManager.EventComplete(QIS); return false; }
public void EventSchExec(QueueItemStruct QIS) { if (QIS.ID == null || QIS.ID.Script == null) return; if (!QIS.ID.Running) { //do only state_entry and on_rez if (QIS.functionName != "state_entry" || QIS.functionName != "on_rez") { return; } } //Check the versionID so that we can kill events if (QIS.functionName != "link_message" && QIS.VersionID != Interlocked.Read(ref QIS.ID.VersionID)) { MainConsole.Instance.WarnFormat("FOUND BAD VERSION ID, OLD {0}, NEW {1}, FUNCTION NAME {2}", QIS.VersionID, Interlocked.Read(ref QIS.ID.VersionID), QIS.functionName); //return; } if(MainConsole.Instance.IsTraceEnabled) MainConsole.Instance.TraceFormat("[DNE]: Running Event {0} in object {1} in region {2}", QIS.functionName, QIS.ID.Part.ToString(), QIS.ID.Part.ParentEntity.Scene.RegionInfo.RegionName); if (!EventSchProcessQIS(ref QIS)) //Execute the event { //All done QIS.EventsProcData.State = ScriptEventsState.Idle; } else { if (QIS.CurrentlyAt.SleepTo.Ticks != 0) { QIS.EventsProcData.TimeCheck = QIS.CurrentlyAt.SleepTo; QIS.EventsProcData.State = ScriptEventsState.Sleep; //If it is greater, we need to check sooner for this one if (NextSleepersTest.Ticks > QIS.CurrentlyAt.SleepTo.Ticks) NextSleepersTest = QIS.CurrentlyAt.SleepTo; lock (SleepingScriptEvents) { SleepingScriptEvents.Enqueue(QIS, QIS.CurrentlyAt.SleepTo.Ticks); SleepingScriptEventCount++; } } else { QIS.EventsProcData.State = ScriptEventsState.Running; this.ScriptEvents.Enqueue(QIS); } } }
public void eventLoop() { int numberOfEmptyWork = 0; while (!m_ScriptEngine.ConsoleDisabled && !m_ScriptEngine.Disabled && m_ScriptEngine.Scene.ShouldRunHeartbeat) { //int numScriptsProcessed = 0; int numSleepScriptsProcessed = 0; //const int minNumScriptsToProcess = 1; //processMoreScripts: QueueItemStruct QIS = new QueueItemStruct(); bool found = false; //Check whether it is time, and then do the thread safety piece if (Interlocked.CompareExchange(ref m_CheckingSleepers, 1, 0) == 0) { lock (SleepingScriptEvents) { restart: if (SleepingScriptEvents.Count > 0) { QIS = SleepingScriptEvents.Dequeue().Value; found = true; if (QIS.RunningNumber > 2 && SleepingScriptEventCount > 0 && numSleepScriptsProcessed < SleepingScriptEventCount) { QIS.RunningNumber = 1; SleepingScriptEvents.Enqueue(QIS, QIS.EventsProcData.TimeCheck.Ticks); numSleepScriptsProcessed++;found = false; found = false; goto restart; } } } if (found) { if (QIS.EventsProcData.TimeCheck.Ticks < DateTime.Now.Ticks) { DateTime NextTime = DateTime.MaxValue; lock (SleepingScriptEvents) { if (SleepingScriptEvents.Count > 0) NextTime = SleepingScriptEvents.Peek().Value.EventsProcData.TimeCheck; //Now add in the next sleep time NextSleepersTest = NextTime; //All done Interlocked.Exchange(ref m_CheckingSleepers, 0); } //Execute the event EventSchExec(QIS); lock (SleepingScriptEvents) SleepingScriptEventCount--; //numScriptsProcessed++; } else { lock (SleepingScriptEvents) { NextSleepersTest = QIS.EventsProcData.TimeCheck; SleepingScriptEvents.Enqueue(QIS, QIS.EventsProcData.TimeCheck.Ticks); //All done Interlocked.Exchange(ref m_CheckingSleepers, 0); } } } else //No more left, don't check again { lock (SleepingScriptEvents) { NextSleepersTest = DateTime.MaxValue; //All done Interlocked.Exchange(ref m_CheckingSleepers, 0); } } } int timeToSleep = 5; //If we can, get the next event if (Interlocked.CompareExchange(ref m_CheckingEvents, 1, 0) == 0) { if (ScriptEvents.TryDequeue(out QIS)) { Interlocked.Exchange(ref m_CheckingEvents, 0); #if Debug MainConsole.Instance.Warn(QIS.functionName + "," + ScriptEvents.Count); #endif EventSchExec(QIS); //numScriptsProcessed++; } else Interlocked.Exchange(ref m_CheckingEvents, 0); } //Process a bunch each time //if (ScriptEventCount > 0 && numScriptsProcessed < minNumScriptsToProcess) // goto processMoreScripts; if (ScriptEvents.Count == 0 && NextSleepersTest.Ticks != DateTime.MaxValue.Ticks) timeToSleep = (int) (NextSleepersTest - DateTime.Now).TotalMilliseconds; if (timeToSleep < 5) timeToSleep = 5; if (timeToSleep > 50) timeToSleep = 50; if (SleepingScriptEventCount == 0 && ScriptEvents.Count == 0) { numberOfEmptyWork++; if (numberOfEmptyWork > EMPTY_WORK_KILL_THREAD_TIME) //Don't break immediately, otherwise we have to wait to spawn more threads { break; //No more events, end } else if (numberOfEmptyWork > EMPTY_WORK_KILL_THREAD_TIME/20) timeToSleep += 10; } else if (Interlocked.Read(ref scriptThreadpool.nthreads) > (ScriptEvents.Count + (int) ((SleepingScriptEventCount/2f + 0.5f))) || Interlocked.Read(ref scriptThreadpool.nthreads) > MaxScriptThreads) { numberOfEmptyWork++; if (numberOfEmptyWork > (EMPTY_WORK_KILL_THREAD_TIME/2)) //Don't break immediately { break; //Too many threads, kill some off } else if (numberOfEmptyWork > EMPTY_WORK_KILL_THREAD_TIME/20) timeToSleep += 5; } else numberOfEmptyWork /= 2; //Cut it down, but don't zero it out, as this may just be one event #if Debug MainConsole.Instance.Warn ("Sleep: " + timeToSleep); #endif Interlocked.Increment(ref scriptThreadpool.nSleepingthreads); Thread.Sleep(timeToSleep); Interlocked.Decrement(ref scriptThreadpool.nSleepingthreads); } }
public void AddEventSchQueue(ScriptData ID, string FunctionName, DetectParams[] qParams, EventPriority priority, params object[] param) { QueueItemStruct QIS = new QueueItemStruct { EventsProcData = new ScriptEventsProcData(), ID = ID, functionName = FunctionName, llDetectParams = qParams, param = param, VersionID = Interlocked.Read(ref ID.VersionID), State = ID.State, CurrentlyAt = null }; if (ID == null || ID.Script == null || ID.IgnoreNew) return; if (!ID.SetEventParams(QIS)) // check events delay rules return; ScriptEvents.Enqueue(QIS); long threadCount = Interlocked.Read(ref scriptThreadpool.nthreads); if (threadCount == 0 || threadCount < (ScriptEvents.Count + (SleepingScriptEventCount/2))*EventPerformance) { scriptThreadpool.QueueEvent(eventLoop, 2); } }
public bool AddEventSchQIS(QueueItemStruct QIS, EventPriority priority) { if (QIS.ID == null || QIS.ID.IgnoreNew) { EventManager.EventComplete(QIS); return false; } if (QIS.ID.Script == null) { QIS.ID.CheckAddEventToQueue(QIS); return false; } if (!QIS.ID.SetEventParams(QIS)) // check events delay rules { EventManager.EventComplete(QIS); return false; } QIS.CurrentlyAt = null; if (priority == EventPriority.Suspended || priority == EventPriority.Continued) { lock (SleepingScriptEvents) { long time = priority == EventPriority.Suspended ? DateTime.Now.AddMilliseconds(10).Ticks : DateTime.Now.Ticks; //Let it sleep for 10ms so that other scripts can process before it, any repeating plugins ought to use this SleepingScriptEvents.Enqueue(QIS, time); SleepingScriptEventCount++; #if Debug MainConsole.Instance.Warn (ScriptEventCount + ", " + QIS.functionName); #endif } } else ScriptEvents.Enqueue(QIS); long threadCount = Interlocked.Read(ref scriptThreadpool.nthreads); if (threadCount == 0 || threadCount < (ScriptEvents.Count + (SleepingScriptEventCount/2))*EventPerformance) { scriptThreadpool.QueueEvent(eventLoop, 2); } return true; }