<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Memory order relaxed?]]></title><description><![CDATA[<p>hi!<br />
ich habe eine linked list erstellt. immer, wenn ein element am ende engefügt wird, soll im neuen element die nummer des vorigen elementes + 1 gespeichert werden.</p>
<p>wird der folgende code garantiert immer</p>
<pre><code>20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
</code></pre>
<p>ausgeben?</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;atomic&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;chrono&gt;

struct node {
	node* prev = nullptr;
	int sequence = 0;
};

std::atomic&lt;bool&gt; start{ };
std::atomic&lt;node*&gt; tail{ };

void add(node* n) {
	node* expected = tail.load(std::memory_order_relaxed);
	do {
		n-&gt;prev = expected;
		n-&gt;sequence = expected-&gt;sequence + 1;
	} while(!tail.compare_exchange_weak(expected, n, std::memory_order_relaxed));
}

int main() {
	tail.store(new node);
	std::vector&lt;node*&gt; nodes(20);
	std::generate(std::begin(nodes), std::end(nodes), []() { return new node; });

	for(auto n : nodes) {
		std::thread([](std::atomic&lt;bool&gt;&amp; start, node* n) {
			while(!start.load()) std::this_thread::yield();
			add(n);
		}, std::ref(start), n).detach();
	};

	start.store(true);

	while(tail.load(std::memory_order_relaxed)-&gt;sequence != 20) std::this_thread::yield();
	for(node* n = tail.load(); n != nullptr; n = n-&gt;prev)
		std::cout &lt;&lt; n-&gt;sequence &lt;&lt; ' ';
	std::cout &lt;&lt; std::endl;
}
</code></pre>
<p>ich behaupte nein: wenn ein thread A einen node in die liste einfügt (per CAS), dann sind für ihn die writes in zeile 20,21 <em>sequenced-before</em> dem CAS und für ihn damit auch <em>happened-before</em>, aber der thread hat mit keinem anderen thread eine &quot;synchronized-with&quot;-Beziehung.<br />
wenn jetzt ein zweiter thread B den neuen tail relaxed läd, kann es doch sein, dass der immer noch die alten werte von sequence und prev liest, obwohl der tail aktualisiert wurde, sodass jetzt eine Sequenznummer doppelt auftreten kann bzw. er den gleichen vorgänger wie der aus thread A ermittelt, richtig? und das auch unabhängig davon, ob prev und sequence atomics sind oder nicht.</p>
<p>richtig wäre also:</p>
<pre><code class="language-cpp">void add(node* n) {
	node* expected = tail.load(std::memory_order_acquire);
	do {
		n-&gt;prev = expected;
		n-&gt;sequence = expected-&gt;sequence + 1;
	} while(!tail.compare_exchange_weak(expected, n, std::memory_order_release, std::memory_order_relaxed));
}
</code></pre>
<p>wenn der CAS im thread A erfolgreich war, sind alle threads, die den tail infolgedessen mit acquire laden <em>synchronized-with</em> dem thread A. wenn der CAS fehlschlug, ist's eh egal, da dann sowieso nochmal geändert werden muss.</p>
<p>stimmt das so?</p>
]]></description><link>https://www.c-plusplus.net/forum/topic/333149/memory-order-relaxed</link><generator>RSS for Node</generator><lastBuildDate>Mon, 27 Apr 2026 11:32:38 GMT</lastBuildDate><atom:link href="https://www.c-plusplus.net/forum/topic/333149.rss" rel="self" type="application/rss+xml"/><pubDate>Mon, 15 Jun 2015 13:15:06 GMT</pubDate><ttl>60</ttl><item><title><![CDATA[Reply to Memory order relaxed? on Mon, 15 Jun 2015 13:15:06 GMT]]></title><description><![CDATA[<p>hi!<br />
ich habe eine linked list erstellt. immer, wenn ein element am ende engefügt wird, soll im neuen element die nummer des vorigen elementes + 1 gespeichert werden.</p>
<p>wird der folgende code garantiert immer</p>
<pre><code>20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
</code></pre>
<p>ausgeben?</p>
<pre><code class="language-cpp">#include &lt;iostream&gt;
#include &lt;thread&gt;
#include &lt;atomic&gt;
#include &lt;vector&gt;
#include &lt;algorithm&gt;
#include &lt;chrono&gt;

struct node {
	node* prev = nullptr;
	int sequence = 0;
};

std::atomic&lt;bool&gt; start{ };
std::atomic&lt;node*&gt; tail{ };

void add(node* n) {
	node* expected = tail.load(std::memory_order_relaxed);
	do {
		n-&gt;prev = expected;
		n-&gt;sequence = expected-&gt;sequence + 1;
	} while(!tail.compare_exchange_weak(expected, n, std::memory_order_relaxed));
}

int main() {
	tail.store(new node);
	std::vector&lt;node*&gt; nodes(20);
	std::generate(std::begin(nodes), std::end(nodes), []() { return new node; });

	for(auto n : nodes) {
		std::thread([](std::atomic&lt;bool&gt;&amp; start, node* n) {
			while(!start.load()) std::this_thread::yield();
			add(n);
		}, std::ref(start), n).detach();
	};

	start.store(true);

	while(tail.load(std::memory_order_relaxed)-&gt;sequence != 20) std::this_thread::yield();
	for(node* n = tail.load(); n != nullptr; n = n-&gt;prev)
		std::cout &lt;&lt; n-&gt;sequence &lt;&lt; ' ';
	std::cout &lt;&lt; std::endl;
}
</code></pre>
<p>ich behaupte nein: wenn ein thread A einen node in die liste einfügt (per CAS), dann sind für ihn die writes in zeile 20,21 <em>sequenced-before</em> dem CAS und für ihn damit auch <em>happened-before</em>, aber der thread hat mit keinem anderen thread eine &quot;synchronized-with&quot;-Beziehung.<br />
wenn jetzt ein zweiter thread B den neuen tail relaxed läd, kann es doch sein, dass der immer noch die alten werte von sequence und prev liest, obwohl der tail aktualisiert wurde, sodass jetzt eine Sequenznummer doppelt auftreten kann bzw. er den gleichen vorgänger wie der aus thread A ermittelt, richtig? und das auch unabhängig davon, ob prev und sequence atomics sind oder nicht.</p>
<p>richtig wäre also:</p>
<pre><code class="language-cpp">void add(node* n) {
	node* expected = tail.load(std::memory_order_acquire);
	do {
		n-&gt;prev = expected;
		n-&gt;sequence = expected-&gt;sequence + 1;
	} while(!tail.compare_exchange_weak(expected, n, std::memory_order_release, std::memory_order_relaxed));
}
</code></pre>
<p>wenn der CAS im thread A erfolgreich war, sind alle threads, die den tail infolgedessen mit acquire laden <em>synchronized-with</em> dem thread A. wenn der CAS fehlschlug, ist's eh egal, da dann sowieso nochmal geändert werden muss.</p>
<p>stimmt das so?</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456757</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456757</guid><dc:creator><![CDATA[Relax0r]]></dc:creator><pubDate>Mon, 15 Jun 2015 13:15:06 GMT</pubDate></item><item><title><![CDATA[Reply to Memory order relaxed? on Mon, 15 Jun 2015 19:51:42 GMT]]></title><description><![CDATA[<p>Relax0r schrieb:</p>
<blockquote>
<p>richtig wäre also:</p>
<pre><code class="language-cpp">void add(node* n) {
	node* expected = tail.load(std::memory_order_acquire);
	do {
		n-&gt;prev = expected;
		n-&gt;sequence = expected-&gt;sequence + 1;
	} while(!tail.compare_exchange_weak(expected, n, std::memory_order_release, std::memory_order_relaxed));
}
</code></pre>
</blockquote>
<p>Der Fehlerfall bedarf acquire wegen des späteren Lesens von expected-&gt;sequence.<br />
Normalerweise genügt es, ein Orderingargument anzubieten (std::memory_order_acq_rel). Der zweite Parameter wird automatisch passend gewählt.</p>
]]></description><link>https://www.c-plusplus.net/forum/post/2456780</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456780</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Mon, 15 Jun 2015 19:51:42 GMT</pubDate></item><item><title><![CDATA[Reply to Memory order relaxed? on Mon, 15 Jun 2015 21:57:55 GMT]]></title><description><![CDATA[<p>Würde hier auch <code>memory_order_consume</code> reichen?<br />
Also</p>
<pre><code class="language-cpp">void add(node* n) {
    node* expected = tail.load(std::memory_order_consume);
    do {
        n-&gt;prev = expected;
        n-&gt;sequence = expected-&gt;sequence + 1;
    } while(!tail.compare_exchange_weak(expected, n, std::memory_order_release, std::memory_order_consume));
}
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/2456793</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456793</guid><dc:creator><![CDATA[hustbaer]]></dc:creator><pubDate>Mon, 15 Jun 2015 21:57:55 GMT</pubDate></item><item><title><![CDATA[Reply to Memory order relaxed? on Tue, 16 Jun 2015 01:02:27 GMT]]></title><description><![CDATA[<p>hustbaer schrieb:</p>
<blockquote>
<p>Würde hier auch <code>memory_order_consume</code> reichen?</p>
</blockquote>
<p>Ich denke schon.</p>
<pre><code>Thread A

(1) n-&gt;sequence = ...                                                                                           -|A
    .                                                                                                            |
    . (sequenced before)                                                                                         |
    .                                                                                                            |
(2) *n = expected (CAS, release)                                              -|A                                |X- (inter-thread happens before 1.10/13.3.2)
    .                                                                          |                                 |
    . (dependency-ordered 1.10/12.1)                                           |                                 |
--------------------------------                                               |                                 |
Thread B                                                                       |                                 |
    .                                                                          |                                 |
(3) expected = *n (CAS, consume)           -|A                                 |X (dependency-ordered 1.10/12.2) |
    .                                       |                                  |                                 |
    . (carries dependency 1.10/11.2)        |                                  |                                 |
    .                                       |                                  |                                 |
(4) expected (lvalue-&gt;rvalue)               |X (carries dependency 1.10/11.3)  |                                 |
    .                                       |                                  |                                 |
    . (carries dependency 1.10/11.1)        |                                  |                                 |
    .                                       |                                  |                                 |
(5) expected-&gt;sequence (lvalue-&gt;rvalue)    -|B                                -|B                               -|B
</code></pre>
]]></description><link>https://www.c-plusplus.net/forum/post/2456796</link><guid isPermaLink="true">https://www.c-plusplus.net/forum/post/2456796</guid><dc:creator><![CDATA[camper]]></dc:creator><pubDate>Tue, 16 Jun 2015 01:02:27 GMT</pubDate></item></channel></rss>