public PathProcessor(AstarPath astar, PathReturnQueue returnQueue, int processors, bool multithreaded) { this.astar = astar; this.returnQueue = returnQueue; if (processors < 0) { throw new System.ArgumentOutOfRangeException("processors"); } if (!multithreaded && processors != 1) { throw new System.Exception("Only a single non-multithreaded processor is allowed"); } // Set up path queue with the specified number of receivers queue = new ThreadControlQueue(processors); threadInfos = new PathThreadInfo[processors]; for (int i = 0; i < processors; i++) { threadInfos[i] = new PathThreadInfo(i, astar, new PathHandler(i, processors)); } if (multithreaded) { threads = new Thread[processors]; // Start lots of threads for (int i = 0; i < processors; i++) { var threadIndex = i; var thread = new Thread(() => CalculatePathsThreaded(threadInfos[threadIndex])); thread.Name = "Pathfinding Thread " + i; thread.IsBackground = true; threads[i] = thread; thread.Start(); } } else { // Start coroutine if not using multithreading threadCoroutine = CalculatePaths(threadInfos[0]); } }
/** Sets up all needed variables and scans the graphs. * Calls Initialize, starts the ReturnPaths coroutine and scans all graphs. * Also starts threads if using multithreading * \see #OnAwakeSettings */ public void Awake () { //Very important to set this. Ensures the singleton pattern holds active = this; if (FindObjectsOfType (typeof(AstarPath)).Length > 1) { Debug.LogError ("You should NOT have more than one AstarPath component in the scene at any time.\n" + "This can cause serious errors since the AstarPath component builds around a singleton pattern."); } //Disable GUILayout to gain some performance, it is not used in the OnGUI call useGUILayout = false; isEditor = Application.isEditor; if (OnAwakeSettings != null) { OnAwakeSettings (); } //To make sure all graph modifiers have been enabled before scan (to avoid script run order issues) GraphModifier.FindAllModifiers (); RelevantGraphSurface.FindAllGraphSurfaces (); int numThreads = CalculateThreadCount (threadCount); // Trying to prevent simple modding to add support for more than one thread if ( numThreads > 1 ) { threadCount = ThreadCount.One; numThreads = 1; } threads = new Thread[numThreads]; //Thread info, will contain at least one item since the coroutine "thread" is thought of as a real thread in this case threadInfos = new PathThreadInfo[System.Math.Max(numThreads,1)]; //Set up path queue with the specified number of receivers pathQueue = new ThreadControlQueue(threadInfos.Length); for (int i=0;i<threadInfos.Length;i++) { threadInfos[i] = new PathThreadInfo(i,this,new PathHandler()); } for (int i=0;i<threads.Length;i++) { threads[i] = new Thread (new ParameterizedThreadStart (CalculatePathsThreaded)); threads[i].Name = "Pathfinding Thread " + i; threads[i].IsBackground = true; } //Start coroutine if not using multithreading if (numThreads == 0) { threadEnumerator = CalculatePaths (threadInfos[0]); } else { threadEnumerator = null; } //Start pathfinding threads for (int i=0;i<threads.Length;i++) { if (logPathResults == PathLog.Heavy) Debug.Log ("Starting pathfinding thread "+i); threads[i].Start (threadInfos[i]); } Thread graphUpdateThread = new Thread (new ParameterizedThreadStart(ProcessGraphUpdatesAsync)); graphUpdateThread.IsBackground = true; graphUpdateThread.Start (this); Initialize (); // Flush work items, possibly added in initialize to load graph data FlushWorkItems(); if (scanOnStartup) { if (!astarData.cacheStartup || astarData.data_cachedStartup == null) { Scan (); } } }
/** Sets up all needed variables and scans the graphs. * Calls Initialize, starts the ReturnPaths coroutine and scans all graphs. * Also starts threads if using multithreading * \see #OnAwakeSettings */ public void Awake () { //Very important to set this. Ensures the singleton pattern holds active = this; if (FindObjectsOfType (typeof(AstarPath)).Length > 1) { Debug.LogError ("You should NOT have more than one AstarPath component in the scene at any time.\n" + "This can cause serious errors since the AstarPath component builds around a singleton pattern."); } //Disable GUILayout to gain some performance, it is not used in the OnGUI call useGUILayout = false; if (OnAwakeSettings != null) { OnAwakeSettings (); } int numThreads = CalculateThreadCount (threadCount); threads = new Thread[numThreads]; //Thread info, will contain at least one item since the coroutine "thread" is thought of as a real thread in this case threadInfos = new PathThreadInfo[System.Math.Max(numThreads,1)]; for (int i=0;i<threadInfos.Length;i++) { threadInfos[i] = new PathThreadInfo(i,this,new NodeRunData()); } for (int i=0;i<threads.Length;i++) { threads[i] = new Thread (new ParameterizedThreadStart (CalculatePathsThreaded)); threads[i].IsBackground = true; } Initialize (); StartCoroutine (ReturnsPathsHandler()); if (scanOnStartup) { if (!astarData.cacheStartup || astarData.data_cachedStartup == null) { Scan (); } } UpdatePathThreadInfoNodes (); //Start pathfinding threads if (threads.Length > 0) { Thread lockThread = new Thread(new ParameterizedThreadStart(LockThread)); lockThread.Start (this); } for (int i=0;i<threads.Length;i++) { if (logPathResults == PathLog.Heavy) Debug.Log ("Starting pathfinding thread "+i); threads[i].Start (threadInfos[i]); } //Or if there are no threads, it should run as a coroutine if (threads.Length == 0) StartCoroutine (CalculatePathsHandler(threadInfos[0])); }
private IEnumerator CalculatePaths(PathThreadInfo threadInfo) { int numPaths = 0; PathHandler runData = threadInfo.runData; if (runData.nodes == null) { throw new NullReferenceException("NodeRuns must be assigned to the threadInfo.runData.nodes field before threads are started\nthreadInfo is an argument to the thread functions"); } long maxTicks = (long)(this.astar.maxFrameTime * 10000f); long targetTick = DateTime.UtcNow.Ticks + maxTicks; for (;;) { Path p = null; bool blockedBefore = false; while (p == null) { try { p = this.queue.PopNoBlock(blockedBefore); blockedBefore |= (p == null); } catch (ThreadControlQueue.QueueTerminationException) { yield break; } if (p == null) { yield return null; } } maxTicks = (long)(this.astar.maxFrameTime * 10000f); p.PrepareBase(runData); p.AdvanceState(PathState.Processing); Action<Path> tmpOnPathPreSearch = this.OnPathPreSearch; if (tmpOnPathPreSearch != null) { tmpOnPathPreSearch(p); } numPaths++; long startTicks = DateTime.UtcNow.Ticks; long totalTicks = 0L; p.Prepare(); if (!p.IsDone()) { this.astar.debugPath = p; p.Initialize(); while (!p.IsDone()) { p.CalculateStep(targetTick); p.searchIterations++; if (p.IsDone()) { break; } totalTicks += DateTime.UtcNow.Ticks - startTicks; yield return null; startTicks = DateTime.UtcNow.Ticks; if (this.queue.IsTerminating) { p.Error(); } targetTick = DateTime.UtcNow.Ticks + maxTicks; } totalTicks += DateTime.UtcNow.Ticks - startTicks; p.duration = (float)totalTicks * 0.0001f; } p.Cleanup(); OnPathDelegate tmpImmediateCallback = p.immediateCallback; if (tmpImmediateCallback != null) { tmpImmediateCallback(p); } Action<Path> tmpOnPathPostSearch = this.OnPathPostSearch; if (tmpOnPathPostSearch != null) { tmpOnPathPostSearch(p); } this.returnQueue.Enqueue(p); p.AdvanceState(PathState.ReturnQueue); if (DateTime.UtcNow.Ticks > targetTick) { yield return null; targetTick = DateTime.UtcNow.Ticks + maxTicks; numPaths = 0; } } yield break; }
private void CalculatePathsThreaded(PathThreadInfo threadInfo) { try { PathHandler runData = threadInfo.runData; if (runData.nodes == null) { throw new NullReferenceException("NodeRuns must be assigned to the threadInfo.runData.nodes field before threads are started\nthreadInfo is an argument to the thread functions"); } long num = (long)(this.astar.maxFrameTime * 10000f); long num2 = DateTime.UtcNow.Ticks + num; for (;;) { Path path = this.queue.Pop(); num = (long)(this.astar.maxFrameTime * 10000f); path.PrepareBase(runData); path.AdvanceState(PathState.Processing); if (this.OnPathPreSearch != null) { this.OnPathPreSearch(path); } long ticks = DateTime.UtcNow.Ticks; long num3 = 0L; path.Prepare(); if (!path.IsDone()) { this.astar.debugPath = path; path.Initialize(); while (!path.IsDone()) { path.CalculateStep(num2); path.searchIterations++; if (path.IsDone()) { break; } num3 += DateTime.UtcNow.Ticks - ticks; Thread.Sleep(0); ticks = DateTime.UtcNow.Ticks; num2 = ticks + num; if (this.queue.IsTerminating) { path.Error(); } } num3 += DateTime.UtcNow.Ticks - ticks; path.duration = (float)num3 * 0.0001f; } path.Cleanup(); if (path.immediateCallback != null) { path.immediateCallback(path); } if (this.OnPathPostSearch != null) { this.OnPathPostSearch(path); } this.returnQueue.Enqueue(path); path.AdvanceState(PathState.ReturnQueue); if (DateTime.UtcNow.Ticks > num2) { Thread.Sleep(1); num2 = DateTime.UtcNow.Ticks + num; } } } catch (Exception ex) { if (ex is ThreadAbortException || ex is ThreadControlQueue.QueueTerminationException) { if (this.astar.logPathResults == PathLog.Heavy) { Debug.LogWarning("Shutting down pathfinding thread #" + threadInfo.threadIndex); } return; } Debug.LogException(ex); Debug.LogError("Unhandled exception during pathfinding. Terminating."); this.queue.TerminateReceivers(); } Debug.LogError("Error : This part should never be reached."); this.queue.ReceiverTerminated(); }
/** Main pathfinding method. * This method will calculate the paths in the pathfinding queue. * * \see CalculatePathsThreaded * \see StartPath */ IEnumerator CalculatePaths(PathThreadInfo threadInfo) { int numPaths = 0; // Initialize memory for this thread PathHandler runData = threadInfo.runData; if (runData.nodes == null) { throw new System.NullReferenceException("NodeRuns must be assigned to the threadInfo.runData.nodes field before threads are started\n" + "threadInfo is an argument to the thread functions"); } // Max number of ticks before yielding/sleeping long maxTicks = (long)(astar.maxFrameTime * 10000); long targetTick = System.DateTime.UtcNow.Ticks + maxTicks; while (true) { //The path we are currently calculating Path p = null; AstarProfiler.StartProfile("Path Queue"); //Try to get the next path to be calculated bool blockedBefore = false; while (p == null) { try { p = queue.PopNoBlock(blockedBefore); blockedBefore |= p == null; } catch (ThreadControlQueue.QueueTerminationException) { yield break; } if (p == null) { AstarProfiler.EndProfile(); yield return(null); AstarProfiler.StartProfile("Path Queue"); } } AstarProfiler.EndProfile(); AstarProfiler.StartProfile("Path Calc"); //Max number of ticks we are allowed to continue working in one run //One tick is 1/10000 of a millisecond maxTicks = (long)(astar.maxFrameTime * 10000); p.PrepareBase(runData); //Now processing the path //Will advance to Processing p.AdvanceState(PathState.Processing); // Call some callbacks // It needs to be stored in a local variable to avoid race conditions var tmpOnPathPreSearch = OnPathPreSearch; if (tmpOnPathPreSearch != null) { tmpOnPathPreSearch(p); } numPaths++; //Tick for when the path started, used for calculating how long time the calculation took long startTicks = System.DateTime.UtcNow.Ticks; long totalTicks = 0; AstarProfiler.StartFastProfile(8); AstarProfiler.StartFastProfile(0); //Prepare the path AstarProfiler.StartProfile("Path Prepare"); p.Prepare(); AstarProfiler.EndProfile("Path Prepare"); AstarProfiler.EndFastProfile(0); // Check if the Prepare call caused the path to complete // If this happens the path usually failed if (!p.IsDone()) { //For debug uses, we set the last computed path to p, so we can view debug info on it in the editor (scene view). astar.debugPath = p; //Initialize the path, now ready to begin search AstarProfiler.StartProfile("Path Initialize"); p.Initialize(); AstarProfiler.EndProfile(); //The error can turn up in the Init function while (!p.IsDone()) { // Do some work on the path calculation. // The function will return when it has taken too much time // or when it has finished calculation AstarProfiler.StartFastProfile(2); AstarProfiler.StartProfile("Path Calc Step"); p.CalculateStep(targetTick); AstarProfiler.EndFastProfile(2); p.searchIterations++; AstarProfiler.EndProfile(); // If the path has finished calculation, we can break here directly instead of sleeping // Improves latency if (p.IsDone()) { break; } AstarProfiler.EndFastProfile(8); totalTicks += System.DateTime.UtcNow.Ticks - startTicks; // Yield/sleep so other threads can work AstarProfiler.EndProfile(); yield return(null); AstarProfiler.StartProfile("Path Calc"); startTicks = System.DateTime.UtcNow.Ticks; AstarProfiler.StartFastProfile(8); //Cancel function (and thus the thread) if no more paths should be accepted. //This is done when the A* object is about to be destroyed //The path is returned and then this function will be terminated (see similar IF statement higher up in the function) if (queue.IsTerminating) { p.Error(); } targetTick = System.DateTime.UtcNow.Ticks + maxTicks; } totalTicks += System.DateTime.UtcNow.Ticks - startTicks; p.duration = totalTicks * 0.0001F; #if ProfileAstar System.Threading.Interlocked.Increment(ref AstarPath.PathsCompleted); System.Threading.Interlocked.Add(ref AstarPath.TotalSearchedNodes, p.searchedNodes); #endif } // Cleans up node tagging and other things p.Cleanup(); AstarProfiler.EndFastProfile(8); // Call the immediate callback // It needs to be stored in a local variable to avoid race conditions var tmpImmediateCallback = p.immediateCallback; if (tmpImmediateCallback != null) { tmpImmediateCallback(p); } AstarProfiler.StartFastProfile(13); // It needs to be stored in a local variable to avoid race conditions var tmpOnPathPostSearch = OnPathPostSearch; if (tmpOnPathPostSearch != null) { tmpOnPathPostSearch(p); } AstarProfiler.EndFastProfile(13); //Push the path onto the return stack //It will be detected by the main Unity thread and returned as fast as possible (the next late update) returnQueue.Enqueue(p); p.AdvanceState(PathState.ReturnQueue); AstarProfiler.EndProfile(); //Wait a bit if we have calculated a lot of paths if (System.DateTime.UtcNow.Ticks > targetTick) { yield return(null); targetTick = System.DateTime.UtcNow.Ticks + maxTicks; numPaths = 0; } } }
/** Main pathfinding method (multithreaded). * This method will calculate the paths in the pathfinding queue when multithreading is enabled. * * \see CalculatePaths * \see StartPath * * \astarpro */ void CalculatePathsThreaded(PathThreadInfo threadInfo) { #if !ASTAR_FAST_BUT_NO_EXCEPTIONS try { #endif //Initialize memory for this thread PathHandler runData = threadInfo.runData; if (runData.nodes == null) { throw new System.NullReferenceException("NodeRuns must be assigned to the threadInfo.runData.nodes field before threads are started\nthreadInfo is an argument to the thread functions"); } //Max number of ticks before yielding/sleeping long maxTicks = (long)(astar.maxFrameTime * 10000); long targetTick = System.DateTime.UtcNow.Ticks + maxTicks; while (true) { //The path we are currently calculating Path p = queue.Pop(); //Max number of ticks we are allowed to continue working in one run //One tick is 1/10000 of a millisecond maxTicks = (long)(astar.maxFrameTime * 10000); AstarProfiler.StartFastProfile(0); p.PrepareBase(runData); //Now processing the path //Will advance to Processing p.AdvanceState(PathState.Processing); //Call some callbacks if (OnPathPreSearch != null) { OnPathPreSearch(p); } //Tick for when the path started, used for calculating how long time the calculation took long startTicks = System.DateTime.UtcNow.Ticks; long totalTicks = 0; //Prepare the path p.Prepare(); AstarProfiler.EndFastProfile(0); if (!p.IsDone()) { //For debug uses, we set the last computed path to p, so we can view debug info on it in the editor (scene view). astar.debugPath = p; AstarProfiler.StartFastProfile(1); //Initialize the path, now ready to begin search p.Initialize(); AstarProfiler.EndFastProfile(1); //The error can turn up in the Init function while (!p.IsDone()) { //Do some work on the path calculation. //The function will return when it has taken too much time //or when it has finished calculation AstarProfiler.StartFastProfile(2); p.CalculateStep(targetTick); p.searchIterations++; AstarProfiler.EndFastProfile(2); // If the path has finished calculation, we can break here directly instead of sleeping if (p.IsDone()) { break; } // Yield/sleep so other threads can work totalTicks += System.DateTime.UtcNow.Ticks - startTicks; Thread.Sleep(0); startTicks = System.DateTime.UtcNow.Ticks; targetTick = startTicks + maxTicks; // Cancel function (and thus the thread) if no more paths should be accepted. // This is done when the A* object is about to be destroyed // The path is returned and then this function will be terminated if (queue.IsTerminating) { p.Error(); } } totalTicks += System.DateTime.UtcNow.Ticks - startTicks; p.duration = totalTicks * 0.0001F; #if ProfileAstar System.Threading.Interlocked.Increment(ref AstarPath.PathsCompleted); System.Threading.Interlocked.Add(ref AstarPath.TotalSearchedNodes, p.searchedNodes); System.Threading.Interlocked.Add(ref AstarPath.TotalSearchTime, totalTicks); #endif } // Cleans up node tagging and other things p.Cleanup(); AstarProfiler.StartFastProfile(9); if (p.immediateCallback != null) { p.immediateCallback(p); } if (OnPathPostSearch != null) { OnPathPostSearch(p); } // Push the path onto the return stack // It will be detected by the main Unity thread and returned as fast as possible (the next late update hopefully) returnQueue.Enqueue(p); // Will advance to ReturnQueue p.AdvanceState(PathState.ReturnQueue); AstarProfiler.EndFastProfile(9); // Wait a bit if we have calculated a lot of paths if (System.DateTime.UtcNow.Ticks > targetTick) { Thread.Sleep(1); targetTick = System.DateTime.UtcNow.Ticks + maxTicks; } } #if !ASTAR_FAST_BUT_NO_EXCEPTIONS } catch (System.Exception e) { #if !NETFX_CORE if (e is ThreadAbortException || e is ThreadControlQueue.QueueTerminationException) #else if (e is ThreadControlQueue.QueueTerminationException) #endif { if (astar.logPathResults == PathLog.Heavy) { Debug.LogWarning("Shutting down pathfinding thread #" + threadInfo.threadIndex); } return; } Debug.LogException(e); Debug.LogError("Unhandled exception during pathfinding. Terminating."); //Unhandled exception, kill pathfinding queue.TerminateReceivers(); } #endif Debug.LogError("Error : This part should never be reached."); queue.ReceiverTerminated(); }
public PathProcessor (AstarPath astar, PathReturnQueue returnQueue, int processors, bool multithreaded) { this.astar = astar; this.returnQueue = returnQueue; if (processors < 0) { throw new System.ArgumentOutOfRangeException("processors"); } if (!multithreaded && processors != 1) { throw new System.Exception("Only a single non-multithreaded processor is allowed"); } // Set up path queue with the specified number of receivers queue = new ThreadControlQueue(processors); threadInfos = new PathThreadInfo[processors]; for (int i = 0; i < processors; i++) { threadInfos[i] = new PathThreadInfo(i, astar, new PathHandler(i, processors)); } if (multithreaded) { threads = new Thread[processors]; // Start lots of threads for (int i = 0; i < processors; i++) { var threadIndex = i; var thread = new Thread (() => CalculatePathsThreaded(threadInfos[threadIndex])); thread.Name = "Pathfinding Thread " + i; thread.IsBackground = true; threads[i] = thread; thread.Start(); } } else { // Start coroutine if not using multithreading threadCoroutine = CalculatePaths(threadInfos[0]); } }
/** Main pathfinding method. * This method will calculate the paths in the pathfinding queue. * * \see CalculatePathsThreaded * \see StartPath */ IEnumerator CalculatePaths (PathThreadInfo threadInfo) { int numPaths = 0; // Initialize memory for this thread PathHandler runData = threadInfo.runData; if (runData.nodes == null) throw new System.NullReferenceException ("NodeRuns must be assigned to the threadInfo.runData.nodes field before threads are started\n" + "threadInfo is an argument to the thread functions"); // Max number of ticks before yielding/sleeping long maxTicks = (long)(astar.maxFrameTime*10000); long targetTick = System.DateTime.UtcNow.Ticks + maxTicks; while (true) { //The path we are currently calculating Path p = null; AstarProfiler.StartProfile ("Path Queue"); //Try to get the next path to be calculated bool blockedBefore = false; while (p == null) { try { p = queue.PopNoBlock(blockedBefore); blockedBefore |= p == null; } catch (ThreadControlQueue.QueueTerminationException) { yield break; } if (p == null) { AstarProfiler.EndProfile (); yield return null; AstarProfiler.StartProfile ("Path Queue"); } } AstarProfiler.EndProfile (); AstarProfiler.StartProfile ("Path Calc"); //Max number of ticks we are allowed to continue working in one run //One tick is 1/10000 of a millisecond maxTicks = (long)(astar.maxFrameTime*10000); p.PrepareBase (runData); //Now processing the path //Will advance to Processing p.AdvanceState (PathState.Processing); // Call some callbacks // It needs to be stored in a local variable to avoid race conditions var tmpOnPathPreSearch = OnPathPreSearch; if (tmpOnPathPreSearch != null) tmpOnPathPreSearch (p); numPaths++; //Tick for when the path started, used for calculating how long time the calculation took long startTicks = System.DateTime.UtcNow.Ticks; long totalTicks = 0; AstarProfiler.StartFastProfile(8); AstarProfiler.StartFastProfile(0); //Prepare the path AstarProfiler.StartProfile ("Path Prepare"); p.Prepare (); AstarProfiler.EndProfile ("Path Prepare"); AstarProfiler.EndFastProfile (0); // Check if the Prepare call caused the path to complete // If this happens the path usually failed if (!p.IsDone()) { //For debug uses, we set the last computed path to p, so we can view debug info on it in the editor (scene view). astar.debugPath = p; //Initialize the path, now ready to begin search AstarProfiler.StartProfile ("Path Initialize"); p.Initialize (); AstarProfiler.EndProfile (); //The error can turn up in the Init function while (!p.IsDone ()) { // Do some work on the path calculation. // The function will return when it has taken too much time // or when it has finished calculation AstarProfiler.StartFastProfile(2); AstarProfiler.StartProfile ("Path Calc Step"); p.CalculateStep (targetTick); AstarProfiler.EndFastProfile(2); p.searchIterations++; AstarProfiler.EndProfile (); // If the path has finished calculation, we can break here directly instead of sleeping // Improves latency if (p.IsDone ()) break; AstarProfiler.EndFastProfile(8); totalTicks += System.DateTime.UtcNow.Ticks-startTicks; // Yield/sleep so other threads can work AstarProfiler.EndProfile (); yield return null; AstarProfiler.StartProfile ("Path Calc"); startTicks = System.DateTime.UtcNow.Ticks; AstarProfiler.StartFastProfile(8); //Cancel function (and thus the thread) if no more paths should be accepted. //This is done when the A* object is about to be destroyed //The path is returned and then this function will be terminated (see similar IF statement higher up in the function) if (queue.IsTerminating) { p.Error (); } targetTick = System.DateTime.UtcNow.Ticks + maxTicks; } totalTicks += System.DateTime.UtcNow.Ticks-startTicks; p.duration = totalTicks*0.0001F; } // Cleans up node tagging and other things p.Cleanup (); AstarProfiler.EndFastProfile(8); // Call the immediate callback // It needs to be stored in a local variable to avoid race conditions var tmpImmediateCallback = p.immediateCallback; if ( tmpImmediateCallback != null ) tmpImmediateCallback (p); AstarProfiler.StartFastProfile(13); // It needs to be stored in a local variable to avoid race conditions var tmpOnPathPostSearch = OnPathPostSearch; if (tmpOnPathPostSearch != null) tmpOnPathPostSearch (p); AstarProfiler.EndFastProfile(13); //Push the path onto the return stack //It will be detected by the main Unity thread and returned as fast as possible (the next late update) returnQueue.Enqueue (p); p.AdvanceState (PathState.ReturnQueue); AstarProfiler.EndProfile (); //Wait a bit if we have calculated a lot of paths if (System.DateTime.UtcNow.Ticks > targetTick) { yield return null; targetTick = System.DateTime.UtcNow.Ticks + maxTicks; numPaths = 0; } } //Debug.LogError ("Error : This part should never be reached"); }
/** Main pathfinding method (multithreaded). * This method will calculate the paths in the pathfinding queue when multithreading is enabled. * * \see CalculatePaths * \see StartPath * * \astarpro */ void CalculatePathsThreaded (PathThreadInfo threadInfo) { try { //Initialize memory for this thread PathHandler runData = threadInfo.runData; if (runData.nodes == null) throw new System.NullReferenceException ("NodeRuns must be assigned to the threadInfo.runData.nodes field before threads are started\nthreadInfo is an argument to the thread functions"); //Max number of ticks before yielding/sleeping long maxTicks = (long)(astar.maxFrameTime*10000); long targetTick = System.DateTime.UtcNow.Ticks + maxTicks; while (true) { //The path we are currently calculating Path p = queue.Pop(); //Max number of ticks we are allowed to continue working in one run //One tick is 1/10000 of a millisecond maxTicks = (long)(astar.maxFrameTime*10000); //Trying to prevent simple modding to allow more than one thread if ( threadInfo.threadIndex > 0 ) { throw new System.Exception ("Thread Error"); } AstarProfiler.StartFastProfile (0); p.PrepareBase (runData); //Now processing the path //Will advance to Processing p.AdvanceState (PathState.Processing); //Call some callbacks if (OnPathPreSearch != null) { OnPathPreSearch (p); } //Tick for when the path started, used for calculating how long time the calculation took long startTicks = System.DateTime.UtcNow.Ticks; long totalTicks = 0; //Prepare the path p.Prepare (); AstarProfiler.EndFastProfile (0); if (!p.IsDone()) { //For debug uses, we set the last computed path to p, so we can view debug info on it in the editor (scene view). astar.debugPath = p; AstarProfiler.StartFastProfile (1); //Initialize the path, now ready to begin search p.Initialize (); AstarProfiler.EndFastProfile (1); //The error can turn up in the Init function while (!p.IsDone ()) { //Do some work on the path calculation. //The function will return when it has taken too much time //or when it has finished calculation AstarProfiler.StartFastProfile (2); p.CalculateStep (targetTick); p.searchIterations++; AstarProfiler.EndFastProfile (2); // If the path has finished calculation, we can break here directly instead of sleeping if (p.IsDone ()) break; // Yield/sleep so other threads can work totalTicks += System.DateTime.UtcNow.Ticks-startTicks; Thread.Sleep(0); startTicks = System.DateTime.UtcNow.Ticks; targetTick = startTicks + maxTicks; // Cancel function (and thus the thread) if no more paths should be accepted. // This is done when the A* object is about to be destroyed // The path is returned and then this function will be terminated if (queue.IsTerminating) { p.Error (); } } totalTicks += System.DateTime.UtcNow.Ticks-startTicks; p.duration = totalTicks*0.0001F; } // Cleans up node tagging and other things p.Cleanup (); AstarProfiler.StartFastProfile (9); if ( p.immediateCallback != null ) p.immediateCallback (p); if (OnPathPostSearch != null) { OnPathPostSearch (p); } // Push the path onto the return stack // It will be detected by the main Unity thread and returned as fast as possible (the next late update hopefully) returnQueue.Enqueue(p); // Will advance to ReturnQueue p.AdvanceState (PathState.ReturnQueue); AstarProfiler.EndFastProfile (9); // Wait a bit if we have calculated a lot of paths if (System.DateTime.UtcNow.Ticks > targetTick) { Thread.Sleep(1); targetTick = System.DateTime.UtcNow.Ticks + maxTicks; } } } catch (System.Exception e) { #if !NETFX_CORE if (e is ThreadAbortException || e is ThreadControlQueue.QueueTerminationException) #else if (e is ThreadControlQueue.QueueTerminationException) #endif { if (astar.logPathResults == PathLog.Heavy) Debug.LogWarning ("Shutting down pathfinding thread #"+threadInfo.threadIndex); return; } Debug.LogException (e); Debug.LogError ("Unhandled exception during pathfinding. Terminating."); //Unhandled exception, kill pathfinding queue.TerminateReceivers(); } Debug.LogError ("Error : This part should never be reached."); queue.ReceiverTerminated (); }