Example #1
0
        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]);
            }
        }
Example #2
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 ();
			}
		}
		
	}
Example #3
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;
		
		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]));
		
	}
Example #4
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;
		}
Example #5
0
		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();
		}
Example #6
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;

                                        #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;
                }
            }
        }
Example #7
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");
		}
Example #10
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) {

			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 ();
		}