I am trying to send a post request to an API. I made a coroutine with a “yield return request.SendWebRequest()” statement and a callback function gets executed afterwards.
IEnumerator Post(string url, string requestString, Action<string> callback)
{
var request = new UnityWebRequest(url, "POST");
byte[] bodyRaw = Encoding.UTF8.GetBytes(requestString);
request.uploadHandler = new UploadHandlerRaw(bodyRaw);
request.downloadHandler = new DownloadHandlerBuffer();
request.SetRequestHeader("Content-Type", "application/json");
Debug.Log("before");
yield return request.SendWebRequest();
Debug.Log("after");
callback(request.downloadHandler.text);
}
However, the debug log with “before” in it gets executed. The debug log with “after” in it is never executed, nor is the callback function and Unity freezes. No errors are thrown in the console.
If I comment out the callback function, the debug log with “after” in it works and Unity does not freeze. I used the content of the callback function outside of this coroutine and it works fine.
I have no idea why this happens and I did not find any other threads with a solution.
Some help is appreciated. Thank you!
Can you show the callback function? It seems highly likely that there is an infinite loop in that function. That would explain all of the behavior you are seeing.
You said you tried out outside this coroutine, sure, but it may be specifically that the data returned by this coroutine causes that function to enter an infinite loop. Please share the callback code.
Also try simply printing out the downloaded data and not running the callback. See what happens then.
This is the callback function. It’s really big chunk of code, sorry.
if (!string.IsNullOrEmpty(result))
{
MapquestJsonObject obj = JsonConvert.DeserializeObject<MapquestJsonObject>(result);
for (int i = 0; i < obj.locations.Count; i++)
{
Vector2 latLng = new Vector2((float)obj.locations[i].latLng.lng, (float)obj.locations[i].latLng.lat);
fullAddressSet.Add(new Address(i, "", latLng, i == 0 ? 0 : UnityEngine.Random.Range(1800f, 7200f)));
}
costMatrix = obj.time;
if (debugMode)
{
Debug.Log("fullAddressSet: " + AddressListToString(fullAddressSet));
foreach (Address item in fullAddressSet)
{
Debug.Log(item.ToString());
}
string str = "";
foreach (var item in costMatrix)
{
str += "[";
foreach (var item2 in item)
{
str += item2 + " ";
}
str += "]\n";
}
Debug.Log("costmatrix:\n" + str);
}
List<Address> fullSetWithoutStart = new List<Address>(fullAddressSet);
fullSetWithoutStart.RemoveAt(0);
double lowestCost = Mathf.Infinity;
int highestAddressAmount = 0;
List<Tour> finalDays = new List<Tour>();
int bestSeed = -1;
for (int i = 0; i < seedAmount; i++)
{
if (debugMode) Debug.Log("-------------- seed " + i + " --------------");
List<Vector2> latLngList = fullSetWithoutStart.Select(o => o.location).ToList();
int[][] clusters = KMeans.Cluster(latLngList.ToArray(), daysAmount, 50, i).clusters;
double cost = 0;
int addressAmount = 0;
List<Tour> tmp = new List<Tour>();
foreach (int[] cluster in clusters)
{
if (debugMode) Debug.Log("----------- cluster " + Array.IndexOf(clusters, cluster) + " -----------");
List<Address> addressCluster = cluster.Select(o => fullSetWithoutStart[o]).ToList();
float start = Time.realtimeSinceStartup;
Tour bestTour = GetBestTour(addressCluster);
float end = Time.realtimeSinceStartup;
bestTour = new Tour(bestTour.addressList.Prepend(fullAddressSet[0]).ToList());
Tour clampedTour = GetClampedTour(bestTour, maxDayCost);
if (debugMode)
{
Debug.Log("execution time: " + (end - start).ToString("F2") + " seconds");
Debug.Log("bestTour: " + AddressListToString(bestTour.addressList) + ", cost: " + bestTour.cost);
Debug.Log("clampedTour: " + AddressListToString(clampedTour.addressList) + ", cost: " + clampedTour.cost);
}
addressAmount += clampedTour.addressList.Count;
cost += clampedTour.cost;
tmp.Add(clampedTour);
}
if (addressAmount < highestAddressAmount)
{
continue;
}
else if (addressAmount == highestAddressAmount)
{
if (cost >= lowestCost)
{
continue;
}
}
if (debugMode) Debug.Log("seed " + i + " is current best\naddressAmount: " + addressAmount + "\ncost: " + cost);
highestAddressAmount = addressAmount;
lowestCost = cost;
bestSeed = i;
finalDays = tmp;
}
string temp = "";
foreach (var item in finalDays)
{
temp += AddressListToString(item.addressList) + "\n";
}
Debug.Log("~~~~~~ best days ~~~~~~\nseed: " + bestSeed + "\n\n" + temp + "\nAddress Amount: " + highestAddressAmount + "\nCost: " + lowestCost + "\n");
}
else
{
Debug.Log("Post request returned empty response");
}
The only while-loop there is, is the following. It is inside the function “GetBestTour” used in the code above on line 59.
public Tour GetBestTour(List<Address> addressList)
{
float timeStamp = Time.realtimeSinceStartup;
r = new Random();
Tour dest = new Tour(addressList);
Population p = Population.randomized(dest, GlobalVariables.popSize);
int gen = 0;
bool better = true;
while (gen < GlobalVariables.maxIterations)
{
if (better && debugMode) display(p, gen);
if (Time.realtimeSinceStartup - timeStamp > maxExecutionTime / (daysAmount * seedAmount))
{
if (debugMode) Debug.Log("Generation amount: " + gen);
break;
}
better = false;
double oldFit = p.maxFit;
p = p.evolve();
if (p.maxFit > oldFit) better = true;
gen++;
}
return p.findBest();
}
“GlobalVariables.maxIterations” is set to 100.
It prints out the data correctly if I don’t run the callback.
This code is pretty complicated, the best advice I have is to hook up the debugger and follow the code line by line until you find where it’s getting hung up. Put breakpoints inside all your loops.
Switching from while to for won’t inherently solve infinite loops (though foreach usually will). You have to address whatever is causing the loop never to exit.
Update:
Thanks to the advice above I found the following.
Somewhere in my code there is this while loop:
while (true)
{
int i = GeneticAlgorithm.r.Next(0, GlobalVariables.popSize);
if (GeneticAlgorithm.r.NextDouble() < this.tourList[i].fitness / this.maxFit)
return new Tour(this.tourList[i].addressList);
}
“this.maxFit” is calculated by 1 divided by a cost value. Due to a very rare case of calculating the cost of an empty list, this cost value is zero. As we know, 1 divided by 0 returns positive infinity.
“GeneticAlgorithm.r.NextDouble()” is a random value between 0 and 1. Because “this.maxFit” is positive infinity, it causes “this.tourList*.*fitness / this.maxFit” to be 0. This results in the if statement never to be true. So the return statement is never hit and the while loop keeps going and going.
I edited the code so that an empty list never gets to this while loop and now Unity doesn’t crash anymore.