A toy example of the concurrent stack via atomic variables and CAS pattern I've written a toy example of the concurrent stack which has only three functions push(), peek(), and length(). I've used atomic variables for synchronization. Is there anything incorrect in terms of synchronization? Could you please review this code?
#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;atomic&gt;
#include &lt;vector&gt;

template &lt;typename T&gt;
struct Node {
    Node&lt;T&gt;* next{};
    T val{};
    Node(const T&amp; v) : val(v) {};
};

template &lt;typename T&gt;
class Stack {
    std::atomic&lt;Node&lt;T&gt;*&gt; top{};
    std::atomic&lt;size_t&gt; size{};
    public:
    void push(const T&amp; data) {
        Node&lt;T&gt;* node = new Node&lt;T&gt;(data);
        size_t length = size.load(std::memory_order_relaxed);
        node-&gt;next = top.load(std::memory_order_relaxed);
        while(!std::atomic_compare_exchange_weak_explicit(&amp;top, &amp;node-&gt;next, node, std::memory_order_release, std::memory_order_relaxed));
        while(!std::atomic_compare_exchange_weak_explicit(&amp;size, &amp;length, size + 1, std::memory_order_release, std::memory_order_relaxed));
    }
    Node&lt;T&gt;* peek() {
        return top.load();
    }
    size_t length() const {
        return size;
    }
};

template &lt;typename T&gt;
class Runner {
    int cnt{};
    Stack&lt;T&gt;* ss{};
    public:
        Runner(const int n, Stack&lt;T&gt;* s) : cnt(n), ss(s) {}
        void operator()() {
            for(int i = 0; i &lt; cnt; ++i) {
                size_t size = ss-&gt;length();
                if(size &gt;= cnt) {
                    return;
                }
                ss-&gt;push(i);
            }
        }
        Stack&lt;T&gt;* get_stack() const {
            return ss;
        }
};

int main() {
    std::vector&lt;std::thread&gt; threads{};
    Stack&lt;int&gt;* s = new Stack&lt;int&gt;();
    int n = 10;
    Runner&lt;int&gt; r(n, s);
    for(int i = 0; i &lt; 3; ++i) {
        threads.push_back(std::move(std::thread(r)));
    }
    for(int i = 0; i &lt; 3; ++i) {
        threads[i].join();
    }
    Stack&lt;int&gt;* top = r.get_stack();
    Node&lt;int&gt;* head = top-&gt;peek();
    while(head) {
        std::cout &lt;&lt; head-&gt;val &lt;&lt; &quot;, &quot;;
        head = head-&gt;next;
    }
    size_t size = top-&gt;length();
    return 0;
}
Naming
My immediate reaction is that without a pop (or something equivalent) it's not really a stack. Especially given the item below about exposing the implementation details, from a client's viewpoint it's really a linked list that supports only a couple of operations: adding nodes to the beginning, getting the length, and (by knowing the implementation) traversing the list.
Exposing Internal Details
As it stands now, it seems to rely on client code to realize that it's implemented as a linked-list, so access to elements other than the top require client code to use peek to get a pointer to the top element, and then traverse the nodes of the linked list to get to the rest of the elements.
Avoiding Race Conditions
I'd also question (to at least some degree) inclusion of length in the interface. In a multi-threaded environment, almost all possible uses of length are practically guaranteed to introduce race conditions--by the time you actually try to use a length you've read, the length may have changed, so you're using obsolete information.
It can (at least sort of) make some sense to use a length for things like logging, simply to give the user at least some notion of whether the stack is (for example) growing constantly (but in this case, that's pretty much a given, since you've omitted a pop from its interface).
Side Note
It's not immediately apparent whether you're thinking of Runner as part of the Stack itself, or an ancillary class you included to demonstrate/test the actual Stack. At least for now, I've treated it as ancillary, so it shows how the Stack is used, but I haven't really tried to review it in itself.