2

I have a pointer (IntPtr) to some location in the process virtual memory, I need to be able to write a 32-bit integer into this location atomically, as there can be several native threads that read/write the integer from/to this location. Currently I'm using Marshal.WriteInt32 to do so, but I'm not sure if this function writes memory atomically.

So far I haven't run into race condition situation during test, but I want to know for sure. Are all Marshal.WriteInt* functions write memory atomically? If not, how can I do it?

user2102508
  • 1,009
  • 11
  • 23
  • [What operations are atomic in C#?](http://stackoverflow.com/questions/11745440/what-operations-are-atomic-in-c) handles the CLI side of things, but as far as a [quick search points out](https://github.com/dotnet/coreclr/issues/916), once you leave the runtime, all bets seem to be off as far as I understand it. – CodeCaster Oct 15 '15 at 14:26
  • if I were you I would try [interlocked.exchange](https://msdn.microsoft.com/en-us/library/801kt583(v=vs.110).aspx) – mg30rg Oct 15 '15 at 14:27

3 Answers3

2

Looking at the implementation of Marshal.WriteInt32 it seems that aligned writes are atomic and non aligned writes are not atomic.

If not, how can I do it?

As usual, any non atomic operation can be made atomic by synchronizing the access. Use some sort of synchronization. Intra process synchronization can be done easily with lock statements. For Inter process synchronization you can use Mutex.(Assuming you're having control over both processes)

Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
2

WriteInt32 will always be atomic, as long as the memory is properly aligned. There is no synchronization primitive you can use while directly writing to somebody else's memory, so it's really your only bet.

However, it only means you will not get torn writes or reads - it doesn't give you any guarantee that anyone else will ever see the value you've written. If you need to share data between processes, just make it coöperatively. Writing directly to another processes memory is tricky at the best of times, and outright dangerous most of the time.

The simplest way to handle synchronized access would be using a mutex - as long as each of the applications follows the proper protocol, you're safe, no matter if the write is atomic or not. But really, just use some other method of communication between the processes - direct memory manipulation has never been supported. Not to mention that it gives your processes way too many privileges for comfort :)

Luaan
  • 62,244
  • 7
  • 97
  • 116
  • Why don't simply use memory fence? – SergeyA Oct 15 '15 at 14:34
  • 1
    @SergeyA I wouldn't ever use "memory fence" in the same sentence as "simply" :D Lockless thread-safe programming is very tricky, and rarely needed. It's only worth it if you have hard evidence it would mean a significant performance gain. And I suspect that if that were the case, the OP would simply use `unsafe` code, rather than using `Marshal`. – Luaan Oct 15 '15 at 14:37
  • don't get me wrong - I know nothig of C# :) But there is nothing conceptually hard with using memory fences (thorugh OS-provided primitives, of course) and there are many important cases which demand you to use lockless structures. – SergeyA Oct 15 '15 at 14:40
  • @Luaan: I didn't get your point you have made on Alex's answer and want to understand it. Can you kindly elaborate on that? Edit: The answer is deleted now but you said something about thread-safe method having unprotected memory writes. – displayName Oct 15 '15 at 14:40
  • 1
    @displayName The documentation says that static members are thread-safe. That doesn't mean that it magically solves all concurrency issues for you - it just means that you don't have to e.g. make sure that all `Marshal` calls are made from the same thread. It's just like with e.g. `File.WriteAllText` - the method itself is thread-safe, but that doesn't mean that two threads (or applications) trying to write to the same file at the same time will work. Whenever you're working with shared resources, you must ensure *all* the code using that shared resource is synchronized in some way. – Luaan Oct 15 '15 at 14:45
1

I've tryed this: unsafe { Interlocked.Exchange(ref *((int*)ptr), 42); } where ptr is my IntPtr pointer to the memory location and it seemed to work fine. What do you think about this solution? Or maybe I should simply use *((int*)ptr) = 42?

user2102508
  • 1,009
  • 11
  • 23