Write to location access violation, variable shared between threads

1

I'm building an Unity application that makes some use of Selenium webdriver, I start the headless browser(chromedriver) in another Thread. In that thread I keep taking screenshots of what is shown in the headless browser, and store it in an byte array. In the main thread I have to use that byte array to make an image that Unity understands(Texture2D). I start the application, it works for a little while, but after some time has passed the application crashes. The crash file says:

UnityPlayer.dll caused an Access Violation (0xc0000005) in module UnityPlayer.dll at 0023:105311be.

Error occurred at 2020-07-30_150418. C:\Codes\Apps\MyApp\MyApp.exe, run by Fabio.

94% memory in use.

0 MB physical memory [423 MB free].

0 MB paging file [1990 MB free].

0 MB user address space [108 MB free].

Write to location 007e7200 caused an access violation.

second thread and the method "LoadImage"(as Coroutine) are started as soon as the application starts()

code that take the screenshot:

public async Task ScreenShotBrowser()
    {
        //await Task.Yield();
        while (isBrowserOpen)
        {
                Screenshot screenshot = ssdriver.GetScreenshot();  //Selenium stuff
            lock (imgArray)
            {
                imgArray = screenshot.AsByteArray;               
            }
            Thread.Sleep(100);
        }
        
    }

loads the image to into the unity material:

IEnumerator LoadImage()
    {
        while (!isBrowserOpen) { yield return new WaitForSecondsRealtime(0.1f); } // ugly code, but avoid freezing

        while (isBrowserOpen)
        {
            
            lock (imgArray)
            {
                mat.mainTexture = loadTexture(imgArray);
            }
            yield return new WaitForSecondsRealtime(0.1f); 
        }
    }

    private Texture2D loadTexture(byte[] imageArray)
    {
            Texture2D tex = new Texture2D(1, 1);
        lock (imageArray)
        {
            tex.LoadImage(imageArray);
        }
        return tex;
    }

thanks in advance

c#
selenium
unity3d
selenium-chromedriver
asked on Stack Overflow Jul 30, 2020 by Fabio Vieira

1 Answer

1

not sure if this is the issue but I think what happens here is that you indeed run out of memory.

private Texture2D loadTexture(byte[] imageArray)
{
    Texture2D tex = new Texture2D(1, 1);
    lock (imageArray)
    {
        tex.LoadImage(imageArray);
    }
    return tex;
}

This creates and loads a new Texture2D all the time quite often in your app. But even if the texture is not referenced anymore it is not automatically GC collected! You have to actively destroy it.

So I would either do something like

IEnumerator LoadImage()
{
    // You could also use this
    yield return new WaitWhile(()=>!isBrowserOpen);

    while (isBrowserOpen)
    {
        // destroy the current texture first
        if(mat.mainTexture) Destroy(mat.mainTexture);

        lock (imgArray)
        {
            mat.mainTexture = loadTexture(imgArray);
        }

        yield return new WaitForSecondsRealtime(0.1f); 
    }
}

or alternatively not even create a new texture everytime but rather simply do

private Texture2D loadTexture(byte[] imageArray, Texture2D tex)
{
    lock (imageArray)
    { 
        // Load into and overwrite the existing texture
        tex.LoadImage(imageArray);
    }
}

IEnumerator LoadImage()
{
    // You could also use this
    yield return new WaitWhile(()=>!isBrowserOpen);

    // Create the texture once if it doesn't exist yet
    if(!mat.mainTexture) mat.mainTexture = new Texture2D(1f, 1f);

    while (isBrowserOpen)
    {
        lock (imgArray)
        {
            loadTexture(imgArray, mat.mainTexture);
        }

        yield return new WaitForSecondsRealtime(0.1f); 
    }
}

In addition to that like mentioned in the comments it might be an issue to lock the imgArray but then exchange that reference later in imgArray = screenshot.AsByteArray;. Here I'm not sure but just to be save you should follow the recommendations of using a dedicated object just for the lock like

///<summary>
/// This is the Lock object for <see cref="imgArray" />
/// </summary>
private readonly object imgArrayLock = new object();

private Texture2D loadTexture(byte[] imageArray, Texture2D tex)
{
    lock (imgArrayLock)
    { 
        // Load into and overwrite the existing texture
        tex.LoadImage(imageArray);
    }
}

IEnumerator LoadImage()
{
    // You could also use this
    yield return new WaitWhile(()=>!isBrowserOpen);

    // Create the texture once if it doesn't exist yet
    if(!mat.mainTexture) mat.mainTexture = new Texture2D(1f, 1f);

    while (isBrowserOpen)
    {
        lock (imgArrayLock)
        {
            loadTexture(imgArray, mat.mainTexture);
        }

        yield return new WaitForSecondsRealtime(0.1f); 
    }
}
answered on Stack Overflow Jul 31, 2020 by derHugo • edited Jul 31, 2020 by derHugo

User contributions licensed under CC BY-SA 3.0