- Home /
Unity iOS - function pointers
It seems that my C SDKs may be unusable for Unity iOS (without a bit of an overhaul), but I wanted to verify to be sure. The long story short: I can compile and run my Unity sample that uses native code with function pointers for callbacks, but when it comes time to trigger my C# callback I get the following error: ExecutionEngineException: Attempting to JIT compile method '(wrapper native-to-managed) gamespySample.CreateUserAccount:createuserCallback (Gamespy.GHTTPResult,intptr,intptr)' while running with --aot-only.
The same code works on PC (with c code compiled into a DLL) and Android (with c code compiled into a .so), but it seems the iOS is limited to aot compiling which does not allow for this function pointer-to-delegate marshaling. Some web searching led me to this issue being a known limitation of MonoDevelop/iOS, which can be solved by using the 'MonoPInvokeCallback' attribute (within the MonoTouch namespace) for the C# delegate, but it seems this namespace is unavailable within the Unity version of MonoDevelop.
Is it in fact not possible to use function pointers in Unity iOS? If this is the case, I presume I'll need to write a layer of c wrappers that handle the function pointer callbacks and just return the needed data (after getting polled).
For reference, I'll include an example function below: C declaration: WSCreateUserAccountValue wsCreateUserAccount(..., WSCreateUserAccountCallback userCallback);
where WSCreateUserAccountCallback is the function pointer: *typedef void (WSCreateUserAccountCallback)(...);
Then in our C# wrapper we have the following declarations: [UnmanagedFunctionPointerAttribute(CallingConvention.Cdecl)] public delegate void WSCreateUserAccountCallback(GHTTPResult httpResult, IntPtr theResponse, IntPtr userData); [DllImport ("__Internal")] public static extern void wsCreateUserAccount(..., WSCreateUserAccountCallback callback);
And lastly here's our C# invokation: WSCreateUserAccountCallback myCreateUserAccountCallback = new WSCreateUserAccountCallback(createuserCallback); wsCreateUserAccount(..., myCreateUserAccountCallback);
where createuserCallback is defined like: public static void createuserCallback(...) { ... }
I'm running into this issue as well. Would be great to have a work around without using UnitySend$$anonymous$$essage or something similar.
I ran into the exact same issue as well. Currently using a queue to solve the issue. It will be great to have delegates working on iOS
Answer by Dreamora · Dec 23, 2011 at 10:19 AM
Callbacks from stuff running in own threads actually are never an option. Unity is not callback safe so if the GameSpy library calls into a UnityEngine.Object derived class (monobehaviour / scriptable object) unity will simply crash on a multicore machine as the ipad2 / iphone 4s.
You should write a C layer inbetween that does the callback and then uses the iOS Unity exposed UnitySendMessage to inform the inside of unity to stay safe and clean
Your point is well-met, but personally I'd rather use normal thread lock procedures and retain the flexibility of real, well-typed callback arguments than have to condense all the callback arguments into a string as mandated by UnitySend$$anonymous$$essage. If you're only ever returning single values easily encapsulated in a string, however, UnitySend$$anonymous$$essage is most certainly the safer option.
Unity does not respect locks, as such you can decide to either not use a distinct thread in a native code library (means the library itself is in the same thread and you don't use os level asyncs with callbacks) or you have to call into unity through UnitySend$$anonymous$$essage or a 'polling approach' and store the 'callback event' inside the native code layer.
there is no other option or alternative. Either you are fully in sync in the plugin (its driven through calls from unity basically and only operates when told to) when you call back in or you will kill unity flat out.
This is very good to know; I had no idea that Unity didn't respect locks. I figured if I put a locked flag in the Update loop I would be able to pick up the required data for consumption after it was prepped, but if that's not the case I'm definitely going to have to re-evaluate my solution. Thanks again!
Thanks for the feedback Dreamora. We've since upgraded our SD$$anonymous$$s to be full C#, so we no longer need the C plugins. Is there still an issue with using a callback to return data (rather than polling or using UnitySend$$anonymous$$essage), or is the issue you described specific to the native code plugin scenario?
Answer by konistehrad · Dec 23, 2011 at 05:11 AM
According to the patch to Mono that implements the MonoPInvokeCallback attribute, it's by name and not by any specific marker on the attribute! This means you can reimplement the Attribute with the correct method signature and it'll still work. The implementation looks like this:
public class MonoPInvokeCallbackAttribute : System.Attribute
{
private Type type;
public MonoPInvokeCallbackAttribute( Type t ) { type = t; }
}
Simply throw this attribute on a static method and your native code can now access your managed delegates. :)
For this use case (Objective-C/$$anonymous$$ono callbacks), limitations are listed here: http://docs.xamarin.com/ios/about/limitations#section_8.
corroborating evidence: http://stackoverflow.com/questions/7058254/calling-c-sharp-function-from-c
Thanks konistehrad, you saved me here. Works for me on Unity 4.2.