1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218
|
<!DOCTYPE html
PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<!-- saved from url=(0014)about:internet -->
<html xmlns:MSHelp="http://www.microsoft.com/MSHelp/" lang="en-us" xml:lang="en-us"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="DC.Type" content="topic">
<meta name="DC.Title" content="Fenced Data Transfer">
<meta name="DC.subject" content="Fenced Data Transfer">
<meta name="keywords" content="Fenced Data Transfer">
<meta name="DC.Relation" scheme="URI" content="../../tbb_userguide/Design_Patterns/Design_Patterns.htm">
<meta name="DC.Relation" scheme="URI" content="Lazy_Initialization.htm#Lazy_Initialization">
<meta name="DC.Format" content="XHTML">
<meta name="DC.Identifier" content="Fenced_Data_Transfer">
<link rel="stylesheet" type="text/css" href="../../intel_css_styles.css">
<title>Fenced Data Transfer</title>
<xml>
<MSHelp:Attr Name="DocSet" Value="Intel"></MSHelp:Attr>
<MSHelp:Attr Name="Locale" Value="kbEnglish"></MSHelp:Attr>
<MSHelp:Attr Name="TopicType" Value="kbReference"></MSHelp:Attr>
</xml>
</head>
<body id="Fenced_Data_Transfer">
<!-- ==============(Start:NavScript)================= -->
<script src="..\..\NavScript.js" language="JavaScript1.2" type="text/javascript"></script>
<script language="JavaScript1.2" type="text/javascript">WriteNavLink(2);</script>
<!-- ==============(End:NavScript)================= -->
<a name="Fenced_Data_Transfer"><!-- --></a>
<h1 class="topictitle1">Fenced Data Transfer</h1>
<div>
<div class="section"><h2 class="sectiontitle">Problem</h2>
<p>Write a message to memory and have another processor read it on
hardware that does not have a sequentially consistent memory model.
</p>
</div>
<div class="section"><h2 class="sectiontitle">Context</h2>
<p>The problem normally arises only when unsynchronized threads
concurrently act on a memory location, or are using reads and writes to create
synchronization. High level synchronization constructs normally include
mechanisms that prevent unwanted reordering.
</p>
<p>Modern hardware and compilers can reorder memory operations in a way
that preserves the order of a thread's operation from its viewpoint, but not as
observed by other threads. A serial common idiom is to write a message and mark
it as ready to ready as shown in the following code:
</p>
<pre>bool Ready;
std::string Message;
void Send( const std::string& src ) {. // Executed by thread 1
Message=src;
Ready = true;
}
bool Receive( std::string& dst ) { // Executed by thread 2
bool result = Ready;
if( result ) dst=Message;
return result; // Return true if message was received.
}</pre>
<p>Two key assumptions of the code are:
</p>
<ol class="abc">
<li>
<p><samp class="codeph">Ready</samp> does not become true until
<samp class="codeph">Message</samp> is written.
</p>
</li>
<li>
<p><samp class="codeph">Message</samp> is not read until
<samp class="codeph">Ready</samp> becomes true.
</p>
</li>
</ol>
<p>These assumptions are trivially true on uniprocessor hardware.
However, they may break on multiprocessor hardware. Reordering by the hardware
or compiler can cause the sender's writes to appear out of order to the
receiver (thus breaking condition a) or the receiver's reads to appear out of
order (thus breaking condition b).
</p>
</div>
<div class="section"><h2 class="sectiontitle">Forces</h2>
<ul type="disc">
<li>
<p>Creating synchronization via raw reads and writes.
</p>
</li>
</ul>
</div>
<div class="section"><h2 class="sectiontitle">Related</h2>
<ul type="disc">
<li>Lazy Initialization
</li>
</ul>
</div>
<div class="section"><h2 class="sectiontitle">Solution</h2>
<p>Change the flag from
<samp class="codeph">bool</samp> to
<samp class="codeph">tbb::atomic<bool></samp> for the flag that indicates
when the message is ready. Here is the previous example, with modifications
shown in
<samp class="codeph"><span style="color:blue"><strong>bold font</strong></span></samp>.
</p>
<pre><span style="color:blue"><strong>tbb::atomic<</strong></span>bool<span style="color:blue"><strong>></strong></span> Ready;
std::string Message;
void Send( const std::string& src ) {. // Executed by thread 1
Message=src;
Ready = true;
}
bool Receive( std::string& dst ) { // Executed by thread 2
bool result = Ready;
if( result ) dst=Message;
return result; // Return true if message was received.
}</pre>
<p>A write to a
<samp class="codeph">tbb::atomic</samp> value has
<em>release</em> semantics, which means that all of its prior writes will
be seen before the releasing write. A read from
<samp class="codeph">tbb::atomic</samp> value has
<em>acquire</em> semantics, which means that all of its subsequent reads
will happen after the acquiring read. The implementation of
<samp class="codeph">tbb::atomic</samp> ensures that both the compiler and the
hardware observe these ordering constraints.
</p>
</div>
<div class="section"><h2 class="sectiontitle">Variations</h2>
<p>Higher level synchronization constructs normally include the necessary
<em>acquire</em> and
<em>release</em> fences. For example, mutexes are normally implemented
such that acquisition of a lock has
<em>acquire</em> semantics and release of a lock has
<em>release</em> semantics. Thus a thread that acquires a lock on a mutex
always sees any memory writes done by another thread before it released a lock
on that mutex.
</p>
</div>
<div class="section"><h2 class="sectiontitle">Non Solutions</h2>
<p>Mistaken solutions are so often proposed that it is worth
understanding why they are wrong.
</p>
<p>One common mistake is to assume that declaring the flag with the
<samp class="codeph">volatile</samp> keyword solves the problem. Though the
<samp class="codeph">volatile</samp> keyword forces a write to happen immediately,
it generally has no effect on the visible ordering of that write with respect
to other memory operations. An exception to this rule are processors from the
Intel® Itanium® processor family, which by convention assign acquire semantics
to
<samp class="codeph">volatile</samp> reads and release semantics to volatile
writes.
</p>
<p>Another mistake is to assume that conditionally executed code cannot
happen before the condition is tested. However, the compiler or hardware may
speculatively hoist the conditional code above the condition.
</p>
<p>Similarly, it is a mistake to assume that a processor cannot read the
target of a pointer before reading the pointer. A modern processor does not
read individual values from main memory. It reads cache lines. The target of a
pointer may be in a cache line that has already been read before the pointer
was read, thus giving the appearance that the processor presciently read the
pointer target.
</p>
</div>
</div>
<div class="familylinks">
<div class="parentlink"><strong>Parent topic:</strong> <a href="../../tbb_userguide/Design_Patterns/Design_Patterns.htm">Design Patterns</a></div>
</div>
<div class="See Also">
<h2>See Also</h2>
<div class="linklist">
<div><a href="Lazy_Initialization.htm#Lazy_Initialization">Lazy Initialization
</a></div></div>
</div>
</body>
</html>
|