NullReferenceException on all runs after the first

Hello everyone!

I’m having one of those annoying NullReferenceExceptions and the peculiar thing about this bug is about is that it never occurs during the first run after i start the editor. It happens on all of the following runs after the first.
I have tracked down the error to this class but cant figure out what is wrong with it so any help would be appreciated.

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

public class MeshBuffer : MonoBehaviour
{
    private Mutex bufferLock = new Mutex();
    private static int bufferSize = 4096;
    public MeshManager original;
    private List<MeshManager> objectList = new List<MeshManager>();
 
    public void SetBufferSize(int newSize)
    {
        bufferSize = newSize;
    }
 
    public void LoadBuffer()
    {
        if(original != null)
        {
            while(objectList.Count < bufferSize)
            {
                objectList.Add((MeshManager)Instantiate(original, new Vector3(0,0,0), Quaternion.identity));
                if(objectList[objectList.Count-1] == null)
                {
                    Debug.Log("object not added1");
                }
            }
        }
        else
        {
            Debug.Log("BufferError: original object not set");
        }
    }
 
    public void Initialize()
    {
        bufferLock.WaitOne();
        LoadBuffer();
        bufferLock.ReleaseMutex();
    }
 
    public MeshManager GetObject()
    {
        MeshManager tempObj;

        bufferLock.WaitOne();
        if(objectList.Count == 0)
        {
            LoadBuffer();
        }
        tempObj = objectList[0];
        if(objectList[0] == null)
        {
            Debug.Log("object not added3");
        }

        objectList.RemoveAt(0);
        bufferLock.ReleaseMutex();
     
        return tempObj;
    }
 
    public void ReturnObject(MeshManager retObj)
    {
        bufferLock.WaitOne();
        objectList.Add(retObj);
        bufferLock.ReleaseMutex();
    }

    public void OutputContentToLog()
    {
        for(int i=0; i<objectList.Count; i++)
        {
            if(objectList == null)
            {
                Debug.Log("content check: object not added4");
            }
            else
            {
                Debug.Log("content check: object added4");
            }
        }
    }
}

LoadBuffer()
This function is used to fill the list and the “object not added1” log never occurs.

GetObject()
This fetches objects from the list for use during the run and the bug occurs when this function returns a null reference which it isn’t supposed to do.

Initialize()
Called once at the beginning of the run.

OutputContentToLog()
Called once directly after Initialize and shows that 3584 objects in the list are null.

SetBufferSize(int newSize)
not used at all currently

I have already made sure that all of my own worker threads are terminated at the end of a run.

Can you provide the NullReferenceExceptions stack trace?

I’ve added some comments in the code:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Threading;

public class MeshBuffer : MonoBehaviour
{
    private Mutex bufferLock = new Mutex();
    private static int bufferSize = 4096;
    public MeshManager original;
    private List<MeshManager> objectList = new List<MeshManager>();

    public void SetBufferSize(int newSize)
    {
        bufferSize = newSize;
    }

    public void LoadBuffer()
    {
        // *** load buffer does not do anything if original is null
        if(original != null)
        {
            while(objectList.Count < bufferSize)
            {
                objectList.Add((MeshManager)Instantiate(original, new Vector3(0,0,0), Quaternion.identity));
                if(objectList[objectList.Count-1] == null)
                {
                    Debug.Log("object not added1");
                }
            }
        }
        else
        {
            Debug.Log("BufferError: original object not set");
        }
    }

    public void Initialize()
    {
        // *** Do you really want this here? thread locking ?? typically not needed since user script code in Unity
        // *** is by default single threaded unless you're creating threads somewhere (which I would not suggest
        // *** unless it's absolutely required
        bufferLock.WaitOne();
        LoadBuffer();
        bufferLock.ReleaseMutex();
    }

    public MeshManager GetObject()
    {
        MeshManager tempObj;
        // ** More thread locks - are you sure you need/want this, I expect not
        bufferLock.WaitOne();
        if(objectList.Count == 0)
        {
            // *** LoadBuffer() is not going to do anything if original == null
            LoadBuffer();
        }
        // *** so unless original != null, objectList[0] may not be valid still
        tempObj = objectList[0];
        if(objectList[0] == null)
        {
            Debug.Log("object not added3");
        }

        objectList.RemoveAt(0);
        bufferLock.ReleaseMutex();
 
        return tempObj;
    }

    public void ReturnObject(MeshManager retObj)
    {
        // *** more locks - just seems wrong... Where are the additional threads you're guarding against?
        bufferLock.WaitOne();
        objectList.Add(retObj);
        bufferLock.ReleaseMutex();
    }

    public void OutputContentToLog()
    {
        for(int i=0; i<objectList.Count; i++)
        {
           // ** you just referenced objectList.Count in the for statement above, so that'll explode is objectList is null
           // ** this test will never get executed as it'll die before it gets here.
            if(objectList == null)
            {
                Debug.Log("content check: object not added4");
            }
            else
            {
                Debug.Log("content check: object added4");
            }
        }
    }
}

Lots of comments added there, may help.

System.NullReferenceException: Object reference not set to an instance of an object
at ChunkManager.AddTriangletoMesh (Vector3 first, Vector3 second, Vector3 third, Boolean reversed, Byte texture) [0x00040] in C:\Users\Risto\Documents\HIVE 1.0.1\Assets\Scripts\ChunkManager.cs:313

