Windows Forms Invoke causing stack overflow? - c#

I'm making a memory reader for a game, and I've got an almost infinite thread running in the background which checks for the players position, and then displays it on a label by using Invoke(). I'll only post the offending function. This gets called on the same thread every 10 ms.
Invoke((MethodInvoker)delegate
{
lblCoords.Text = "Player Coordinates: < " + (int)x + ", " + (int)y + ", " + (int)z + " >";
});
After the code has been running for about 20 minutes, it will crash and throw a StackOverflowException related to this function. Why is it happening and how can I stop it? Obviously I could just stop using a label to show it, though it would be more useful to know why it's happening for future reference.
So this is the thread method, someone mentioned that it's multiple objects getting created at once, I'm going to assume it's this because it is an infinite loop of calling UpdateThread()... Should this have a while loop instead of calling itself?
private void UpdateThread()
{
if (!running) return;
ReadPos();
Thread.Sleep(100);
UpdateThread();
}
private void ReadPos()
{
int pointerAddress = Memory.HexToDec(MemoryOffsets.PlayerPosAddress);
byte[] xVal = memory.PointerRead((IntPtr)pointerAddress, 4, MemoryOffsets.PlayerX);
byte[] yVal = memory.PointerRead((IntPtr)pointerAddress, 4, MemoryOffsets.PlayerY);
byte[] zVal = memory.PointerRead((IntPtr)pointerAddress, 4, MemoryOffsets.PlayerZ);
float x = BitConverter.ToSingle(xVal, 0);
float y = BitConverter.ToSingle(yVal, 0);
float z = BitConverter.ToSingle(zVal, 0);
Invoke((MethodInvoker)delegate
{
lblCoords.Text = "Player Coordinates: < " + (int)x + ", " + (int)y + ", " + (int)z + " >";
});
}
The error the program was showing me pointed at the Invoke method, which is why I thought it was just that causing it. Since it takes about 20 minutes for the exception to occur, I can't get too much information about it.

Should this have a while loop instead of calling itself? Yes, by all means.
Sanly C# will not generate tail calls for this code, meaning it will stack the calls of UpdateThread until the call stack is full. This is causing the StackOverflowException.
So, you would implement something like this:
private void UpdateThread()
{
while (running) //I'm assuming running is a volatile variable
{
ReadPos();
Thread.Sleep(100);
}
}
Note: It is good idea to make running volatile, as it will be set by another thread (I presume).
You could also consider to post the execution to the thread loop or a similar scheduling mechanism instead of having a dedicated thread, on that regard, do your testing to see what works best.
Another thing to consider is to keep your delegate object alive, it will reduce pressure on the garbage collector (since you are currently creating too many short lived objects), but it is not the cause of the StackOverflowException.

Yes, you should use a loop instead.
You are using recursion to make a loop, and as the loop doesn't have a reasonable limit it will fill up the stack with call frames.
Just loop inside the method:
private void UpdateThread()
{
while (running) {
ReadPos();
Thread.Sleep(100);
}
}

Related

Counter not updating on unity UI but the variable is

