Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Interop causes Unity to crash

is it possible at all to make C/C++ function callback into Unity scripts, provided that you can create a new thread from the scripts? I tried but Unity crashes as soon as the scripts get executed.

I googled about it and found this thread which says

Unity is not callback nor threadsafe, any access to classes that extend UnityEngine.Object is only allowed from within the same thread as unity scripts are running in, not asyncronous from other threads nor from asyncrnous callbacks from COM / OS async operations

If thats the case for you there are two possible ways:

(1) Write a wrapper that gets these callbacks and queues the stuff and then expose a function that allows unity to request the next event / dataobject or whatever in a blocking, unthreaded form (2) Make the callbacks call into a static function on something extending from System.Object and write the same kind of logic as above to request the information on classes extending UnityEngine.Object

But I think if I create a thread and callback into that thread, it will be okay right? I am thinking like this because I've read threads like this one that introduces how to make C functions calling back C# functions. So I reasoned that if I create a new thread, it's no longer Unity, it will just be mono and C#.

Here is my code that crashes Unity:

The C++ code:

#include <iostream>
// #include "stdafx.h"

typedef int (__stdcall * Callback)(const char* text);

Callback Handler = 0;

extern "C" __declspec(dllexport)
void __stdcall SetCallback(Callback handler) {
    Handler = handler;
}

extern "C" __declspec(dllexport)
void __stdcall TestCallback() {
    int retval = Handler("hello world");
}

The C# code:

using UnityEngine;
using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Threading;


class UnManagedInterop : MonoBehaviour {
  private delegate int Callback(string text);
  private Callback mInstance;   // Ensure it doesn't get garbage collected


  public void Test() {
        mInstance = new Callback(Handler);
        SetCallback(mInstance);
        TestCallback();
  }

  private int Handler(string text) {
    // Do something...
    print(text);
    return 42;
  }

  [DllImport("test0")]
  private static extern void SetCallback(Callback fn);
  [DllImport("test0")]
  private static extern void TestCallback();

    void Start()
    {
        Thread oThread = new Thread(new ThreadStart(Test));

        // Start the thread
        oThread.Start();


    }
}
like image 456
Zening Qu Avatar asked May 09 '12 21:05

Zening Qu


1 Answers

The answer is Yes!

I tested again on August 8, 2012, with Unity 3.5.2f2, Pro license. Thanks for @hatboyzero's comment I found this example.

Although the code in my question doesn't work, the following code works:

// C#
using System.Runtime.InteropServices;

class Demo {
    delegate int MyCallback1 (int a, int b);

    [DllImport ("MyRuntime")]
    extern static void RegisterCallback (MyCallback1 callback1);

    static int Add (int a, int b) { return a + b; }
    static int Sub (int a, int b) { return a - b; }

    void Init ()
    {
        // This one registers the method "Add" to be invoked back by C code
        RegisterCallback (Add);
    }
}


// C
typedef int (*callback_t) (int a, int b);
static callback_t my_callback;

void RegisterCallback (callback_t cb)
{
    my_callback = cb;
}

int InvokeManagedCode (int a, int b)
{
    if (my_callback == NULL){
         printf ("Managed code has not initialized this library yet");
         abort ();
    }
    return (*my_callback) (a, b);
}

I didn't have to embed MonoRuntime as the tutorial suggests. Just the above two pieces of code solved my problem.

like image 101
Zening Qu Avatar answered Oct 25 '22 07:10

Zening Qu