EDIT: The actual answer to the question is much more simple. Supposing that p
is the int*
to the variable to be decremented. At least the C# compiler allows ref
on the dereferenced p directly:
Threading.Interlocked.Decrement(ref (*p));
And when coming from
int local = 10;
IntPtr p = (IntPtr)(&local);
...
// decrement local via p
Threading.Interlocked.Decrement(ref (*(int*)p));
Note that it is important to use the interlocked class (Interlocked.Decrement
) since the JIT produces efficient code for it. (But not as efficient as using ref local
directly, though...)
This part of the answer is now outdated. However, I leave it here since the technique of wrapping a local into a struct
might be helpful anyways...
This answer is applicable only if you have the option to modify the code and the semantic of the 'pointer'. Exactly spoken, it does not answer the original question but should be helpful nevertheless.
Besides the option to P/Invoke a native function (and its disadvantages: P/Invoke overhead, unmanaged function maintenance overhead) there is another option. It enables the direct use of the Interlocked
class. The JIT compiler will generate more efficient code for it and we save the P/Invoke calls.
The solution is inspired by this thread and actually a shameless copy of the answer presented by Mike Danes:
https://social.msdn.microsoft.com/Forums/en-US/b9f9e359-3ca9-43c1-9870-fe562794fca8/interlockedcompareexchange-of-unmanaged-memory-from-vb?forum=clr&prof=required
The following code demonstrates how to decrement a local, stack allocated int
counter atomically from multiple threads. It gets away without any additional objects on the managed heap and without any more advanced synchronization objects. Please make sure to secure this approach against the common risks before applying to production code.
internal struct DownCounter {
internal int value;
public DownCounter(int initialValue) {
value = initialValue;
}
public int Decrement() {
return Interlocked.Decrement(ref value);
}
}
unsafe class Program {
static void Main(string[] args) {
// the local stack allocated counter, to be shared by all threads
DownCounter counter = new DownCounter(10);
// some work for the threads
Action<object> work = (c) => {
double a = 1;
for (int i = 0; i < 1 << 26; i++) {
// do stuff here
a = a % i * a % (i + 1);
}
// decrement the main thread's stack counter
int d = (*((DownCounter*)(IntPtr)c)).Decrement();
Console.WriteLine($"Decremented: {d}");
};
// start 10 threads, each decrements the local, stack allocated(!) counter
for (int i = 0; i < 10; i++) {
Task.Factory.StartNew(work, (IntPtr)(&counter));
}
// poor mans 'spin wait' on the local counter
while (counter.value > 0) {
Thread.Sleep(100);
Console.WriteLine($"# threads running: {counter.value}");
}
Console.Read();
}
}
Instead of passing around the int*
pointer itself, we now encapsulate the value to be decremented into a small struct and pass the pointer to the struct. In order to decrement the value inside the struct atomically we dereference the pointer to call a helper function Decrement()
of the struct. This simply calls Interlocked.Decrement
on the wrapped value.