mdjarv / assettocorsasharedmemory

Assetto Corsa Shared Memory library written in C#
MIT License
114 stars 34 forks source link

Race Condition when reading and writing memory? #7

Open olispeedy opened 7 years ago

olispeedy commented 7 years ago

How you prevent a possible race condition between the processes that share memory? It will be very likely that Assetto Corsa will overwrite the values in memory at a high rate that you are currently reading. When some values cannot be written with an atomar operation you end up having values where some bytes contain old data while others contain new. Or does this memory mapped variant guarantee that you can read all data consistently?

mdjarv commented 7 years ago

This library does not write to memory, it only reads from it.

Telemetry data is read from the games shared memory API according to Kunos documentation at a set interval and stored in simple .NET objects for ease of use.

Have you experienced any errors from half-written values in a way that can be reliably reproduced using this library, or is what you describe a purely theoretical speculation?

olispeedy commented 7 years ago

It doesn't matter if you only read that data and how often. Most people use mutexes or other locking primitives to avoid race conditions when reading data from shared memory. And yes I experienced that myself in other environments like transferring video data, where dirty read results are more obvious. There exist also more advanced approaches with lock-free circular buffers for instance. By the way I asked the same question in the meantime in the AC forum.

mdjarv commented 7 years ago

I'm not trying to be complicated, but let me ask the question once again since you missed a vital part of it:

Have you experienced any errors from half-written values in a way that can be reliably reproduced using this library

The reason I repeat myself is because during the time when I was actively working on this library I never experienced any problems like this, and because I am not able to reproduce such a (still hypothetical) result there would be no way for me to test if a code change would actually fix it or just add more bloat to the already not-so-elegant code.

I'm sure you are more well versed than me when it comes to memory mapped files and shared memory and the code is fully open so feel free to submit a pull-request if you solve the issue you describe.

olispeedy commented 7 years ago

No I haven't used your library yet, that's why I was asking if you considered a possible race condition. I don't think you can fix it on your side alone, because AC would have to offer something as long they provide the shared data themselves. You don't experience obvious problems because most data from AC fits into 4 bytes and such small types seem to be written at once ( in an atomar operation ), at least on x86 based systems. But it's very likely that the values you read at the end of the memory are sometimes not from the same batch/update like the values from the beginning. I am not sure how consistent the data from AC are at all ( if it would make a difference that all data comes from one batch / update cycle ). Once more complex types with dynamic size come into play it's more likely that you will experience problems. Think of strings and new drivers entering a server for instance.

If I am going to use your library I will send pull requests, don't worry. I will start with an rF2 plugin for now. ;-)

mdjarv commented 7 years ago

To answer your main question: There are no safety measures taken to prevent race conditions in this code.

Consider the frequency/improbability of such errors and, more importantly, how it would impact your end-user implementation.

The same applies to what you describe as batch-updates, does it really matter if the RPM value is taken a few microseconds later than the current speed?

If it causes an actual problem you can always handle it in the front-end implementation by sanity-checking data and apply some smoothing or statistical analysis like n-th percentile.