2

I am using a jagged array to hold the map segments (Clusters) in a game I am making. The position in the array corresponds to the Cluster's position in the map. Furthermore, if the inner array (the jagged part) no longer has any Clusters loaded into it (Clusters are removed from the array after a not-used timeout) that element of the outer array is set back to null to keep the map's memory usage down.

The problem arises seemingly at random when the game tries to get a Cluster from the map:

public Cluster getCluster(int xIndex, int yIndex)
{
    lock (xAxis)
    {
        loadCluster(xIndex, yIndex);
        return xAxis[xIndex][yIndex];
    }
}

public void loadCluster(int xIndex, int yIndex)
{
    lock (xAxis)
    {
        if (xAxis[xIndex] == null)
            xAxis[xIndex] = new Cluster[(int)worldSize.Y];
        if (xAxis[xIndex][yIndex] == null)
            xAxis[xIndex][yIndex] = new Cluster(this, new Vector2(xIndex * 256, yIndex * 256), worldLoader.loadClusterData(xIndex, yIndex));
    }
}

The loadCluster(int, int) method should ensure the Cluster is loaded into the array before the getCluster(int, int) method retrieves it and almost all the time it does, but occasionally loadCluster(int, int) somehow fails to add the Cluster.

It doesn't seem to happen with any particular cluster, but always happens when the inner array has not been added (however, most of the time it creates the inner arrays with no problems at all). Also, when Visual Studio catches the ensuing null pointer exception, stepping back and re-calling loadCluster(int, int) always (at least so far) works as normal. Adding extra calls to loadCluster(int, int) in the getCluster(int, int) method also greatly decreases the frequency of this bug.

I honestly have no idea what is causing this rather straightforward function to not work, seemingly at random even. Any help would be much appreciated

EDIT: Other code that edits xAxis[][]

public override void Update(GameTime gameTime)
{
    for (int x = 0; x < worldSize.X; x++)
    {
        if (xAxis[x] == null) continue;
        int loaded = 0;
        for (int y = 0; y < worldSize.Y; y++)
        {
            if (xAxis[x][y] == null)
            {
                continue;
            }
            xAxis[x][y].Update(gameTime);

            if (xAxis[x][y].clusterLoaded)
            {
                loaded++;
            }
            else if (xAxis[x][y].clusterTimer == 0)
            {
                xAxis[x][y] = null;
            }
        }

        if(loaded == 0) xAxis[x] = null;
    }
}

Cluster.clusterLoaded is a bool showing if the Cluster is currently in use. Cluster.clusterTimer is an int which counts down once clusterLoaded becomes false. It is reset if to its maximum value if clusterLoaded becomes true again. It is decreased by 1 every time Cluster.Update(GameTime) is run while clusterLoaded is false. loaded is used to count how many Clusters in the current inner array are loaded.

3
  • There is no code ensuring that (int)worldSize.Y > yIndex. Please add such assertion, so to break in that case. Commented Aug 16, 2012 at 22:09
  • Can you post all code that changes the array, including the bits that remove from it - cleanup, etc.? Commented Aug 16, 2012 at 22:10
  • Well spotted, but other parts of the code so far ensures that yIndex is never greater than worldSize.Y. Though I'll add a check in regardless. In any case, the bug happens even when xIndex and yIndex are well within the limits of the array. Commented Aug 16, 2012 at 22:11

1 Answer 1

3

First thing to change here is to modify the loadCluster method to return the xAxis[xIndex][yIndex] value right from it. Then there will be no need to lock in the getCluster method. What about other sections of the code - from the description it seems to be a multi-threading issue. Although you've locked on xAxis variable, I don't know what else you do somewhere else. One more point - arrays in the language are really a core functionality, so it's really unexpected to have a bug there (I mean Microsoft).

Sign up to request clarification or add additional context in comments.

2 Comments

loadCluster only loading and not returning anything is intentional, I will likely make it private/protected. However, your multithreading point is interesting. I have a BackgroundWorker which generates a live minimap and may be interfering. I shall give it some testing.
Aha. It does indeed appear to be a threading issue. Deactivating the BackgroundWorker prevents the error from occurring. I've also managed to find the source of the problem. I hadn't put a lock around the Update method, so the BackgroundWorker must have been messing things up there. Many thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.