// Worker threads come here to take items off the work queue. // Multiple threads loop around here taking items off the work queue and // processing them. When there are no more things to do, the threads // return which puts them back in the thread pool. private void DoWork(object x) { while (m_workItems.Count > 0) { DoLaterBase w = null; lock (m_workItems) { if (m_workItems.Count > 0) { w = m_workItems.Dequeue(); } } if (w != null) { try { if (!w.DoIt()) { // LogManager.Log.Log(LogLevel.DRENDERDETAIL, "{0}.DoLater: DoWork: DoEvenLater", m_queueName); DoItEvenLater(w); } } catch (Exception e) { LogManager.Log.Log(LogLevel.DBADERROR, "{0}.DoLater: DoWork: EXCEPTION: {1}", m_queueName, e); // we drop the work item in the belief that it will exception again next time } } } lock (m_workItems) { m_activeWorkProcessors--; // not sure if this is atomic } }
// IWorkQueue.DoLater() public void DoLater(DoLaterBase w) { w.containingClass = this; w.remainingWait = 0; // the first time through, do it now w.timesRequeued = 0; AddWorkItemToQueue(w); }
// requeuing the work item. Since requeuing, add the delay public void DoLaterRequeue(ref DoLaterBase w) { w.timesRequeued++; int nextTime = Math.Min(w.requeueWait * w.timesRequeued, 5000); w.remainingWait = System.Environment.TickCount + nextTime; AddToWorkQueue(w); }
private void DoItEvenLater(DoLaterBase w) { w.timesRequeued++; lock (m_doEvenLater) { int nextTime = Math.Min(w.requeueWait * w.timesRequeued, 2000); nextTime = Math.Max(nextTime, 100); // never less than this w.remainingWait = Utilities.TickCount() + nextTime; m_doEvenLater.Add(w); } }
public void DoLater(DoLaterBase w) { if (((m_totalRequests++) % 100) == 0) { LogManager.Log.Log(LogLevel.DRENDERDETAIL, "{0}.DoLater: Queuing. requests={1}, queueSize={2}", m_queueName, m_totalRequests, m_workQueue.Count); } w.containingClass = this; w.remainingWait = 0; // the first time through, do it now w.timesRequeued = 0; AddToWorkQueue(w); }
// A thread from the outside world calls in here to do some work on the queue // We process work items on the queue until the queue is empty or we reach 'maximumCost'. // Each queued item has a delay (a time in the future when it can be done) and a // cost. As the work items are done, the cost is added up. // This means the thread coming in can count on being here only a limited amount // of time. public void ProcessQueue(int maximumCost) { int totalCost = 0; int totalCounter = 100; int now = System.Environment.TickCount; DoLaterBase found = null; while ((totalCost < maximumCost) && (totalCounter > 0) && (m_workQueue.Count > 0)) { now = System.Environment.TickCount; found = null; lock (m_workQueue) { // find an entry in the list who's time has come foreach (DoLaterBase ww in m_workQueue) { if (ww.remainingWait < now) { found = ww; break; } } if (found != null) { // if found, remove from list m_workQueue.Remove(found); } } if (found == null) { // if nothing found, we're done break; } else { // try to do the operation totalCounter--; if (found.DoIt()) { // if it worked, count it as successful totalCost += found.cost; } else { // if it didn't work, requeue it for later ((OnDemandWorkQueue)found.containingClass).DoLaterRequeue(ref found); } } } }
// Add a new work item to the work queue. If we don't have the maximum number // of worker threads already working on the queue, start a new thread from // the pool to empty the queue. private void AddWorkItemToQueue(DoLaterBase w) { if ((m_totalRequests++ % 100) == 0) { LogManager.Log.Log(LogLevel.DRENDERDETAIL, "{0}.DoLater: Queuing, c={1}, l={2}", m_queueName, m_totalRequests, m_workItems.Count); } lock (m_workItems) { m_workItems.Enqueue(w); if (m_activeWorkProcessors < m_workProcessorsMax) { m_activeWorkProcessors++; ThreadPool.QueueUserWorkItem(new WaitCallback(this.DoWork), null); // ThreadPool.UnsafeQueueUserWorkItem(new WaitCallback(this.DoWork), null); } } }
/// <summary> /// Add the work item to the queue in the order order /// </summary> /// <param name="w"></param> private void AddToWorkQueue(DoLaterBase w) { lock (m_workQueue) { /* * // Experimental code trying to give some order to the requests * LinkedListNode<DoLaterBase> foundItem = null; * for (LinkedListNode<DoLaterBase> ii = m_workQueue.First; ii != null; ii = ii.Next) { * if (w.order < ii.Value.order) { * foundItem = ii; * break; * } * } * if (foundItem != null) { * // we're pointing to an element to put our element before * m_workQueue.AddBefore(foundItem, w); * } * else { * // just put it on the end * m_workQueue.AddLast(w); * } */ m_workQueue.AddLast(w); } }
/// <summary> /// On our own thread, make the synchronous texture request from the asset server /// </summary> /// <param name="qInstance"></param> /// <param name="obinID"></param> /// <returns></returns> private bool ThrottleTextureMakeRequest(DoLaterBase qInstance, Object obinID) { OMV.UUID binID = (OMV.UUID)obinID; Uri assetPath = new Uri(m_basePath + "/assets/" + binID.ToString() + (m_dataFetch ? "/data" : "")); HttpWebRequest request = (HttpWebRequest)WebRequest.Create(assetPath); request.MaximumAutomaticRedirections = 4; request.MaximumResponseHeadersLength = 4; request.Timeout = 30000; // 30 second timeout if (m_proxyPath != null) { // configure proxy if necessary WebProxy myProxy = new WebProxy(); myProxy.Address = new Uri(m_proxyPath); request.Proxy = myProxy; } try { m_log.Log(LogLevel.DCOMMDETAIL, "ThrottleTextureMakeRequest: requesting '{0}'", assetPath); using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) { m_log.Log(LogLevel.DCOMMDETAIL, "ThrottleTextureMakeRequest: request returned. resp={0}, l={1}", response.StatusCode, response.ContentLength); if (response.StatusCode == HttpStatusCode.OK) { using (Stream receiveStream = response.GetResponseStream()) { OMV.Assets.AssetTexture at; if (m_dataFetch) { // we're getting raw binary data byte[] textureBuff = new byte[response.ContentLength]; receiveStream.Read(textureBuff, 0, (int)response.ContentLength); at = new OMV.Assets.AssetTexture(binID, textureBuff); } else { // receiving a serialized package XmlSerializer xserial = new XmlSerializer(typeof(OpenSim.Framework.AssetBase)); OpenSim.Framework.AssetBase abase = (OpenSim.Framework.AssetBase)xserial.Deserialize(receiveStream); at = new OMV.Assets.AssetTexture(binID, abase.Data); } ProcessDownloadFinished(OMV.TextureRequestState.Finished, at); } } else { OMV.Assets.AssetTexture at = new OMV.Assets.AssetTexture(binID, new byte[0]); ProcessDownloadFinished(OMV.TextureRequestState.NotFound, at); } } } catch (Exception e) { m_log.Log(LogLevel.DBADERROR, "Error fetching asset: {0}", e); OMV.Assets.AssetTexture at = new OMV.Assets.AssetTexture(binID, new byte[0]); ProcessDownloadFinished(OMV.TextureRequestState.NotFound, at); } return true; }
/// <summary> /// Current animations are for TargetOmega which causes rotation in the client. /// </summary> /// <param name="qInstance"></param> /// <param name="parms"></param> /// <returns>true if we can do this update now. False if to retry</returns> private bool DoUpdateAnimationLater(DoLaterBase qInstance, Object parms) { Object[] loadParams = (Object[])parms; float prio = (float)loadParams[0]; IEntity m_ent = (IEntity)loadParams[1]; IWorldRenderConv m_conver = (IWorldRenderConv)loadParams[2]; IAnimation m_anim = (IAnimation)loadParams[3]; string sceneNodeName = RendererOgre.GetSceneNodeName(m_ent); if (sceneNodeName == null) { // prim does not yet have a scene node. Try again later. return false; } return m_conver.UpdateAnimation(0, m_ent, sceneNodeName, m_anim); }
private bool QueueTilLaterDoIt(DoLaterBase dlb, Object p) { Object[] parms = (Object[])p; CommActionCode cac = (CommActionCode)parms[1]; // m_log.Log(LogLevel.DCOMMDETAIL, "QueueTilLaterDoIt: c={0}", cac); RegionAction(cac, parms[2], parms[3], parms[4], parms[5]); return true; }
private bool UI_MouseMoveLater(DoLaterBase qInstance, Object parms) { Object[] loadParams = (Object[])parms; int param = (int)loadParams[0]; float x = (float)loadParams[1]; float y = (float)loadParams[2]; int sinceLastMouse = System.Environment.TickCount - m_lastMouseMoveTime; m_lastMouseMoveTime = System.Environment.TickCount; // m_log.Log(LogLevel.DVIEWDETAIL, "OnMouseMove: x={0}, y={1}, time since last={2}", x, y, sinceLastMouse); if (m_mainCamera != null) { if (((Renderer.UserInterface.LastKeyCode & Keys.Control) == 0) && ((Renderer.UserInterface.LastKeyCode & Keys.Alt) != 0)) { m_log.Log(LogLevel.DVIEWDETAIL, "OnMouseMove: ALT: "); } else if ( ((Renderer.UserInterface.LastKeyCode & Keys.Control) != 0) && ((Renderer.UserInterface.LastKeyCode & Keys.Alt) != 0) ) { // if ALT+CNTL is held down, movement is on view plain float xMove = x * m_cameraSpeed; float yMove = y * m_cameraSpeed; OMV.Vector3d movement = new OMV.Vector3d( 0, xMove, yMove); m_log.Log(LogLevel.DVIEWDETAIL, "OnMouseMove: CNTL-ALT: Move camera x={0}, y={1}", xMove, yMove); m_mainCamera.GlobalPosition -= movement; } else if ((Renderer.UserInterface.LastKeyCode & Keys.Control) != 0) { // if CNTL is held down, movement is on land plane float xMove = x * m_cameraSpeed; float yMove = y * m_cameraSpeed; m_log.Log(LogLevel.DVIEWDETAIL, "OnMouseMove: CNTL: Move camera x={0}, y={1}", xMove, yMove); OMV.Vector3d movement = new OMV.Vector3d( yMove, xMove, 0f); m_mainCamera.GlobalPosition -= movement; } else if ((Renderer.UserInterface.LastMouseButtons & MouseButtons.Left) != 0) { // move the camera around the horizontal (X) and vertical (Z) axis float xMove = (-x * m_cameraRotationSpeed * Constants.DEGREETORADIAN) % Constants.TWOPI; float yMove = (-y * m_cameraRotationSpeed * Constants.DEGREETORADIAN) % Constants.TWOPI; // rotate around local axis // m_log.Log(LogLevel.DVIEWDETAIL, "OnMouseMove: Rotate camera x={0}, y={1}, lmb={2}", // xMove, yMove, Renderer.UserInterface.LastMouseButtons); m_mainCamera.rotate(yMove, 0f, xMove); } } return true; }
private bool ThrottleTextureMakeRequest(DoLaterBase qInstance, Object obinID) { OMV.UUID binID = (OMV.UUID)obinID; m_comm.GridClient.Assets.RequestImage(binID, OMV.ImageType.Normal, 1013000f, 0, 0, OnACDownloadFinished, false); return true; }
/// <summary> /// One of the input subsystems has received a key or mouse press. /// </summary> /// <param name="qInstance"></param> /// <param name="parms"></param> /// <returns></returns> private bool ReceiveLater(DoLaterBase qInstance, Object parms) { Object[] loadParams = (Object[])parms; ReceiveUserIOInputEventTypeCode typ = (ReceiveUserIOInputEventTypeCode)loadParams[0]; int param1 = (int)loadParams[1]; float param2 = (float)loadParams[2]; float param3 = (float)loadParams[3]; switch (typ) { case ReceiveUserIOInputEventTypeCode.KeyPress: param1 = param1 & (int)Keys.KeyCode; // remove extra cruft this.UpdateModifier(param1, true); AddKeyToLastKeyCode(param1); m_log.Log(LogLevel.DVIEWDETAIL, "UserInterfaceCommon: ReceiveLater: KeyPress: {0}. LastKeyCode={1}", param1, this.LastKeyCode); this.m_repeatKey = param1; this.KeyPressed = true; this.m_repeatTimer.Change(this.KeyRepeatMs, this.KeyRepeatMs); if (this.OnUserInterfaceKeypress != null) this.OnUserInterfaceKeypress(this.LastKeyCode, true); break; case ReceiveUserIOInputEventTypeCode.KeyRelease: param1 = param1 & (int)Keys.KeyCode; // remove extra cruft this.UpdateModifier(param1, false); AddKeyToLastKeyCode(param1); m_log.Log(LogLevel.DVIEWDETAIL, "UserInterfaceCommon: ReceiveLater: KeyRelease: {0}. LastKeyCode={1}", param1, this.LastKeyCode); this.KeyPressed = false; this.m_repeatTimer.Change(Timeout.Infinite, Timeout.Infinite); if (this.OnUserInterfaceKeypress != null) this.OnUserInterfaceKeypress(this.LastKeyCode, false); break; case ReceiveUserIOInputEventTypeCode.MouseButtonDown: this.UpdateMouseModifier(param1, true); m_log.Log(LogLevel.DVIEWDETAIL, "UserInterfaceCommon: ReceiveLater: MouseBtnDown: {0}, {1}", param1, this.LastMouseButtons); if (this.OnUserInterfaceMouseButton != null) this.OnUserInterfaceMouseButton( ThisMouseButtonCode(param1), true); break; case ReceiveUserIOInputEventTypeCode.MouseButtonUp: this.UpdateMouseModifier(param1, false); m_log.Log(LogLevel.DVIEWDETAIL, "UserInterfaceCommon: ReceiveLater: MouseBtnUp: {0}, {1}", param1, this.LastMouseButtons); if (this.OnUserInterfaceMouseButton != null) this.OnUserInterfaceMouseButton( ThisMouseButtonCode(param1), false); break; case ReceiveUserIOInputEventTypeCode.MouseMove: // pass the routine tracking the raw position information // param1 is usually zero (actually mouse selector but we have only one at the moment) // param2 is the X movement // param3 is the Y movement if (this.OnUserInterfaceMouseMove != null) this.OnUserInterfaceMouseMove(param1, param2, param3); break; } // successful return true; }
// Called on workqueue thread to create material in Ogre private bool RequestMaterialLater(DoLaterBase qInstance, Object parms) { Object[] loadParams = (Object[])parms; EntityNameOgre m_entName = (EntityNameOgre)loadParams[0]; string m_matName = (string)loadParams[1]; try { IEntity ent; if (World.World.Instance.TryGetEntity(m_entName, out ent)) { lock (ent) { LogManager.Log.Log(LogLevel.DRENDERDETAIL, "RequestMaterialLater.DoIt(): converting {0}", m_entName); IWorldRenderConv conver; if (!ent.TryGet<IWorldRenderConv>(out conver)) { // the rendering context is not set up. Odd but not fatal // try again later return false; } else { // Create the material resource and then make the rendering redisplay conver.CreateMaterialResource(qInstance.priority, ent, m_matName); Ogr.RefreshResourceBF(qInstance.priority, Ogr.ResourceTypeMaterial, m_matName); } } } else { // we couldn't find the entity for the material. not good m_log.Log(LogLevel.DBADERROR, "ProcessWaitingMaterials: could not find entity for material {0}", m_matName); } } catch (Exception e) { m_log.Log(LogLevel.DBADERROR, "ProcessWaitingMaterials: exception realizing material: {0}", e.ToString()); } return true; }
private bool DoUpdateLater(DoLaterBase qInstance, object parm) { object[] parms = (object[])parm; IEntity ent = (IEntity)parms[0]; UpdateCodes detail = (UpdateCodes)parms[1]; EntityUpdateCallback euc = OnEntityUpdate; if (euc != null) { euc(ent, detail); } return true; }
private bool RequestTextureLater(DoLaterBase qInstance, Object parm) { EntityNameOgre m_entName = (EntityNameOgre)parm; // note the super kludge since we don't know the real asset context // This information is coded into the entity name // The callback can (and will) be called multiple times as the texture gets better resolution AssetContextBase.RequestTextureLoad(m_entName, AssetContextBase.AssetType.Texture, TextureLoadedCallback); return true; }
// Doing the work didn't work the first time so we again add it to the queue public void DoLaterRequeue(DoLaterBase w) { AddWorkItemToQueue(w); }
/// <summary> /// Add the work item to the queue in the order order /// </summary> /// <param name="w"></param> private void AddToWorkQueue(DoLaterBase w) { lock (m_workQueue) { /* // Experimental code trying to give some order to the requests LinkedListNode<DoLaterBase> foundItem = null; for (LinkedListNode<DoLaterBase> ii = m_workQueue.First; ii != null; ii = ii.Next) { if (w.order < ii.Value.order) { foundItem = ii; break; } } if (foundItem != null) { // we're pointing to an element to put our element before m_workQueue.AddBefore(foundItem, w); } else { // just put it on the end m_workQueue.AddLast(w); } */ m_workQueue.AddLast(w); } }
private bool DoRenderLater(DoLaterBase qInstance, Object parms) { Object[] loadParams = (Object[])parms; IEntity m_ent = (IEntity)loadParams[0]; RenderableInfo m_ri = (RenderableInfo)loadParams[1]; bool m_hasMesh = (bool)loadParams[2]; // m_log.Log(LogLevel.DRENDERDETAIL, "DoRenderLater: ent={0}", m_ent.Name); IRenderEntity rEntity; if (m_ent.TryGet<IRenderEntity>(out rEntity)) { if (!rEntity.Create(ref m_ri, ref m_hasMesh, qInstance.priority, qInstance.timesRequeued)) { // Didn't want to create for some reason. // Remember progress flags and return 'false' so we get retried loadParams[1] = m_ri; loadParams[2] = m_hasMesh; return false; } } else { m_log.Log(LogLevel.DBADERROR, "DoRenderLater: entity had no renderEntity: {0}", m_ent.Name); } // System.GC.Collect(); // only for debugging return true; }
// Called on workQueue to call into gather parameters and create the mesh resource private bool RequestMeshLater(DoLaterBase qInstance, Object parm) { Object[] lparams = (Object[])parm; string m_meshName = (string)lparams[0]; EntityName m_contextEntityName = (EntityName)lparams[1]; try { // type information is at the end of the name. remove if there EntityName eName = EntityNameOgre.ConvertOgreResourceToEntityName(m_meshName); // the terrible kludge here is that the name of the mesh is the name of // the entity. We reach back into the worlds and find the underlying // entity then we can construct the mesh. IEntity ent; if (!World.World.Instance.TryGetEntity(eName, out ent)) { m_log.Log(LogLevel.DBADERROR, "RendererOgre.RequestMeshLater: could not find entity " + eName); lock (m_requestedMeshes) { m_requestedMeshes.Remove(m_meshName); } return true; } // Create mesh resource. In this case its most likely a .mesh file in the cache // The actual mesh creation is queued and done later between frames lock (ent) { float priority = CalculateInterestOrder(ent); IWorldRenderConv conver; ent.TryGet<IWorldRenderConv>(out conver); if (!conver.CreateMeshResource(priority, ent, m_meshName, m_contextEntityName)) { // we need to wait until some resource exists before we can complete this creation return false; } // tell Ogre to refresh (reload) the resource m_log.Log(LogLevel.DRENDERDETAIL, "RendererOgre.RequestMeshLater: refresh for {0}. prio={1}", m_meshName, priority); Ogr.RefreshResourceBF(priority, Ogr.ResourceTypeMesh, m_meshName); } // following code commented out to leave the mesh in the list and let it time out // lock (m_requestedMeshes) { // m_requestedMeshes.Remove(m_meshName); // } } catch { // an oddity but not fatal } return true; }
private bool DoEventLater(DoLaterBase qInstance, object parm) { EntityNewCallback enc = OnEntityNew; if (enc != null) { enc((IEntity)parm); } return true; }