title: Volatile member variables vs. volatile object?
body: <p>I'm trying to implement a multiple producer (via interrupt), single consumer (via application thread) queue on an embedded target in "MpscQueue.h" below.</p>
  
  <p>I'm wondering if I can safely remove the some of the <code>volatile</code> usage below (see inline questions).  I would also consider using <code>volatile std::array</code> in place of the C-style <code>buffer_[]</code> shown below, but I was unsure if I could trust its implementation to match the intent of the below.  A third alternative would be to mark the MpscQueue object itself as <code>volatile</code> and qualify the relevant methods <code>volatile</code>, but it's not clear if that would result in all member variables (and what they point to, in the case of pointers) being treated as volatile.</p>
  
  <p>Any guidance on this?</p>
  
  <pre><code>template&lt;typename T, uint32_t depth&gt;
  class MpscQueue
  {
  public:
      MpscQueue(void);
      bool push(T&amp; t);
      bool pop(T* const t);
  
  private:
      T volatile buffer_[depth];  // Q1: is volatile unnecessary if never access buffer_[]?
      T volatile* const begin_;   // Q2: is volatile unnecessary if never access value
      T volatile* const end_;     //     via begin_/end_?
      T volatile* head_;          // volatile required so that thread always checks value
      T volatile* volatile tail_; // Q3: is 'T volatile' required so that ISR accounts
                                  //     for other ISRs when setting value?
                                  // Q4: is '* volatile' required so that ISR accounts
                                  //     for other ISRs when checking pointer?
  };
  
  template&lt;typename T, uint32_t depth&gt;
  MpscQueue&lt;T, depth&gt;::MpscQueue(void) :
      begin_(&amp;buffer_[0]),
      end_(&amp;buffer_[depth - 1]),
      head_(begin_),
      tail_(begin_)
  {}
  
  template&lt;typename T, uint32_t depth&gt;
  bool MpscQueue&lt;T, depth&gt;::push(T&amp; t)
  {
      // "Multiple producer" ISRs can use this function to push at tail
  
      // Pseudo-code: if not full, *(tail_++) = t
  }
  
  template&lt;typename T, uint32_t depth&gt;
  bool MpscQueue&lt;T, depth&gt;::pop(T* const t)
  {
      // "Single consumer" thread can use this function to pop at head
  
      // Pseudo-code: if not empty, *t = *(head_++)
  }
  </code></pre>
  
  <hr>
  
  <p>Edit: To focus the question in the right direction, let me clarify that I have taken care of thread-safety, and it is not part of the question here.</p>
  
  <p>Because this queue is single consumer, there is no thread safety required on the read/pop side.  On the write/push side, thread safety among interrupts will be handled by setting all relevant interrupts to the same priority level (otherwise, a lock will be used).</p>