0

Let's suppose I have a big memory buffer used as a framebuffer, what is constantly written by a thread (or even multiple threads, guaranteed that no two threads write the same byte concurrently). These writes are indeterministic in time, scattered through the codebase, and cannot be blocked.

I have another single thread which periodically reads out (copies) the whole buffer for generating a display frame. This read should not be blocked, too. Tearing is not a problem in my case. In other words, my only goal is that every change done by the write thread(s) should eventually appear in the reading thread. The ordering or some (negligible compared to a display refresh rate) delay does not matter.

Reading and writing the same memory location concurrently is a data race, which results in an undefined behavior in c++11, and this article lists same really dreadful examples where the optimizing compiler generates code for a memory read that alters the memory contents in the presence of data race.

Still, I need some solution without completely redesigning this legacy code. Every advice counts what is safe from practical standpoints, independent of if it is theoretically correct or not. I am also open to not-fully-portable solutions, too.

Aside from that I have a data race, I can easily force the visibility of the buffer changes in the reading thread by establishing a synchronizes-with relation between the threads (acquire-release an atomic guard variable, used for nothing else), or by adding platform-specific memory fence calls to key points in the writer thread(s).

My ideas to target the data race:

  1. Use assembly for the reading thread. I would try to avoid that.

  2. Make the memory buffer volatile, thus preventing the compiler to optimize such nasty things what are described in the referenced article.

  3. Put the reading thread's code in a separate compile unit, and compile with -O0

+1. Leave everything as is, and cross my fingers (as currently I do not notice issues) :)

What is the safest from the list above? Do you see a better solution?

FYI, the target platform is ARM (with multiple cores) and x86 (for testing).

(This question is concretizing a previous one what was a little too generic.)

Community
  • 1
  • 1
Ferenc
  • 779
  • 1
  • 6
  • 14
  • I think the first thing to do is come up with a test for whether a given implementation is acceptable or not. Once you have that, you can experiment with various implementations and see how they work, but without it, how would you know if any particular change you made was an improvement or a setback? – Jeremy Friesner Apr 06 '17 at 22:41
  • Maybe the easiest test is to check the compiled assembly code. I cannot do that for the writing side (too many places), but is feasible for the single reading location. I am hoping that maybe somebody comes up with a standards-compliant solution I miss. Otherwise I need to check the assembly every time I make a release, especially if I update the compiler or the code. Or I should simply let it go and stick with writing it in assembly... – Ferenc Apr 07 '17 at 05:54

0 Answers0