In the fishing mini game I'm working on, each unique fish that the player catches is written to a "journal" type UI. If the player has caught that type again, instead of writing a whole new entry to the UI, a score increments by 1 to show how many times they've caught that type of fish.
I'm using scriptable objects to store the information about each fish (name, size, a picture, etc) along with the number of times it's been caught. I can see in the inspector that the variable is increasing each time that fish is caught and I'm also debugging a message each time a unique fish has been caught as well as the number of times.
The UI seems to update the number of times caught the first time, however, however it seems to always stay at 1 for every time that fish is caught afterwards, even though the variable that I'm writing to the UI is changing.
Here's the code that updates the UI:
public void UpdateFishPages(Fish fish)
{
if (!CheckIfExists(fish))
{
Debug.Log("New Fish: " + fish.fishName + " " + "caught ");
Debug.Log("Fish index: " + fishIndex);
fishCaught.Add(fish);
fishes[fishIndex].SetActive(true);
fishes[fishIndex].GetComponent<Image>().sprite = fish.image;
fishes[fishIndex].transform.GetChild(0).transform.GetChild(0).GetComponent<TMPro.TextMeshProUGUI>().text = fish.fishName;
fishes[fishIndex].transform.GetChild(0).transform.GetChild(1).GetComponent<TMPro.TextMeshProUGUI>().text = fish.fishDesc;
fishes[fishIndex].transform.GetChild(0).transform.GetChild(2).GetComponent<TMPro.TextMeshProUGUI>().text = fish.fishSize;
string newFishCount = fish.timesCaught.ToString();
Debug.Log(newFishCount);
fishes[fishIndex].transform.GetChild(1).transform.GetChild(0).GetComponent<TMPro.TextMeshProUGUI>().text = newFishCount;
fishIndex++;
}
else
{
string newFishCount = fish.timesCaught.ToString();
Debug.Log(fish.fishName + " now caught " + fish.timesCaught + " times!");
fishes[fishIndex].transform.GetChild(1).transform.GetChild(0).GetComponent<TMPro.TextMeshProUGUI>().text = newFishCount;
}
}
bool CheckIfExists(Fish fish)
{
if (fishCaught.Contains(fish))
{
return true;
}
else
{
return false;
}
}
and here's how I'm calling that method when a player catches the fish:
if (Input.GetKeyDown(KeyCode.E) || Input.GetButtonDown("Button A"))
{
if (timer < 1f)
{
int randomFish = Random.Range(1, 5);
playerAnimator.SetInteger("fishing_randomIndex", randomFish);
fish[randomFish - 1].timesCaught++;
journal.UpdateFishPages(fish[randomFish - 1]);
//Debug.Log("caught");
timer = 0;
fishStage = 2;
}
}
else if (timer > 1f)
{
//Debug.Log("Didn't reel");
playerAnimator.SetTrigger("fishing_fail");
}
I'm hoping someone can shed some light onto what I might be doing wrong.
Many thanks for your time and help!

Coroutine only firing once

