Skip to content

0.15 c# - async callbacks calling convention mismatch#171

Description

@enbyted

This one took a while to debug 馃槃
Note: All below is tested when compiling for 32-bit Windows, i.e. in rust: i686-pc-windows-msvc in C# x86.
This issue doesn't exist in x64 Windows as stdcall and cdecl calling conventions are equivalent there.
It also shouldn't exist on Unix platforms as those default to cdecl in C# (I believe that to be true - didn't check).

The ffi_service makes async functions by taking in an extra AsyncCallback. That callback has this definition: extern "C" fn(&T, *const c_void) -> ()>.

In C# we use these as follows:

  • The function pointer is set to Marshal.GetFunctionPointerForDelegate(_delegate)
  • The void pointer is used for a sequential ID

All good, however the _delegate has type: delegate void AsyncCallbackCommon(IntPtr data, IntPtr callback_data)
Which by default uses stdcall calling conventions, whereas rust expects C calling convention (which currently is compatible with cdecl).

There are 2 possible solutions to this:

  • define the callback in rust to use extern "stdcall"
  • Add the following attribute to the delegate type: [UnmanagedFunctionPointer(CallingConvention.Cdecl)]

Both should work in x86 and x64. I believe it would be most portable to set both sides to cdecl, i.e. in rust use extern "cdecl" and the attribute in C#. This would be most compatible with C as well.
Using cdecl should be most portable

Unfortunately I do not know how this translates to Python - I never worked with FFI in Python.

EDIT

Apparently you've already made the decision for callbacks generated with callback! macro - these seem to add the attribute to use Cdecl in c#.
Thus I would consider this a bug that the attribute is not generated for the async callback.

// Debug - write_type_definition_named_callback 
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void LogListenerNative(Utf8String.Unmanaged message, IntPtr callback_data); // 'True' native callback signature
public delegate void LogListenerDelegate(Utf8String message); // Our C# signature

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions