Search code examples
c#marshallingunmanaged

Array of structure passed from C# to C code


I have passed a struct array from C# (managed code) to C (unmanaged code). The memory of struct is allocated in C# end. The array is populated in C code. My code is multithreaded. The filling of array is done by one thread and another thread reads the already populated items from the struct array. But I am unable to read the data from second thread until the first thread exits however the memory is shared by both.

Sample code

C# struct

public struct Data
{
    public IntPtr str;

    [MarshalAs(UnmanagedType.I4)]
    public int id;
}

Allocating memory of struct array in C# side

GCHandle[] handles = new GCHandle[10];

for (int i = 0; i < 10; i++)
{
     _data[i] = new Data();
     byte[] bd = new byte[100];
     handles[i] = GCHandle.Alloc(bd, GCHandleType.Pinned);
     data[i].str = handles[i].AddrOfPinnedObject();
}

First thread passes this struct array _data to unmanaged code (C code) to fill using func

void func([In,Out] Data[] _data);

Second thread starts reading the filled struct data but still the first thread is filling the data for rest index. In this case the data is available for _data[0].str but _data[0].id is showing 0 but at C end it shows the correct filled value i.e. 5. Please help why the data for struct member int id is coming 0. The correct value only comes after the first thread ends. But as the memory is shared it must be available as soon as it is filled by unmanaged code.

Any help will be highly appreciated.

I want the the even if thread 1 doesn't ends thread 2 must be able to fetch the values of indexes which are already filled at the unmanaged code.


Solution

  • Because you pass an managed array to unmanaged code, clr will copy the array into another memory block first, then pass that to the unmanaged function. After the unmanaged function finished, clr will marshal the memory back into the managed array, then you got the result.

    A solution is use the array address directly with unsafe code.

    extern void func(Data* data);
    
    fixed(Data* p = _data)
    {
        func(p);
    }
    

    If you don't like unsafe code, since the memory of a structure array is sequential, you can pass the reference of the 1st element. But I am not sure is this safe enough.

    extern void func(ref Data data);
    
    func(ref _data[0]);