not as helpfull as i had hoped. It occurs inside a workerthread so i had to use a try catch just to see the exception.

that is why i put in the else statement with “Debug.Log(“BufferError: original object not set”);”. It is never seen in the log.

I am creating procedural content which is currently really calculation heavy . The extra threads in the background are needed to handle the calculations, and because they are asynchronous they need safeguards against tripping each other up.

The exception is in ChunkManager.cs, but you’ve shown your MeshBuffer class…

damn it. this forum eats up some characters. i cant post the correct version of the if statement. are there escape characters for square brackets?
the if statement is supposed to be if(objectList<begin squarebracket>i<end square bracket> ==null)

yes that is the first time I try to access the MeshManager. The ChunkManager isn’t suppose to be a referencing a null object. I tracked the bug to the MeshBuffer(where the ChunkManager gets the MeshManagers from with the GetObject() function) class which isn’t suppose to return null references.

Specifically, the error is at : ChunkManager.AddTriangletoMesh (Vector3 first, Vector3 second, Vector3 third, Boolean reversed, Byte texture)

You must be passing a null “texture” parameter in there. Nothing in your MeshManager script returns a byte. Show us that AddTriangleToMesh function please. Also, a texture cannot be just 1 byte.

The texture parameter is just a number which i decode later to determine which texture to put where.

If we can’t see the code here and the context around it then we have no chance of guessing what’s wrong.

Here are all of the mesh related parts of the ChunkManager:

using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;

public class ChunkManager
{
    List<MeshManager> meshes = new List<MeshManager>();
    List<int> meshVertCount = new List<int>();
    int curMesh = 0;

    public void InitializeChunk()
    {
        AddNewMesh();
    }

    public int VertCount()
    {
        int sum = 0;
        for(int i=0; i<meshVertCount.Count; i++)
        {
            sum =+ meshVertCount[i];
        }
        return sum;
    }

    public void ActivateMeshes()
    {
        for(int i = 0; i<meshes.Count; i++)
        {
            meshes[i].InitiateMesh();
            meshes[i].CreateVisualMesh();
        }
    }

    void ClearMeshes()
    {
        for(int i = 0; i<meshes.Count; i++)
        {
            meshes[i].ClearMesh();
            meshVertCount[i] = 0;
        }
        curMesh = 0;
    }

    void AddNewMesh()
    {
        meshes.Add(worldAccess.MeshBuffer.GetObject());
        meshVertCount.Add(0);
    }

    public void AddTriangletoMesh(Vector3 first, Vector3 second, Vector3 third, bool reversed, byte texture)
    {

        if(Vector3.Cross(second - first, third - first).sqrMagnitude > 0f)
        {
            if(meshVertCount[curMesh] < 64998)
            {
                try
                {
                    meshes[curMesh].AddTriangle(first, second, third, reversed, texture);
                    meshVertCount[curMesh] = meshVertCount[curMesh] + 3;
                }
                catch(Exception e)
                {
                    Debug.Log(e);
                    Debug.Log(meshes.Count);
                }
            }
            else
            {
                curMesh++;
                if(meshes.Count < curMesh)
                {
                    AddNewMesh();
                }
                AddTriangletoMesh(first, second, third, reversed, texture);
            }
        }
    }
}

I still think that the bug is in the MeshBuffer class since the debug code there indicates that it is sending null references which it isn’t supposed to.

What is line #313 in your ChunkManager script? Do you know how to use a debugger?

I’m not about to doubt you, you’ve got all the code and would know best. But for us to help debug this we’d need to trace back from the exception as you would have done :slight_smile:

Which line is the equivalent of ChunkManager.cs:313?

I assume it’ll be either 57 or 61 (?)

using UnityEngine;
    void AddNewMesh()
    {
        MeshManager newMeshManager = worldAccess.MeshBuffer.GetObject();
        Debug.Log("newMeshManager is: " (newMeshManager == null ? "NULL" : "not null");
        meshes.Add(newMeshManager);
        meshVertCount.Add(0);
    }
}

Could you just test it with that debug line in, just to see if the MeshBuffer really is returning a null ? If so, then we can be pretty certain that the bug is in MeshBuffer.

I’m also curious about the fact that it works the first time but not on subsequent times - I can’t see any obvious bugs in the MeshBuffer, it looks sane.

I know very little about the Mono Runtime and how it’s running in Unity, but I know it does not release open file handles on any imported native libraries, which suggests to me that the Mono runtime is still executing (excuse my ignorance here) in some capacity… Is there ANY chance that the threads that you start are not being stopped/cleaned up? are they still executing and causing havoc? This is a long shot and perhaps not even technically possible, I’m just thinking of rational reasons why a subsequent execution would be different from the first.

61

it gives “not null” on the first run and “NULL” on the following runs

I have a debugging code at the end of a thread termination, so yes I can confirm that all of the threads are terminated.

I’m very suspicious that your worker threads are not being terminated. Do you do a thread.join() on each of them to ensure they’re complete?

Edit, you beat me to it :slight_smile:

Hmm, I’m at a loss, I don’t see an issue with this code.

Are there any race conditions with regards to the thread startup, worldAccess member reference assignment, etc.

And you 100% sure that “Debug.Log(“BufferError: original object not set”);” is never executed?
Edit: Hmm, scrap that - it’d explode when you reference the list if that was the case…

This seems crazy!