How to make C (P/invoke) code called from C# "Thread-safe"
c, c#, multithreading, pinvoke
Solution
A common pattern is to have
- a function that allocates memory for the state,
- a function that has no side-effects but mutating the passed-in state, and
- a function that releases the memoy for the state.
The C# side would look like this:
Usage:
var state = new ThreadLocal<SomeSafeHandle>(NativeMethods.CreateSomeState);
Parallel.For(0, 100, i =>
{
var result = NativeMethods.SomeFunction(state.Value, i, 42);
Console.WriteLine(result);
});
Declarations:
internal static class NativeMethods
{
[DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern SomeSafeHandle CreateSomeState();
[DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int SomeFunction(SomeSafeHandle handle,
int parameter1,
int parameter2);
[DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int FreeSomeState(IntPtr handle);
}
SafeHandle magic:
[SecurityPermission(SecurityAction.InheritanceDemand, UnmanagedCode = true)]
[SecurityPermission(SecurityAction.Demand, UnmanagedCode = true)]
internal class SomeSafeHandle : SafeHandle
{
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
public SomeSafeHandle()
: base(IntPtr.Zero, true)
{
}
public override bool IsInvalid
{
get { return this.handle == IntPtr.Zero; }
}
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
protected override bool ReleaseHandle()
{
return NativeMethods.FreeSomeState(this.handle) == 0;
}
}
Problem
I have some simple C-code which uses a single global-variable. Obviously this is not thread-safe, so when I call it from multiple threads in C# using P/invoke, things screw up. How can I either import this function separately for each thread, or make it thread-safe? I tried declaring the variable `__declspec(thread)`, but that caused the program to crash. I also tried making a C++/CLI class, but it doesn't allow member-functions to be `__declspec(naked)`, which I need (I'm using inline-assembly). I'm not very experienced writing multi-threaded C++ code, so there might be something I'm missing. Here is some example code: C# ``` [DllImport("MyDll.dll", CallingConvention = CallingConvention.Cdecl)] public static extern int SomeFunction(int parameter1, int parameter2); ``` C++ ``` extern "C" { int someGlobalVariable; int __declspec(naked) _someFunction(int parameter1, int parameter2) { __asm { //someGlobalVariable read/written here } } int __declspec(dllexport) SomeFunction(int parameter1, int parameter2) { return _someFunction(parameter1, parameter2); } } ``` [Edit]: The result of `SomeFunction()` must go in some prescribed order based on `someGlobalVariable` (think of eg. a PRNG, with `someGlobalVariable` as the internal state). So, using a mutex or other sort of lock is not an option - each thread must have its own copy of `someGlobalVariable`.