title: Could this publish / check-for-update class for a single writer + reader use memory_order_relaxed or acquire/release for efficiency?
body: <h1>Introduction</h1>
  
  <p>I have a small class which make use of std::atomic for a lock free operation. Since this class is being called massively, it's affecting the performance and I'm having trouble.</p>
  
  <h1>Class description</h1>
  
  <p>The class similar to a LIFO, but once the pop() function is called, it only return the last written element of its ring-buffer (only if there are new elements since last pop()). </p>
  
  <p>A single thread is calling push(), and another single thread is calling pop(). </p>
  
  <h1>Source I've read</h1>
  
  <p>Since this is using too much time of my computer time, I decided to study a bit further the std::atomic class and its memory_order. I've read a lot of memory_order post avaliable in StackOverflow and other sources and books, but I'm not able to get a clear idea about the different modes. Specially, I'm struggling between acquire and release modes: I fail too see why they are different to memory_order_seq_cst.</p>
  
  <h1>What I <strong>think</strong> each memory order do using my words, from my own research</h1>
  
  <p><strong>memory_order_relaxed:</strong> In the same thread, the atomic operations are instant, but other threads may fail to see the lastest values instantly, they will need some time until they are updated. The code can be re-ordered freely by the compiler or OS.</p>
  
  <p><strong>memory_order_acquire / release:</strong> Used by atomic::load. It prevents the lines of code there are before this from being reordered (the compiler/OS may reorder after this line all it want), and reads the lastest value that was stored on this atomic using <strong>memory_order_release</strong> or <strong>memory_order_seq_cst</strong> in this thread or another thread. <strong>memory_order_release</strong> also prevents that code after it may be reordered. So, in an acquire/release, all the code between both can be shuffled by the OS. I'm not sure if that's between same thread, or different threads. </p>
  
  <p><strong>memory_order_seq_cst:</strong> Easiest to use because it's like the natural writting we are used with variables, instantly refreshing the values of other threads load functions.</p>
  
  <h1>The LockFreeEx class</h1>
  
  <pre class="lang-cpp prettyprint-override"><code>template&lt;typename T&gt;
  class LockFreeEx
  {
  public:
      void push(const T&amp; element)
      {
          const int wPos = m_position.load(std::memory_order_seq_cst);
          const int nextPos = getNextPos(wPos);
          m_buffer[nextPos] = element;
          m_position.store(nextPos, std::memory_order_seq_cst);
      }
  
      const bool pop(T&amp; returnedElement)
      {
  
          const int wPos = m_position.exchange(-1, std::memory_order_seq_cst);
          if (wPos != -1)
          {
              returnedElement = m_buffer[wPos]; 
              return true;
          }
          else
          {
              return false;
          }
      }
  
  private:
      static constexpr int maxElements = 8;
      static constexpr int getNextPos(int pos) noexcept {return (++pos == maxElements)? 0 : pos;}
      std::array&lt;T, maxElements&gt; m_buffer;
      std::atomic&lt;int&gt; m_position {-1};
  };
  </code></pre>
  
  <h1>How I expect it could be improved</h1>
  
  <p>So, my first idea was using memory_order_relaxed in all atomic operations, since the pop() thread is in a loop looking for avaliable updates in pop function each 10-15 ms, then it's allowed to fail in the firsts pop() functions to realize later that there is a new update. It's only a bunch of milliseconds.</p>
  
  <p>Another option would be using release/acquire - but I'm not sure about them. Using release in <strong>all</strong> store() and acquire in <strong>all</strong> load() functions.</p>
  
  <p>Unfortunately, <em>all the memory_order I described seems to work</em>, and I'm not sure when will they fail, if they are supposed to fail.</p>
  
  <h1>Final</h1>
  
  <p>Please, could you tell me if you see some problem using relaxed memory order here? Or should I use release/acquire (maybe a further explanation on these could help me)? why?</p>
  
  <p>I think that <em>relaxed</em> is the best for this class, in all its store() or load(). But I'm not sure!</p>
  
  <p>Thanks for reading.</p>
  
  <h1>EDIT: EXTRA EXPLANATION:</h1>
  
  <p>Since I see everyone is asking for the 'char', I've changed it to int, problem solved! But it doesn't it the one I want to solve. </p>
  
  <p>The class, as I stated before, is something likely to a LIFO but where only matters the last element pushed, if there is any.</p>
  
  <p>I have a big struct T (copiable and asignable), that I must share between two threads in a lock-free way. So, the only way I know to do it is using a circular buffer that writes the last known value for T, and a atomic which know the index of the last value written. When there isn't any, the index would be -1.</p>
  
  <p>Notice that my push thread must know when there is a "new T" avaliable, that's why pop() returns a bool.</p>
  
  <p>Thanks again to everyone trying to assist me with memory orders! :)</p>
  
  <h1>AFTER READING SOLUTIONS:</h1>
  
  <pre><code>template&lt;typename T&gt;
  class LockFreeEx
  {
  public:
      LockFreeEx() {}
      LockFreeEx(const T&amp; initValue): m_data(initValue) {}
  
      // WRITE THREAD - CAN BE SLOW, WILL BE CALLED EACH 500-800ms
      void publish(const T&amp; element)
      {
          // I used acquire instead relaxed to makesure wPos is always the lastest w_writePos value, and nextPos calculates the right one
          const int wPos = m_writePos.load(std::memory_order_acquire);
          const int nextPos = (wPos + 1) % bufferMaxSize;
          m_buffer[nextPos] = element;
          m_writePos.store(nextPos, std::memory_order_release);
      }
  
  
      // READ THREAD - NEED TO BE VERY FAST - CALLED ONCE AT THE BEGGINING OF THE LOOP each 2ms
      inline void update() 
      {
          // should I change to relaxed? It doesn't matter I don't get the new value or the old one, since I will call this function again very soon, and again, and again...
          const int writeIndex = m_writePos.load(std::memory_order_acquire); 
          // Updating only in case there is something new... T may be a heavy struct
          if (m_readPos != writeIndex)
          {
              m_readPos = writeIndex;
              m_data = m_buffer[m_readPos];
          }
      }
      // NEED TO BE LIGHTNING FAST, CALLED MULTIPLE TIMES IN THE READ THREAD
      inline const T&amp; get() const noexcept {return m_data;}
  
  private:
      // Buffer
      static constexpr int bufferMaxSize = 4;
      std::array&lt;T, bufferMaxSize&gt; m_buffer;
  
      std::atomic&lt;int&gt; m_writePos {0};
      int m_readPos = 0;
  
      // Data
      T m_data;
  };
  </code></pre>