Lock-free zero-copy triple buffer
The concurrency looks bug-free to me, which is rare!

Some nits on C++ idioms:

It is extremely unusual to take a function pointer as a template parameter. This drastically limits the reusability of your class template. A more traditional, "STL-ish" interface would be

template&lt;class Buffer, class Produce, class Consume&gt;
class triple_buffer {
    explicit triple_buffer(Buffer bufs[3]);
    explicit triple_buffer(Buffer bufs[3], Produce produce, Consume consume);
    // ...
};


Or, since produce and consume are used only inside the constructor — it immediately wraps them up in lambdas and passes them off to std::thread — you don't even need them to be template parameters of the whole class. They can be template parameters of the constructor itself. Something like this:

template&lt;class Buffer&gt;
class triple_buffer {
    template&lt;class P, class C&gt;
    explicit triple_buffer(Buffer bufs[3], P produce, C consume) {
        // ...
        produce(present_.load());
        producer_thread_ = std::thread([&amp;, produce]() {
            while (!stop_.load()) {
                produce(inprogress_.load());
                inprogress_ = ready_.exchange(inprogress_);
                stale_.store(false);
            }
        });
        // ...
    }
};


(In C++14 you can capture produce as [&amp;, produce = std::move(produce)] to avoid making a copy of it.)

Notice the CamelCase template parameter names.

I also recommend sigilling the names of your member variables — ready_ rather than ready, and so on. (Some coding style guides prefer m_ready or d_ready. No big deal.)

These two naming conventions, together, will help to alleviate some of your confusingly similar naming. Right now you've got a template parameter named produce and a member function named producer (and your test harness has a global function named produce). Of course, I just "fixed" that naming issue by getting rid of the producer member function altogether!



You use both atomic&lt;bool&gt; stop and atomic_flag stale. There's no significant difference between atomic&lt;bool&gt; and atomic_flag; you should pick one and stick to it. I recommend atomic&lt;bool&gt; because it's one fewer thing for your coworkers to look up on cppreference.



inprogress = ready.exchange(inprogress);


This line is correct, but I really strongly encourage you to rewrite it as two lines anyway. "One side-effect per source line" is a good rule of thumb for concurrent code. And there really are two side-effects happening here.

Buffer *p = ready.exchange(inprogress);
inprogress.store(p);


Splitting it up this way makes it clear what's going on — in particular that the swap is not being effected atomically — and incidentally makes the code easier to stress-test (by adding instrumentation between each pair of lines) and debug (because now it's possible to pause the debugger between the two side-effects).



while (stale.test_and_set())


If you switch to consistently using atomic&lt;bool&gt;, this line would become

while (stale.exchange(true))




consumer_thread = std::move(std::thread([this] { consumer(); }));


This line is overengineered. You don't need to std::move a prvalue. And personally, I'd capture [&amp;] unless I had a special reason not to. So:

consumer_thread = std::thread([&amp;]{ consumer(); });




Stylistically, I wonder why triple_buffer takes a pointer to an array of three Buffer objects, as opposed to either


taking three pointers to Buffer objects (which then needn't be adjacent in memory), or
giving triple_buffer three member variables of type Buffer.