I'm trying to run a script that will generate a "ramps" upon touch between point a and b. This code receives a list of where the elements of the ramps should be and then instanciates and places them on the screen.
However the coroutine is only running once and I can't understand why. Can anyone give me some advice?
Thank you very much in advance
public IEnumerator CreateRamp(List<Vector3> lP, float angle)
{
int i = 1;
while (i <= lP.Count)
{
Debug.Log("Iteration " + i + " of " + lP.Count + " position is " + lP[i]);
GameObject item = Instantiate(Resources.Load("floor")) as GameObject;
item.transform.position = current_Position;
item.transform.eulerAngles = new Vector3(0, 0, UnityEngine.Random.Range(0f, 360f));
item.GetComponent<Ramp>().strtPos = item.transform.position;
item.GetComponent<Ramp>().strtRot = item.transform.eulerAngles;
item.GetComponent<Ramp>().strtScale = new Vector3(0.4f, 0.4f, 1);
item.GetComponent<Ramp>().tgtRot = new Vector3(0, 0, angle);
item.GetComponent<Ramp>().tgtPos = lP[i-1];
i += 1;
yield return new WaitForSeconds(0.2f);
}
}
I suspect your condition i <= lP.Count is true only once. (Maybe lP.Count == 1, I think).
The way co-routine works is that, the code inside the CreateRamp function is executed across multiple frames.
When you StartCoroutine(CreateRamp(...)), it is immediately run until it hits yield statement. It will wait there for 0.2 seconds and will be run again from the statement right after the yield.
In the second execution, it evaluates the condition i <= lP.Count again and see that it is False => it jumps out of the loop and because it hits the end of the function, that co-routine will be stopped, no more execution in the future.
Since this function is an IEnumerable it should be treated by other code as a list of Ramp objects. I suspect (there isn't enough of your code to know), that the way you are calling this function is incorrect.
On a side note, with your yield returning a waitforX, It would be better in the long term to either perform the wait outside of this function (where you are calling it from) or at the very least add the wait period as a parameter to the function. Hard coded values like that will end up biting you later on, especially if your game's code-base grows. I recommend it be exposed as a setting on your GameObject, so it can be tweaked from the editor.
One other thing, How do you destroy these Ramp objects when you are done with them? It might be good to consider storing references to them as you create them so you can destroy them later.

Retrieving data from a database in another thread(Unity3D)

I currently have a code which retrieves data from a database and visualizes it in unity3D. However, everytime it retrieves data in the FixedUpdate() function, it spikes dramatically every 1 second. I'm thinking about using threading to do this but i'm not sure what i'm doing wrong.
This is the Function i call in the thread.
public void retrievefromDB(){
if (timeStep - prevTimeStep > 99) {
timeStep -= 1; //special for this dataset
query = "SELECT * FROM GridData2 WHERE timestep=" + timeStep;
if (showParent)
query += " AND (Level != 10)";
else
query += " AND (Level == 10)";
query += " AND temperature >= " + minTemp + " AND temperature <= " + maxTemp;
dt.Rows.Clear ();
dt = sqlDB.ExecuteQuery (query);
prevTimeStep = timeStep;
}
}
This code lags the scene every 1 second therefore i tried to put it into a thread.
void FixedUpdate()
{
Thread testthread = new Thread(new ThreadStart(retrievefromDB));
testthread.Start ();
}
After putting it in a thread, it keeps crashing the scene after awhile.
Can anyone tell me what I did wrongly? And how do i solve it?
The cause of your original issue is relatively obvious: database access is slow. If you put a database call inline in the FixedUpdate method, you're going to essentially pause your game's movement while the DB access happens (which may well take a second if you have to initialise a connection, for example).
The main issue with your threaded code as posted is that you are starting a new thread every time FixedUpdate is called. That means you're starting 60 new threads per second (by default) which will very quickly cripple your game!
While it's fine to use C# threads in Unity for this sort of work, a better approach would be to create a single thread and allow that to manage the timing, rather than creating a new thread each time the job runs. That would mean creating the thread in Awake() or Start() instead, and then using Thread.Sleep or similar to handle the timing.
Coroutines (as suggested by Mihai in his answer) are great for fixing the timing of events, but they still run on the game thread: if you put your DB code in a coroutine, you'll still see pauses when it runs. If you must run this DB access every second, you need it in a proper thread.
That said, have you considered that the DB access might be unnecessary? A more performant model might be to cache all of the data up front and use it from memory when you need it. (This might not be possible if the data is very dynamic, or if you're running in a memory-restricted environment like a mobile device...)
Whatever you do, you need to stop accessing your database every frame.
You only need the result only once every 60 or frames. You can do this easily by using a variable in which you add up the time passed since last call.
As for multi-threading in Unity, you have three options:
A multi-threading framework for Unity, like
https://www.assetstore.unity3d.com/en/#!/content/7285
C# built-in threading
You need to be careful not to call Unity specific API from the secondary threads you spawn. It's OK to send over data structures like Vector3, Color, etc., but don't call reference objects like GameObjects or Components.
https://msdn.microsoft.com/en-us/library/dd321439%28v=vs.110%29.aspx
Unity Coroutines
Coroutines are Unity's way of simulating multiple threads. It's quite a powerful tool for getting things to run asynchronously in the same thread
http://docs.unity3d.com/Manual/Coroutines.html
using System.Threading.Tasks;
public class Example
{
void StartOnDifferentThread()
{
Task.Factory
.StartNew(() =>
{
FunctionToRun();
})
.ContinueWith(task =>
{
if (task.IsCompleted)
{
// handle result
}
else if (task.IsFaulted)
{
// handle error
}
});
}
void FunctionToRun()
{
// do stuff
}
}
It finally works now. Just had to add these in retrievefromDB()
public void retrievefromDB(){
while(true){
if (timeStep - prevTimeStep > 99) {
timeStep -= 1; //special for this dataset
query = "SELECT * FROM GridData2 WHERE timestep=" + timeStep;
if (showParent)
query += " AND (Level != 10)";
else
query += " AND (Level == 10)";
query += " AND temperature >= " + minTemp + " AND temperature <= " + maxTemp;
dt.Rows.Clear ();
dt = sqlDB.ExecuteQuery (query);
prevTimeStep = timeStep;
}
Thread.Sleep(1);
}
}
And put this into the Start() function
testThread = UnityThreadHelper.CreateThread (() =>
{
UnityThreadHelper.TaskDistributor.Dispatch (() => retrievefromDB ());
});
testThread.Start ();
I'm using the threadhelper from
http://forum.unity3d.com/threads/unity-threading-helper.90128/
so u can go and check it out.
Thanks to everyone who helped! :)

Unity IEnumerator, waiting but no longer executing called function fully anymore

I am writing a game with some logic that records player actions and then replays them in the same sequence at the same timing but with other influences involved at a later date.
So far I have the code sort of working, it records perfectly as I require it, and then plays back the sequence perfectly, but the issue has been timing.
As an simple example. If the character is to walk from A -> B -> C but to wait for x time at B, then the code easily traverses the path, but will not stop at B for the given time.
So I wrote a couple of functions that I thought might handle this as you can see here.
private IEnumerator followSequence()
{
// Loop through each action steps in sequence.
foreach (Action step in sequence)
{
//Check if the action time is less than the current clock time.
Debug.Log("Step.tick: " + step.tick + " || Clock.tick: " + clock.getTick());
if (clock.getTick() < step.tick)
{
//If so wait out the difference to resync the action.
yield return new WaitForSeconds(step.tick - clock.getTick());
}
// Carry out the step.
play(step);
}
yield break;
}
private void play(Action step)
{
Debug.Log(step.type);
// Set up a followpath script
FollowPath follow = (player.GetComponent<FollowPath>() ? player.GetComponent<FollowPath>() : player.AddComponent<FollowPath>());
// Tell the follow path script that object is not a guard.
follow.isGuard = false;
// Create a new point object on the map
Transform aPoint = (Transform)Instantiate(PathPoint, step.point, Quaternion.identity);
path.points.Add(aPoint);
// Initiate movement.
follow.Path = path;
follow.TravelPath();
// Check if action is an objective.
if (step.type == Action.Type.Action)
{
step.actions.OnClick();
}
}
Now if I comment out the *yield return new WaitForSeconds()* part in the first function, the code works as expected and completes the step in the sequence just not to the given timing.
When the same code is not commented out, then the timings work perfectly and the 2nd *Debug.log("step.type")* is called at the exact right time, however the character no longer moves, I.E. the *follow.TravelPath()* never seems to be executed.
I have tried making the 2nd function an IEnmerator and yielding to it. I have tried a custom wait() function. I have tried yielding to the TravelPath() function.
Has anyone any more ideas I have follow up or where I could look to try and resolve this?
Edit:
Code that calls followSequence() as requested:
private void playActions()
{
// Reset player immediately to the starting position
player.transform.position = sequence[0].point;
// Stop the character and freeze the scene when alarm sounds!.
player.GetComponent<IncrementController>().freeze();
player.GetComponent<IncrementController>().enabled = false;
// Restart the clock and scene.
clock.Restart();
sceneManager.restart();
// Activate guard and remove sphere.
guard.SetActive(true);
guardShere.SetActive(false);
// Stop recording;
recording = false;
line.SetActive(false);
// Start playback sequence.
StartCoroutine(followSequence());
}
The behavior is as expected, on the line there you do yield return the execution of the context is paused until you do MoveNext() (which does apparently never happen) on the enumerator, which is returned by followSequence.
But as far as I can see from the comments, you just want to wait for some seconds for resync so you don't need to return at all. In your case I don't see any reason why you might need an Enumerable of WaitForSeconds if you just want to skip some time. Without to know how you you call the function and the implementation of WaitForSeconds I would suggest you to change it's return type to void.
private void followSequence()
{
// Loop through each action steps in sequence.
foreach (Action step in sequence)
{
//Check if the action time is less than the current clock time.
Debug.Log("Step.tick: " + step.tick + " || Clock.tick: " + clock.getTick());
if (clock.getTick() < step.tick)
{
//If so wait out the difference to resync the action.
new WaitForSeconds(step.tick - clock.getTick());
}
// Carry out the step.
play(step);
}
}

Gtk.Application.Invoke is not working

I am trying to build an application on MONO using Backgroundworker. My application is working fine when i use the sequential method. Basically my application draws some rectangles the on the drawing area on click of the button.
I am trying to compare the difference in execution time between sequential, using Backgroundworker and others.
I have a problem in using background worker, have a look at the following code,
static void DoWork (object sender, DoWorkEventArgs e)
{
Context ct = (Context)e.Argument;
house.DrawHouse rect = new house.DrawHouse ();
PointD p1, p2, p3, p4;
p1 = new PointD (55, 250);
p2 = new PointD (65, 250);
p3 = new PointD (65, 90);
p4 = new PointD (55, 90);
Gtk.Application.Invoke (delegate {
ct.MoveTo (p1);
ct.LineTo (p2);
ct.LineTo (p3);
ct.LineTo (p4);
ct.LineTo (p1);
ct.ClosePath ();
ct.Color = new Color (0, 0, 0);
ct.FillPreserve ();
ct.Color = new Color (255, 255, 255);
ct.Stroke ();
});
}
}
in the above code worker thread is creating creating the rectangle and giving to the GUI thread to print it. but it is throwing an error in the ct.MoveTo (p1);
Hope to hear from some one soon.
Are you sure you're calling that code in the GUI thread? To find out, you could use this tool.
Also, let me guess: you're running this on Windows, aren't you? I got this impression because AccessViolationExceptions happen more often on MS.NET than on Mono (somehow, the interoperability with the unmanaged world is less strict on Mono).
This has been brought up sometimes in bugs, but not yet fixed. In MS.NET these issues are normally fixed by applying proper calling conventions. An example is this, maybe you're missing some of this? (Or Cairo's binding? be sure to clone gtk-sharp and have a look at the DllImport being called; maybe you can spot a bug there and send a pull request to fix it?)
You are correct. Yes, I am running this on windows. and for the first point of yours, worker thread will start the task and create the rectangle and gives to GUI thread to print it on the screen. (Gtk.Application.Invoke (delegate { }); this will switch to GUI thread)
Have a look at the following code I am doing the same thing but not in perspective of painting but it does some time consuming calculation. The calculation which is time consuming is done by the worker thread and giving the result to GUI thread to print on the UI. This is absolutely working fine for me with out any error.
void DoWork (object sender, DoWorkEventArgs e)
{
BackgroundWorker worker = sender as BackgroundWorker;
var watch = Stopwatch.StartNew ();
for (int i = lowerLimit; i <= upperLimit; i++) {
int j = i;
var result = SumRootN (j);
Gtk.Application.Invoke (delegate {
textview15.Buffer.Text += "root " + j.ToString () + " = " + result.ToString () + Environment.NewLine;
});
worker.ReportProgress ((int)(((double)i / (double)upperLimit) * 100));
}
var time = watch.ElapsedMilliseconds;
textview20.Buffer.Text = "Background Worker: \n execution took " + time + " MS to \n complete the execution \n SLOW & RESPONSIVE";
}
But if i do the same thing on for painting it is giving me the error i attached above.
Please correct me if i am wrong
thanks for the reply knocte, I will have look at your response.

Categories