970 lines
No EOL
69 KiB
HTML
970 lines
No EOL
69 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
|
||
|
||
|
||
<html lang="en">
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||
|
||
<title>6.5. Thread Arguments and Return Values — Computer Systems Fundamentals</title>
|
||
|
||
<link rel="stylesheet" href="_static/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous" />
|
||
<link rel="stylesheet" href="_static/css/pygments.css" type="text/css" />
|
||
<link rel="stylesheet" href="_static/css/normalize.css" type="text/css" />
|
||
<link rel="stylesheet" href="../../../JSAV/css/JSAV.css" type="text/css" />
|
||
<link rel="stylesheet" href="../../../lib/odsaMOD-min.css" type="text/css" />
|
||
<link rel="stylesheet" href="_static/css/jquery-1.11.4-smoothness-ui.css" type="text/css" />
|
||
<link rel="stylesheet" href="../../../lib/odsaStyle-min.css" type="text/css" />
|
||
<link rel="stylesheet" href="_static/css/csf.css" type="text/css" />
|
||
|
||
<style>
|
||
.underline { text-decoration: underline; }
|
||
</style>
|
||
|
||
<script type="text/javascript">
|
||
var DOCUMENTATION_OPTIONS = {
|
||
URL_ROOT: './',
|
||
VERSION: '0.4.1',
|
||
COLLAPSE_INDEX: false,
|
||
FILE_SUFFIX: '.html',
|
||
HAS_SOURCE: true
|
||
};
|
||
</script>
|
||
|
||
<script type="text/x-mathjax-config">
|
||
MathJax.Hub.Config({
|
||
tex2jax: {
|
||
inlineMath: [['$','$'], ['\\(','\\)']],
|
||
displayMath: [ ['$$','$$'], ["\\[","\\]"] ],
|
||
processEscapes: true
|
||
},
|
||
"HTML-CSS": {
|
||
scale: "80"
|
||
}
|
||
});
|
||
</script>
|
||
<link rel="shortcut icon" href="_static/favicon.ico"/>
|
||
<link rel="index" title="Index" href="genindex.html" />
|
||
<link rel="search" title="Search" href="search.html" />
|
||
<link rel="index" title="Computer Systems Fundamentals" href="index.html" />
|
||
<link rel="next" title="6. Implicit Threading and Language-based Threads" href="ImplicitThreads.html" />
|
||
<link rel="prev" title="4. POSIX Thread Library" href="POSIXThreads.html" />
|
||
|
||
</head><body>
|
||
|
||
<nav class="navbar navbar-expand-md navbar-dark navbar-custom fixed-top">
|
||
|
||
<a class="navbar-brand py-0" href="index.html"><img src="_static/CSF-Logo-Square-Text.png" alt="OpenCSF Logo" height="40em" class="py-1 px-2 mb-0 align-center rounded-lg bg-white" /></a>
|
||
<!-- Show a navbar toggler on mobile -->
|
||
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#defaultNavbars" aria-controls="defaultNavbars" aria-expanded="false" aria-label="Toggle navigation">
|
||
<span class="navbar-toggler-icon"></span>
|
||
</button>
|
||
<div class="collapse navbar-collapse" id="defaultNavbars">
|
||
<ul class="navbar-nav mr-auto">
|
||
<li class="nav-item dropdown">
|
||
<a class="nav-link dropdown-toggle jmu-gold rounded" href="ThreadArgs.html#" id="navbarDropdownChapters" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">Contents</a>
|
||
<div class="dropdown-menu scrollable-menu" role="menu" aria-labelledby="navbarDropdownChapters">
|
||
<a class="dropdown-item" tabindex="-1" href="ThreadArgs.html#"><b>Chapter 1</b></a>
|
||
<a class="dropdown-item" href="IntroConcSysOverview.html"> 1.1. Introduction to Concurrent Systems</a>
|
||
<a class="dropdown-item" href="SysAndModels.html"> 1.2. Systems and Models</a>
|
||
<a class="dropdown-item" href="Themes.html"> 1.3. Themes and Guiding Principles</a>
|
||
<a class="dropdown-item" href="Architectures.html"> 1.4. System Architectures</a>
|
||
<a class="dropdown-item" href="StateModels.html"> 1.5. State Models in UML</a>
|
||
<a class="dropdown-item" href="SequenceModels.html"> 1.6. Sequence Models in UML</a>
|
||
<a class="dropdown-item" href="StateModelImplementation.html"> 1.7. Extended Example: State Model Implementation</a>
|
||
<div class="dropdown-divider"></div>
|
||
<a class="dropdown-item disabled"><b>Chapter 2</b></a>
|
||
<a class="dropdown-item" href="ProcessesOverview.html"> 2.1. Processes and OS Basics</a>
|
||
<a class="dropdown-item" href="Multiprogramming.html"> 2.2. Processes and Multiprogramming</a>
|
||
<a class="dropdown-item" href="KernelMechanics.html"> 2.3. Kernel Mechanics</a>
|
||
<a class="dropdown-item" href="Syscall.html"> 2.4. System Call Interface</a>
|
||
<a class="dropdown-item" href="ProcessCycle.html"> 2.5. Process Life Cycle</a>
|
||
<a class="dropdown-item" href="UnixFile.html"> 2.6. The UNIX File Abstraction</a>
|
||
<a class="dropdown-item" href="EventsSignals.html"> 2.7. Events and Signals</a>
|
||
<a class="dropdown-item" href="Extended2Processes.html"> 2.8. Extended Example: Listing Files with Processes</a>
|
||
<div class="dropdown-divider"></div>
|
||
<a class="dropdown-item disabled"><b>Chapter 3</b></a>
|
||
<a class="dropdown-item" href="IPCOverview.html"> 3.1. Concurrency with IPC</a>
|
||
<a class="dropdown-item" href="IPCModels.html"> 3.2. IPC Models</a>
|
||
<a class="dropdown-item" href="Pipes.html"> 3.3. Pipes and FIFOs</a>
|
||
<a class="dropdown-item" href="MMap.html"> 3.4. Shared Memory With Memory-mapped Files</a>
|
||
<a class="dropdown-item" href="POSIXvSysV.html"> 3.5. POSIX vs. System V IPC</a>
|
||
<a class="dropdown-item" href="MQueues.html"> 3.6. Message Passing With Message Queues</a>
|
||
<a class="dropdown-item" href="ShMem.html"> 3.7. Shared Memory</a>
|
||
<a class="dropdown-item" href="IPCSems.html"> 3.8. Semaphores</a>
|
||
<a class="dropdown-item" href="Extended3Bash.html"> 3.9. Extended Example: Bash-lite: A Simple Command-line Shell</a>
|
||
<div class="dropdown-divider"></div>
|
||
<a class="dropdown-item disabled"><b>Chapter 4</b></a>
|
||
<a class="dropdown-item" href="SocketsOverview.html"> 4.1. Networked Concurrency</a>
|
||
<a class="dropdown-item" href="FiveLayer.html"> 4.2. The TCP/IP Internet Model</a>
|
||
<a class="dropdown-item" href="NetApps.html"> 4.3. Network Applications and Protocols</a>
|
||
<a class="dropdown-item" href="Sockets.html"> 4.4. The Socket Interface</a>
|
||
<a class="dropdown-item" href="TCPSockets.html"> 4.5. TCP Socket Programming: HTTP</a>
|
||
<a class="dropdown-item" href="UDPSockets.html"> 4.6. UDP Socket Programming: DNS</a>
|
||
<a class="dropdown-item" href="AppBroadcast.html"> 4.7. Application-Layer Broadcasting: DHCP</a>
|
||
<a class="dropdown-item" href="Extended4CGI.html"> 4.8. Extended Example: CGI Web Server</a>
|
||
<div class="dropdown-divider"></div>
|
||
<a class="dropdown-item disabled"><b>Chapter 5</b></a>
|
||
<a class="dropdown-item" href="InternetOverview.html"> 5.1. The Internet and Connectivity</a>
|
||
<a class="dropdown-item" href="AppLayer.html"> 5.2. Application Layer: Overlay Networks</a>
|
||
<a class="dropdown-item" href="TransLayer.html"> 5.3. Transport Layer</a>
|
||
<a class="dropdown-item" href="NetSec.html"> 5.4. Network Security Fundamentals</a>
|
||
<a class="dropdown-item" href="NetLayer.html"> 5.5. Network Layer: IP</a>
|
||
<a class="dropdown-item" href="LinkLayer.html"> 5.6. Link Layer</a>
|
||
<a class="dropdown-item" href="Wireless.html"> 5.7. Wireless Connectivity: Wi-Fi, Bluetooth, and Zigbee</a>
|
||
<a class="dropdown-item" href="Extended5DNS.html"> 5.8. Extended Example: DNS client</a>
|
||
<div class="dropdown-divider"></div>
|
||
<a class="dropdown-item disabled"><b>Chapter 6</b></a>
|
||
<a class="dropdown-item" href="ThreadsOverview.html"> 6.1. Concurrency with Multithreading</a>
|
||
<a class="dropdown-item" href="ProcVThreads.html"> 6.2. Processes vs. Threads</a>
|
||
<a class="dropdown-item" href="RaceConditions.html"> 6.3. Race Conditions and Critical Sections</a>
|
||
<a class="dropdown-item" href="POSIXThreads.html"> 6.4. POSIX Thread Library</a>
|
||
<a class="dropdown-item" href="ThreadArgs.html"> 6.5. Thread Arguments and Return Values</a>
|
||
<a class="dropdown-item" href="ImplicitThreads.html"> 6.6. Implicit Threading and Language-based Threads</a>
|
||
<a class="dropdown-item" href="Extended6Input.html"> 6.7. Extended Example: Keyboard Input Listener</a>
|
||
<a class="dropdown-item" href="Extended6Primes.html"> 6.8. Extended Example: Concurrent Prime Number Search</a>
|
||
<div class="dropdown-divider"></div>
|
||
<a class="dropdown-item disabled"><b>Chapter 7</b></a>
|
||
<a class="dropdown-item" href="SynchOverview.html"> 7.1. Synchronization Primitives</a>
|
||
<a class="dropdown-item" href="CritSect.html"> 7.2. Critical Sections and Peterson's Solution</a>
|
||
<a class="dropdown-item" href="Locks.html"> 7.3. Locks</a>
|
||
<a class="dropdown-item" href="Semaphores.html"> 7.4. Semaphores</a>
|
||
<a class="dropdown-item" href="Barriers.html"> 7.5. Barriers</a>
|
||
<a class="dropdown-item" href="Condvars.html"> 7.6. Condition Variables</a>
|
||
<a class="dropdown-item" href="Deadlock.html"> 7.7. Deadlock</a>
|
||
<a class="dropdown-item" href="Extended7Events.html"> 7.8. Extended Example: Event Log File</a>
|
||
<div class="dropdown-divider"></div>
|
||
<a class="dropdown-item disabled"><b>Chapter 8</b></a>
|
||
<a class="dropdown-item" href="SynchProblemsOverview.html"> 8.1. Synchronization Patterns and Problems</a>
|
||
<a class="dropdown-item" href="SynchDesign.html"> 8.2. Basic Synchronization Design Patterns</a>
|
||
<a class="dropdown-item" href="ProdCons.html"> 8.3. Producer-Consumer Problem</a>
|
||
<a class="dropdown-item" href="ReadWrite.html"> 8.4. Readers-Writers Problem</a>
|
||
<a class="dropdown-item" href="DiningPhil.html"> 8.5. Dining Philosophers Problem and Deadlock</a>
|
||
<a class="dropdown-item" href="CigSmokers.html"> 8.6. Cigarette Smokers Problem and the Limits of Semaphores and Locks</a>
|
||
<a class="dropdown-item" href="Extended8ModExp.html"> 8.7. Extended Example: Parallel Modular Exponentiation</a>
|
||
<div class="dropdown-divider"></div>
|
||
<a class="dropdown-item disabled"><b>Chapter 9</b></a>
|
||
<a class="dropdown-item" href="ParallelDistributedOverview.html"> 9.1. Parallel and Distributed Systems</a>
|
||
<a class="dropdown-item" href="ParVConc.html"> 9.2. Parallelism vs. Concurrency</a>
|
||
<a class="dropdown-item" href="ParallelDesign.html"> 9.3. Parallel Design Patterns</a>
|
||
<a class="dropdown-item" href="Scaling.html"> 9.4. Limits of Parallelism and Scaling</a>
|
||
<a class="dropdown-item" href="DistTiming.html"> 9.5. Timing in Distributed Environments</a>
|
||
<a class="dropdown-item" href="DistDataStorage.html"> 9.6. Reliable Data Storage and Location</a>
|
||
<a class="dropdown-item" href="DistConsensus.html"> 9.7. Consensus in Distributed Systems</a>
|
||
<a class="dropdown-item" href="Extended9Blockchain.html"> 9.8. Extended Example: Blockchain Proof-of-Work</a>
|
||
<div class="dropdown-divider"></div>
|
||
<a class="dropdown-item disabled"><b>Appendix A</b></a>
|
||
<a class="dropdown-item" href="CLangOverview.html"> A.1. C Language Reintroduction</a>
|
||
<a class="dropdown-item" href="Debugging.html"> A.2. Documentation and Debugging</a>
|
||
<a class="dropdown-item" href="BasicTypes.html"> A.3. Basic Types and Pointers</a>
|
||
<a class="dropdown-item" href="Arrays.html"> A.4. Arrays, Structs, Enums, and Type Definitions</a>
|
||
<a class="dropdown-item" href="Functions.html"> A.5. Functions and Scope</a>
|
||
<a class="dropdown-item" href="Pointers.html"> A.6. Pointers and Dynamic Allocation</a>
|
||
<a class="dropdown-item" href="Strings.html"> A.7. Strings</a>
|
||
<a class="dropdown-item" href="FunctionPointers.html"> A.8. Function Pointers</a>
|
||
<a class="dropdown-item" href="Files.html"> A.9. Files</a>
|
||
</div>
|
||
</li>
|
||
|
||
|
||
|
||
</ul>
|
||
</div>
|
||
|
||
<ul class="navbar-nav flex-row ml-md-auto d-none d-md-flex">
|
||
<li class="nav-item"><a class="nav-link jmu-gold" href="https://w3.cs.jmu.edu/kirkpams/OpenCSF/Books/csf/source/ThreadArgs.rst"
|
||
target="_blank" rel="nofollow">Show Source</a></li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
|
||
<div class="container center">
|
||
«  <a id="prevmod" href="POSIXThreads.html">6.4. POSIX Thread Library</a>
|
||
  ::  
|
||
<a class="uplink" href="index.html">Contents</a>
|
||
  ::  
|
||
<a id="nextmod" href="ImplicitThreads.html">6.6. Implicit Threading and Language-based Threads</a>  »
|
||
|
||
</div>
|
||
<br />
|
||
|
||
|
||
|
||
<script type="text/javascript" src="_static/js/jquery-2.1.4.min.js"></script>
|
||
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.1/MathJax.js?config=TeX-AMS-MML_HTMLorMML"></script>
|
||
<script type="text/javascript" src="_static/js/jquery-1.11.4-ui.min.js"></script>
|
||
<script type="text/javascript" src="_static/js/forge-0.7.0.min.js"></script>
|
||
<script type="text/javascript" src="../../../JSAV/lib/jquery.transit.js"></script>
|
||
<script type="text/javascript" src="../../../JSAV/lib/raphael.js"></script>
|
||
<script type="text/javascript" src="../../../JSAV/build/JSAV-min.js"></script>
|
||
<script type="text/javascript" src="_static/js/config.js"></script>
|
||
<script type="text/javascript" src="../../../lib/odsaUtils-min.js"></script>
|
||
<script type="text/javascript" src="../../../lib/odsaMOD-min.js"></script>
|
||
<script type="text/javascript" src="_static/js/d3-4.13.0.min.js"></script>
|
||
<script type="text/javascript" src="_static/js/d3-selection-multi.v1.min.js"></script>
|
||
<script type="text/javascript" src="../../../lib/dataStructures.js"></script>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<div class="container">
|
||
|
||
<script>ODSA.SETTINGS.DISP_MOD_COMP = true;ODSA.SETTINGS.MODULE_NAME = "ThreadArgs";ODSA.SETTINGS.MODULE_LONG_NAME = "Thread Arguments and Return Values";ODSA.SETTINGS.MODULE_CHAPTER = "Concurrency with Multithreading"; ODSA.SETTINGS.BUILD_DATE = "2021-06-01 15:31:50"; ODSA.SETTINGS.BUILD_CMAP = false;JSAV_OPTIONS['lang']='en';JSAV_EXERCISE_OPTIONS['code']='java';</script><div class="section" id="thread-arguments-and-return-values">
|
||
<h1>6.5. Thread Arguments and Return Values<a class="headerlink" href="ThreadArgs.html#thread-arguments-and-return-values" title="Permalink to this headline">¶</a></h1>
|
||
<p>The <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code> imposes a strict format on the prototype of the
|
||
function that will run in the new thread. It must take a single <code class="docutils literal notranslate"><span class="pre">void*</span></code>
|
||
parameter and return a single <code class="docutils literal notranslate"><span class="pre">void*</span></code> value. The last parameter of
|
||
<code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code> is passed as the argument to the function, whereas the
|
||
return value is passed using <code class="docutils literal notranslate"><span class="pre">pthread_exit()</span></code> and <code class="docutils literal notranslate"><span class="pre">pthread_join()</span></code>. This
|
||
section looks at the details of these mechanisms and their implications.</p>
|
||
<div class="section" id="passing-a-single-argument-to-threads">
|
||
<h2>6.5.1. Passing a Single Argument to Threads<a class="headerlink" href="ThreadArgs.html#passing-a-single-argument-to-threads" title="Permalink to this headline">¶</a></h2>
|
||
<p>Passing a single argument to a thread seems straightforward, but is easy to do
|
||
incorrectly. As a simple example to illustrate the danger, <a class="reference external" href="ThreadArgs.html#cl6-5">Code Listing 6.5</a>
|
||
is designed to run in a separate thread:</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-5"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.5:</span>
|
||
<span class="cm"> A thread that will print a single integer value</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="kt">void</span> <span class="o">*</span>
|
||
<span class="nf">child_thread</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* POTENTIALLY DANGEROUS TIMING */</span>
|
||
<span class="kt">int</span> <span class="o">*</span><span class="n">argptr</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span> <span class="o">*</span><span class="p">)</span> <span class="n">args</span><span class="p">;</span>
|
||
<span class="kt">int</span> <span class="n">arg</span> <span class="o">=</span> <span class="o">*</span><span class="n">argptr</span><span class="p">;</span>
|
||
|
||
<span class="cm">/* Print the local copy of the argument */</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"Argument is %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">arg</span><span class="p">);</span>
|
||
<span class="n">pthread_exit</span> <span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>The danger of this code can be illustrated with the loop in <a class="reference external" href="ThreadArgs.html#cl6-6">Code Listing 6.6</a>.
|
||
The intent is to pass the value 1 to the first thread, 2 to the
|
||
second, and so on. However, it is critical to note that <strong>there is only a single
|
||
copy of the</strong> <code class="docutils literal notranslate"><span class="pre">i</span></code> <strong>variable</strong>. That is, this code passes the address of the
|
||
single variable to all 10 threads; the code almost certainly does not pass the
|
||
intended values.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-6"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0">1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.6:</span>
|
||
<span class="cm"> Passing a pointer to a variable that repeatedly changes is a common error with threads</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* BAD CODE - DON'T DO THIS */</span>
|
||
<span class="cm">/* What value is actually passed to the thread? */</span>
|
||
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
|
||
<span class="n">assert</span> <span class="p">(</span><span class="n">pthread_create</span> <span class="p">(</span><span class="o">&</span><span class="n">child</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">child_thread</span><span class="p">,</span> <span class="o">&</span><span class="n">i</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>The key problem is that thread creation and execution is <a class="reference internal" href="Glossary.html#term-asynchronous"><span class="xref std std-term">asynchronous</span></a>.
|
||
That means that it is impossible to predict when each of the new threads start
|
||
running. One possible timing is that all 10 threads are created first, leading
|
||
to <code class="docutils literal notranslate"><span class="pre">i</span></code> storing the value 11. At that point, each of the threads dereference
|
||
their respective <code class="docutils literal notranslate"><span class="pre">argptr</span></code> variable and all get the same value of 11.</p>
|
||
<p>One common solution to this problem is to cast numeric values as pointers, as
|
||
shown in <a class="reference external" href="ThreadArgs.html#cl6-7">Code Listing 6.7</a>. That is, the int <code class="docutils literal notranslate"><span class="pre">i</span></code> variable gets cast
|
||
as a <code class="docutils literal notranslate"><span class="pre">(void*)</span></code> argument in the call to <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code>. Then, the
|
||
<code class="docutils literal notranslate"><span class="pre">void*</span></code> argument to <code class="docutils literal notranslate"><span class="pre">child_thread()</span></code> casts the argument back to a <code class="docutils literal notranslate"><span class="pre">int</span></code> instance.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-7"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0">1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.7:</span>
|
||
<span class="cm"> Each thread should be given a separate value, rather than a shared address</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* FIXED VERSION */</span>
|
||
<span class="cm">/* ints are passed by value, so a COPY gets passed to each call */</span>
|
||
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">1</span><span class="p">;</span> <span class="n">i</span> <span class="o"><=</span> <span class="mi">10</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
|
||
<span class="n">assert</span> <span class="p">(</span><span class="n">pthread_create</span> <span class="p">(</span><span class="o">&</span><span class="n">child</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">child_thread</span><span class="p">,</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span><span class="n">i</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>What makes this code work is the fact that scalar variables (e.g., <code class="docutils literal notranslate"><span class="pre">int</span></code>
|
||
variables) are passed using call-by-value semantics. When this code prepares for
|
||
the <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code> call, a separate copy of the current value of the <code class="docutils literal notranslate"><span class="pre">i</span></code>
|
||
variable is placed into a register or onto the stack. <a class="reference external" href="ThreadArgs.html#cl6-8">Code Listing 6.8</a> shows the corrected version of <a class="reference external" href="ThreadArgs.html#cl6-5">Code Listing 6.5</a>. The
|
||
<code class="docutils literal notranslate"><span class="pre">child_thread()</span></code> function then gets this copy, regardless of any changes to
|
||
the original <code class="docutils literal notranslate"><span class="pre">i</span></code> variable. When the child thread then casts its <code class="docutils literal notranslate"><span class="pre">args</span></code>
|
||
parameter to a local <code class="docutils literal notranslate"><span class="pre">arg_value</span></code>, it is working with the correct value that was passed.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-8"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16
|
||
17
|
||
18
|
||
19
|
||
20
|
||
21</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.8:</span>
|
||
<span class="cm"> A safer version of Code Listing 6.5</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* Convention: It is common to name a void* parameter with a name</span>
|
||
<span class="cm"> that begins with _, then cast it to a local variable that has</span>
|
||
<span class="cm"> the same (or nearly the same) name without the _. So _args will</span>
|
||
<span class="cm"> become args. Recall that _ has no special meaning and is treated</span>
|
||
<span class="cm"> like a normal alphabetical character. */</span>
|
||
|
||
<span class="kt">void</span> <span class="o">*</span>
|
||
<span class="nf">child_thread</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">_args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* Safe whenever size of int <= size of pointer (which is</span>
|
||
<span class="cm"> usually true) */</span>
|
||
<span class="kt">int</span> <span class="n">arg</span> <span class="o">=</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="n">_args</span><span class="p">;</span>
|
||
|
||
<span class="cm">/* Print the local copy of the argument */</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"Argument is %ld</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">arg</span><span class="p">);</span>
|
||
<span class="n">pthread_exit</span> <span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<div class="topic border border-dark rounded-lg alert-danger px-2 mb-3">
|
||
<div class="figure align-left">
|
||
<a class="reference internal image-reference" href="_images/CSF-Images-BugWarning.png"><img alt="Decorative bug warning" src="_images/CSF-Images-BugWarning.png" style="width: 90%;" /></a>
|
||
</div>
|
||
<p class="topic-title first pt-2 mb-1">Bug Warning</p><hr class="mt-1" />
|
||
<p>Casting integral values to pointers and back again is a common practice for
|
||
passing parameters to pthreads. However, while it is generally safe in
|
||
practice, it is potentially a bug on some platforms. Specifically, this
|
||
technique relies on the fact that pointers are at least as large as standard
|
||
integer types. That is, <code class="docutils literal notranslate"><span class="pre">int</span></code> variables are typically (but not required to
|
||
be) 32 bits in size. Modern CPU architectures tend to use 32- or 64-bit
|
||
addresses. As such, casting a 32-bit <code class="docutils literal notranslate"><span class="pre">int</span></code> up to a <code class="docutils literal notranslate"><span class="pre">void*</span></code> then back to a
|
||
32-bit <code class="docutils literal notranslate"><span class="pre">int</span></code> is safe.</p>
|
||
<p>On the other hand, assume the argument was declared as a <code class="docutils literal notranslate"><span class="pre">long</span></code> variable
|
||
instance. If the code is running on a 32-bit architecture (which is not
|
||
uncommon for virtualized systems) but the <code class="docutils literal notranslate"><span class="pre">long</span></code> type is 64 bits in size,
|
||
then half of the argument is lost by down-casting to the pointer for the call
|
||
to <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code>!</p>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="passing-multiple-arguments-to-threads">
|
||
<h2>6.5.2. Passing Multiple Arguments to Threads<a class="headerlink" href="ThreadArgs.html#passing-multiple-arguments-to-threads" title="Permalink to this headline">¶</a></h2>
|
||
<p>When passing multiple arguments to a child thread, the standard approach is to
|
||
group the arguments within a <code class="docutils literal notranslate"><span class="pre">struct</span></code> declaration, as shown in <a class="reference external" href="ThreadArgs.html#cl6-9">Code Listing
|
||
6.9</a>. The address of the <code class="docutils literal notranslate"><span class="pre">struct</span></code> instance gets passed as the
|
||
<code class="docutils literal notranslate"><span class="pre">arg</span></code> to <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code>. The new thread’s entry point receives a
|
||
<code class="docutils literal notranslate"><span class="pre">void*</span></code> parameter that can then be cast into the <code class="docutils literal notranslate"><span class="pre">struct</span></code> type.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-9"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16
|
||
17</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.9:</span>
|
||
<span class="cm"> Passing multiple arguments to a thread requires grouping them into a struct</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* Assume we have:</span>
|
||
<span class="cm"> struct thread_args {</span>
|
||
<span class="cm"> int first;</span>
|
||
<span class="cm"> const char *second;</span>
|
||
<span class="cm"> };</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="k">struct</span> <span class="n">thread_args</span> <span class="o">*</span><span class="n">args</span> <span class="o">=</span> <span class="n">malloc</span> <span class="p">(</span><span class="k">sizeof</span> <span class="p">(</span><span class="k">struct</span> <span class="n">thread_args</span><span class="p">));</span>
|
||
<span class="n">args</span><span class="o">-></span><span class="n">first</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
|
||
<span class="n">args</span><span class="o">-></span><span class="n">second</span> <span class="o">=</span> <span class="s">"Hello"</span><span class="p">;</span>
|
||
|
||
<span class="cm">/* Note that the data structure resides on the heap */</span>
|
||
<span class="n">assert</span> <span class="p">(</span><span class="n">pthread_create</span> <span class="p">(</span><span class="o">&</span><span class="n">child</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">hello_thread</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p><a class="reference external" href="ThreadArgs.html#cl6-10">Code Listing 6.10</a> shows the new thread receiving the pointer to the
|
||
<code class="docutils literal notranslate"><span class="pre">struct</span></code> and freeing the allocated memory when it is finished with the data.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-10"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.10:</span>
|
||
<span class="cm"> The child thread receives multiple values through the passed struct</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* Using the convention of casting _args to args */</span>
|
||
<span class="kt">void</span> <span class="o">*</span>
|
||
<span class="nf">hello_thread</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">_args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* Cast args into a meaningful pointer type that we can use */</span>
|
||
<span class="k">struct</span> <span class="n">thread_args</span> <span class="o">*</span><span class="n">args</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">thread_args</span> <span class="o">*</span><span class="p">)</span> <span class="n">_args</span><span class="p">;</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"First: %d; Second: '%s'</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">args</span><span class="o">-></span><span class="n">first</span><span class="p">,</span> <span class="n">args</span><span class="o">-></span><span class="n">second</span><span class="p">);</span>
|
||
|
||
<span class="cm">/* Do not forget to free the struct used for arguments */</span>
|
||
<span class="n">free</span> <span class="p">(</span><span class="n">args</span><span class="p">);</span>
|
||
<span class="n">pthread_exit</span> <span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<div class="topic border border-dark rounded-lg alert-danger px-2 mb-3">
|
||
<div class="figure align-left">
|
||
<a class="reference internal image-reference" href="_images/CSF-Images-BugWarning.png"><img alt="Decorative bug warning" src="_images/CSF-Images-BugWarning.png" style="width: 90%;" /></a>
|
||
</div>
|
||
<p class="topic-title first pt-2 mb-1">Bug Warning</p><hr class="mt-1" />
|
||
<p>A common mistake with passing arguments in this manner is to declare the
|
||
<code class="docutils literal notranslate"><span class="pre">struct</span></code> instance as a local variable instead of using dynamic allocation.
|
||
The problem, again, is the asynchronous nature of <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code>.
|
||
Consider this sample code:</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Create a local instance on the current thread's stack */</span>
|
||
<span class="k">struct</span> <span class="n">thread_args</span> <span class="n">args</span><span class="p">;</span>
|
||
<span class="n">args</span><span class="p">.</span><span class="n">first</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
|
||
<span class="n">args</span><span class="p">.</span><span class="n">second</span> <span class="o">=</span> <span class="s">"Hello"</span><span class="p">;</span>
|
||
|
||
<span class="cm">/* Pass a reference to the local instance */</span>
|
||
<span class="n">assert</span> <span class="p">(</span><span class="n">pthread_create</span> <span class="p">(</span><span class="o">&</span><span class="n">child</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">hello_thread</span><span class="p">,</span> <span class="o">&</span><span class="n">args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
|
||
|
||
<span class="cm">/* Parent thread exits, but the child may not have run yet */</span>
|
||
<span class="n">pthread_exit</span> <span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
|
||
|
||
<span class="cm">/* Future references to args are invalid! */</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
</div>
|
||
<p>If the child thread runs immediately before <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code> returns, then
|
||
everything would be fine. However, there is no guarantee that this happens.
|
||
Instead, it is just as likely that <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code> returns and the parent
|
||
thread exits. Once that happens, all data on the parent thread’s stack
|
||
(including the <code class="docutils literal notranslate"><span class="pre">struct</span> <span class="pre">thread_args</span></code> instance) become invalid. The child thread
|
||
now has a dangling pointer to potentially corrupted data. This is another
|
||
example of a race condition that can happen with threads.</p>
|
||
</div>
|
||
<div class="section" id="returning-values-from-threads">
|
||
<h2>6.5.3. Returning Values from Threads<a class="headerlink" href="ThreadArgs.html#returning-values-from-threads" title="Permalink to this headline">¶</a></h2>
|
||
<p>There are three common ways to get return values back from a thread. All three
|
||
use techniques that are similar to those used for passing arguments.
|
||
<a class="reference external" href="ThreadArgs.html#cl6-11">Code Listing 6.11</a> shows one simple technique, which is to augment
|
||
the <code class="docutils literal notranslate"><span class="pre">struct</span></code> declaration to include space for any return values.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-11"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16
|
||
17
|
||
18
|
||
19
|
||
20
|
||
21
|
||
22
|
||
23</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.11:</span>
|
||
<span class="cm"> Allocating space for a return value as part of the struct passed</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* Approach 1: Include space for return values in the struct */</span>
|
||
|
||
<span class="cm">/* Thread argument struct declaration */</span>
|
||
<span class="k">struct</span> <span class="n">numbers</span> <span class="p">{</span>
|
||
<span class="kt">int</span> <span class="n">a</span><span class="p">;</span>
|
||
<span class="kt">int</span> <span class="n">b</span><span class="p">;</span>
|
||
<span class="kt">int</span> <span class="n">sum</span><span class="p">;</span>
|
||
<span class="p">};</span>
|
||
|
||
<span class="kt">void</span> <span class="o">*</span>
|
||
<span class="nf">sum_thread</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">_args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* Cast the arguments to the usable struct type */</span>
|
||
<span class="k">struct</span> <span class="n">numbers</span> <span class="o">*</span><span class="n">args</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">numbers</span> <span class="o">*</span><span class="p">)</span> <span class="n">_args</span><span class="p">;</span>
|
||
|
||
<span class="cm">/* Place the result into the struct itself (on the heap) */</span>
|
||
<span class="n">args</span><span class="o">-></span><span class="n">sum</span> <span class="o">=</span> <span class="n">args</span><span class="o">-></span><span class="n">a</span> <span class="o">+</span> <span class="n">args</span><span class="o">-></span><span class="n">b</span><span class="p">;</span>
|
||
<span class="n">pthread_exit</span> <span class="p">(</span><span class="nb">NULL</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>The child thread receives a pointer to the <code class="docutils literal notranslate"><span class="pre">struct</span></code> instance, using the input
|
||
parameters as needed. In this case, the values of <code class="docutils literal notranslate"><span class="pre">a</span></code> and <code class="docutils literal notranslate"><span class="pre">b</span></code> are added, and
|
||
the resulting sum is copied back into the <code class="docutils literal notranslate"><span class="pre">struct</span></code>. As shown in <a class="reference external" href="ThreadArgs.html#cl6-12">Code Listing
|
||
6.12</a>, the main thread uses <code class="docutils literal notranslate"><span class="pre">pthread_join()</span></code> to wait until the
|
||
child thread exits. Once the child finishes, the main thread can retrieve all
|
||
three values (<code class="docutils literal notranslate"><span class="pre">a</span></code>, <code class="docutils literal notranslate"><span class="pre">b</span></code>, and <code class="docutils literal notranslate"><span class="pre">sum</span></code>) from the <code class="docutils literal notranslate"><span class="pre">struct</span></code> itself.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-12"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16
|
||
17
|
||
18
|
||
19</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.12:</span>
|
||
<span class="cm"> The main thread can retrieve the return value from the struct after joining the child thread</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* Allocate and pass a heap instance of the struct type */</span>
|
||
<span class="k">struct</span> <span class="n">numbers</span> <span class="o">*</span><span class="n">args</span> <span class="o">=</span> <span class="n">calloc</span> <span class="p">(</span><span class="k">sizeof</span> <span class="p">(</span><span class="k">struct</span> <span class="n">numbers</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>
|
||
<span class="n">args</span><span class="o">-></span><span class="n">a</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
|
||
<span class="n">args</span><span class="o">-></span><span class="n">b</span> <span class="o">=</span> <span class="mi">8</span><span class="p">;</span>
|
||
|
||
<span class="n">assert</span> <span class="p">(</span><span class="n">pthread_create</span> <span class="p">(</span><span class="o">&</span><span class="n">child</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">sum_thread</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
|
||
|
||
<span class="cm">/* Wait for the thread to finish */</span>
|
||
<span class="n">pthread_join</span> <span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">);</span>
|
||
<span class="cm">/* The struct is still on the heap, so the result is accessible */</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"%d + %d = %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">args</span><span class="o">-></span><span class="n">a</span><span class="p">,</span> <span class="n">args</span><span class="o">-></span><span class="n">b</span><span class="p">,</span> <span class="n">args</span><span class="o">-></span><span class="n">sum</span><span class="p">);</span>
|
||
|
||
<span class="cm">/* Clean up the struct instance */</span>
|
||
<span class="n">free</span> <span class="p">(</span><span class="n">args</span><span class="p">);</span>
|
||
<span class="n">args</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>There are three key observations about this approach:</p>
|
||
<blockquote>
|
||
<div><ul class="simple">
|
||
<li>The main and the child threads have access to both the input and the output.
|
||
This fact means that the main thread has information about how this particular
|
||
child thread was invoked. If the main thread is keeping track of many threads,
|
||
this additional information may be helpful.</li>
|
||
<li>Responsibility for memory management resides in one location: the main thread.
|
||
If responsibility is split between the programmer maintaining the main thread
|
||
and the programmer maintaining the child thread, there is the possibility for
|
||
miscommunication leading to memory leaks (or worse, premature de-allocation).</li>
|
||
<li>The major disadvantage of this approach is that the input parameters may be
|
||
kept on the heap for much longer than needed, particularly if the child thread
|
||
runs for a significant amount of time.</li>
|
||
</ul>
|
||
</div></blockquote>
|
||
<p><a class="reference external" href="ThreadArgs.html#cl6-13">Code Listing 6.13</a> shows an alternative approach for simple scalar
|
||
return types, which is to reuse the trick of casting to and from the <code class="docutils literal notranslate"><span class="pre">void*</span></code>
|
||
type. When a thread calls <code class="docutils literal notranslate"><span class="pre">pthread_exit()</span></code>, it can specify a pointer to return
|
||
as an argument.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-13"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16
|
||
17
|
||
18
|
||
19
|
||
20
|
||
21</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.13:</span>
|
||
<span class="cm"> A second technique to return a value is to pass it as the thread’s exit code</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* Approach 2: Scalar return types as void* with pthread_exit() */</span>
|
||
|
||
<span class="cm">/* Thread argument struct contains only input parameters */</span>
|
||
<span class="k">struct</span> <span class="n">numbers</span> <span class="p">{</span>
|
||
<span class="kt">int</span> <span class="n">a</span><span class="p">;</span>
|
||
<span class="kt">int</span> <span class="n">b</span><span class="p">;</span>
|
||
<span class="p">};</span>
|
||
|
||
<span class="kt">void</span> <span class="o">*</span>
|
||
<span class="nf">sum_thread</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">_args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* Cast the argument to the usable struct */</span>
|
||
<span class="k">struct</span> <span class="n">numbers</span> <span class="o">*</span><span class="n">args</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">numbers</span> <span class="o">*</span><span class="p">)</span> <span class="n">_args</span><span class="p">;</span>
|
||
|
||
<span class="cm">/* Pass the result back by casting it to the void* */</span>
|
||
<span class="n">pthread_exit</span> <span class="p">((</span><span class="kt">void</span> <span class="o">*</span><span class="p">)</span> <span class="p">(</span><span class="n">args</span><span class="o">-></span><span class="n">a</span> <span class="o">+</span> <span class="n">args</span><span class="o">-></span><span class="n">b</span><span class="p">));</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p><a class="reference external" href="ThreadArgs.html#cl6-14">Code Listing 6.14</a> shows how the main thread calls
|
||
<code class="docutils literal notranslate"><span class="pre">pthread_join()</span></code> to retrieve the pointer. Unless the thread has been detached
|
||
(or it was created with the <code class="docutils literal notranslate"><span class="pre">PTHREAD_CREATE_DETACHED</span></code> attribute), the pointer
|
||
returned with <code class="docutils literal notranslate"><span class="pre">pthread_exit()</span></code> will remain associated with the thread until it
|
||
is joined.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-14"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 1
|
||
2
|
||
3
|
||
4
|
||
5
|
||
6
|
||
7
|
||
8
|
||
9
|
||
10
|
||
11
|
||
12
|
||
13
|
||
14
|
||
15
|
||
16
|
||
17
|
||
18</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.14:</span>
|
||
<span class="cm"> Retrieving a thread’s exit code when it is joined</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* Allocate the struct like before and pass it to the thread */</span>
|
||
<span class="k">struct</span> <span class="n">numbers</span> <span class="o">*</span><span class="n">args</span> <span class="o">=</span> <span class="n">calloc</span> <span class="p">(</span><span class="k">sizeof</span> <span class="p">(</span><span class="k">struct</span> <span class="n">numbers</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>
|
||
<span class="n">args</span><span class="o">-></span><span class="n">a</span> <span class="o">=</span> <span class="mi">5</span><span class="p">;</span>
|
||
<span class="n">args</span><span class="o">-></span><span class="n">b</span> <span class="o">=</span> <span class="mi">8</span><span class="p">;</span>
|
||
|
||
<span class="n">assert</span> <span class="p">(</span><span class="n">pthread_create</span> <span class="p">(</span><span class="o">&</span><span class="n">child</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">sum_thread</span><span class="p">,</span> <span class="n">args</span><span class="p">)</span> <span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
|
||
|
||
<span class="cm">/* Wait for thread to finish and retrieve the void* into sum */</span>
|
||
<span class="kt">void</span> <span class="o">*</span><span class="n">sum</span> <span class="o">=</span> <span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="n">pthread_join</span> <span class="p">(</span><span class="n">child</span><span class="p">,</span> <span class="o">&</span><span class="n">sum</span><span class="p">);</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"Sum: %d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="p">(</span><span class="kt">int</span><span class="p">)</span> <span class="n">sum</span><span class="p">);</span>
|
||
|
||
<span class="n">free</span> <span class="p">(</span><span class="n">args</span><span class="p">);</span>
|
||
<span class="n">args</span> <span class="o">==</span> <span class="nb">NULL</span><span class="p">;</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p><a class="reference external" href="ThreadArgs.html#cl6-15">Code Listing 6.15</a> shows a third approach to returning values from
|
||
the thread. In this style, the child thread allocates a separate <code class="docutils literal notranslate"><span class="pre">struct</span></code>
|
||
dynamically to hold the return values. This technique allows a thread to return
|
||
multiple values rather than a single scalar. For instance, consider the
|
||
following <code class="docutils literal notranslate"><span class="pre">calculator</span></code> thread. It receives two <code class="docutils literal notranslate"><span class="pre">int</span></code> values as input and
|
||
returns the results of five simple arithmetic operations.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-15"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 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</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.15:</span>
|
||
<span class="cm"> The child can dynamically allocate space for the return values</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* Approach 3: Allocate separate struct for return values */</span>
|
||
|
||
<span class="cm">/* struct for passing arguments to the child thread */</span>
|
||
<span class="k">struct</span> <span class="n">args</span> <span class="p">{</span>
|
||
<span class="kt">int</span> <span class="n">a</span><span class="p">;</span>
|
||
<span class="kt">int</span> <span class="n">b</span><span class="p">;</span>
|
||
<span class="p">};</span>
|
||
|
||
<span class="cm">/* struct for returning results from the child thread */</span>
|
||
<span class="k">struct</span> <span class="n">results</span> <span class="p">{</span>
|
||
<span class="kt">int</span> <span class="n">sum</span><span class="p">;</span>
|
||
<span class="kt">int</span> <span class="n">difference</span><span class="p">;</span>
|
||
<span class="kt">int</span> <span class="n">product</span><span class="p">;</span>
|
||
<span class="kt">int</span> <span class="n">quotient</span><span class="p">;</span>
|
||
<span class="kt">int</span> <span class="n">modulus</span><span class="p">;</span>
|
||
<span class="p">};</span>
|
||
|
||
<span class="kt">void</span> <span class="o">*</span>
|
||
<span class="nf">calculator</span> <span class="p">(</span><span class="kt">void</span> <span class="o">*</span><span class="n">_args</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* Cast the args to the usable struct type */</span>
|
||
<span class="k">struct</span> <span class="n">args</span> <span class="o">*</span><span class="n">args</span> <span class="o">=</span> <span class="p">(</span><span class="k">struct</span> <span class="n">args</span> <span class="o">*</span><span class="p">)</span> <span class="n">_args</span><span class="p">;</span>
|
||
|
||
<span class="cm">/* Allocate heap space for this thread's results */</span>
|
||
<span class="k">struct</span> <span class="n">results</span> <span class="o">*</span><span class="n">results</span> <span class="o">=</span> <span class="n">calloc</span> <span class="p">(</span><span class="k">sizeof</span> <span class="p">(</span><span class="k">struct</span> <span class="n">results</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>
|
||
<span class="n">results</span><span class="o">-></span><span class="n">sum</span> <span class="o">=</span> <span class="n">args</span><span class="o">-></span><span class="n">a</span> <span class="o">+</span> <span class="n">args</span><span class="o">-></span><span class="n">b</span><span class="p">;</span>
|
||
<span class="n">results</span><span class="o">-></span><span class="n">difference</span> <span class="o">=</span> <span class="n">args</span><span class="o">-></span><span class="n">a</span> <span class="o">-</span> <span class="n">args</span><span class="o">-></span><span class="n">b</span><span class="p">;</span>
|
||
<span class="n">results</span><span class="o">-></span><span class="n">product</span> <span class="o">=</span> <span class="n">args</span><span class="o">-></span><span class="n">a</span> <span class="o">*</span> <span class="n">args</span><span class="o">-></span><span class="n">b</span><span class="p">;</span>
|
||
<span class="n">results</span><span class="o">-></span><span class="n">quotient</span> <span class="o">=</span> <span class="n">args</span><span class="o">-></span><span class="n">a</span> <span class="o">/</span> <span class="n">args</span><span class="o">-></span><span class="n">b</span><span class="p">;</span>
|
||
<span class="n">results</span><span class="o">-></span><span class="n">modulus</span> <span class="o">=</span> <span class="n">args</span><span class="o">-></span><span class="n">a</span> <span class="o">%</span> <span class="n">args</span><span class="o">-></span><span class="n">b</span><span class="p">;</span>
|
||
<span class="cm">/* De-allocate the input instance and return the pointer to</span>
|
||
<span class="cm"> results on heap */</span>
|
||
<span class="n">free</span> <span class="p">(</span><span class="n">args</span><span class="p">);</span>
|
||
<span class="n">pthread_exit</span> <span class="p">(</span><span class="n">results</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>It is critical to note that the struct instance here must be allocated
|
||
dynamically. Once the thread calls <code class="docutils literal notranslate"><span class="pre">pthread_exit()</span></code>, everything on its stack
|
||
becomes invalid. A thread should never pass a pointer to a local variable with
|
||
<code class="docutils literal notranslate"><span class="pre">pthread_exit()</span></code>.</p>
|
||
<p>Retrieving the returned data can be accomplished with <code class="docutils literal notranslate"><span class="pre">pthread_join()</span></code>. In the
|
||
following example, the main thread creates five separate instances of the
|
||
<code class="docutils literal notranslate"><span class="pre">calculator</span></code> thread. Each of these child threads gets a pointer to a unique
|
||
<code class="docutils literal notranslate"><span class="pre">struct</span> <span class="pre">args</span></code> instance with the corresponding parameters. Each child then
|
||
allocates its own <code class="docutils literal notranslate"><span class="pre">struct</span> <span class="pre">results</span></code> instance on the heap. This allows the data
|
||
to persist after the thread has finished. In <a class="reference external" href="ThreadArgs.html#cl6-14">Code Listing 6.14</a>, the
|
||
main thread gets each thread’s pointer one at a time, with a separate call to
|
||
<code class="docutils literal notranslate"><span class="pre">pthread_join()</span></code>. Since the child thread has already finished at this point,
|
||
the main thread must bear the responsibility for calling <code class="docutils literal notranslate"><span class="pre">free()</span></code> to
|
||
de-allocate the <code class="docutils literal notranslate"><span class="pre">struct</span></code> results instance.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl6-16"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0"> 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</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 6.16:</span>
|
||
<span class="cm"> The main thread passes arguments to the child threads and frees the results</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="cm">/* Create 5 threads, each calling calculator() */</span>
|
||
<span class="n">pthread_t</span> <span class="n">child</span><span class="p">[</span><span class="mi">5</span><span class="p">];</span>
|
||
|
||
<span class="cm">/* Allocate arguments and create the threads */</span>
|
||
<span class="k">struct</span> <span class="n">args</span> <span class="o">*</span><span class="n">args</span><span class="p">[</span><span class="mi">5</span><span class="p">]</span> <span class="o">=</span> <span class="p">{</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span><span class="p">,</span> <span class="nb">NULL</span> <span class="p">};</span>
|
||
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">5</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* args[i] is a pointer to the arguments for thread i */</span>
|
||
<span class="n">args</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="o">=</span> <span class="n">calloc</span> <span class="p">(</span><span class="k">sizeof</span> <span class="p">(</span><span class="k">struct</span> <span class="n">args</span><span class="p">),</span> <span class="mi">1</span><span class="p">);</span>
|
||
|
||
<span class="cm">/* thread 0 calls calculator(1,1)</span>
|
||
<span class="cm"> thread 1 calls calculator(2,4)</span>
|
||
<span class="cm"> thread 2 calls calculator(3,9)</span>
|
||
<span class="cm"> and so on... */</span>
|
||
<span class="n">args</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">a</span> <span class="o">=</span> <span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">;</span>
|
||
<span class="n">args</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">b</span> <span class="o">=</span> <span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">i</span> <span class="o">+</span> <span class="mi">1</span><span class="p">);</span>
|
||
<span class="n">assert</span> <span class="p">(</span><span class="n">pthread_create</span> <span class="p">(</span><span class="o">&</span><span class="n">child</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="nb">NULL</span><span class="p">,</span> <span class="n">calculator</span><span class="p">,</span> <span class="n">args</span><span class="p">[</span><span class="n">i</span><span class="p">])</span>
|
||
<span class="o">==</span> <span class="mi">0</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="cm">/* Allocate an array of pointers to result structs */</span>
|
||
<span class="k">struct</span> <span class="n">results</span> <span class="o">*</span><span class="n">results</span><span class="p">[</span><span class="mi">5</span><span class="p">];</span>
|
||
<span class="k">for</span> <span class="p">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span> <span class="n">i</span> <span class="o"><</span> <span class="mi">5</span><span class="p">;</span> <span class="n">i</span><span class="o">++</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* Passing results[i] by reference creates (void **) */</span>
|
||
<span class="n">pthread_join</span> <span class="p">(</span><span class="n">child</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="p">(</span><span class="kt">void</span> <span class="o">**</span><span class="p">)</span><span class="o">&</span><span class="n">results</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
|
||
|
||
<span class="cm">/* Print each of the results and free the struct */</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"Calculator (%d, %2d) ==> "</span><span class="p">,</span> <span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">,</span> <span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">)</span> <span class="o">*</span> <span class="p">(</span><span class="n">i</span><span class="o">+</span><span class="mi">1</span><span class="p">));</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"+:%3d; "</span><span class="p">,</span> <span class="n">results</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">sum</span><span class="p">);</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"-:%3d; "</span><span class="p">,</span> <span class="n">results</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">difference</span><span class="p">);</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"*:%3d; "</span><span class="p">,</span> <span class="n">results</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">product</span><span class="p">);</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"/:%3d; "</span><span class="p">,</span> <span class="n">results</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">quotient</span><span class="p">);</span>
|
||
<span class="n">printf</span> <span class="p">(</span><span class="s">"%%:%3d</span><span class="se">\n</span><span class="s">"</span><span class="p">,</span> <span class="n">results</span><span class="p">[</span><span class="n">i</span><span class="p">]</span><span class="o">-></span><span class="n">modulus</span><span class="p">);</span>
|
||
<span class="n">free</span> <span class="p">(</span><span class="n">results</span><span class="p">[</span><span class="n">i</span><span class="p">]);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<div class="topic border border-dark rounded-lg alert-danger px-2 mb-3">
|
||
<div class="figure align-left">
|
||
<a class="reference internal image-reference" href="_images/CSF-Images-BugWarning.png"><img alt="Decorative bug warning" src="_images/CSF-Images-BugWarning.png" style="width: 90%;" /></a>
|
||
</div>
|
||
<p class="topic-title first pt-2 mb-1">Bug Warning</p><hr class="mt-1" />
|
||
<p>All of the functions for creating threads, passing arguments, and getting return
|
||
values involve a lot of pointers. Furthermore, the pointers are dereferenced
|
||
and manipulated asynchronously because of the nature of multithreading. It is
|
||
vital to remember the types and lifetimes of each pointer and the corresponding data structure.</p>
|
||
<blockquote>
|
||
<div><ul class="simple">
|
||
<li>The first parameter for <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code> is a <code class="docutils literal notranslate"><span class="pre">pthread_t*</span></code>. The argument
|
||
should typically be an existing <code class="docutils literal notranslate"><span class="pre">pthread_t</span></code> passed by reference with the <code class="docutils literal notranslate"><span class="pre">&</span></code> operator.</li>
|
||
<li>The final parameter to <code class="docutils literal notranslate"><span class="pre">pthread_create()</span></code> must either be a scalar (cast as a
|
||
pointer) or a pointer to data that persists until the child thread runs. That
|
||
is, the target of the pointer must not be modified by the main thread until the
|
||
child thread has been joined (to guarantee the child has run).</li>
|
||
<li>The parameter to <code class="docutils literal notranslate"><span class="pre">pthread_exit()</span></code> must be a scalar value (cast as a pointer)
|
||
or a pointer to non-stack data. The data must be guaranteed to be valid even
|
||
after the thread has been completely destroyed.</li>
|
||
<li>The final parameter to <code class="docutils literal notranslate"><span class="pre">pthread_join()</span></code> must be a pointer that is passed by
|
||
reference. That is, <code class="docutils literal notranslate"><span class="pre">pthread_join()</span></code> will change this pointer to point to the
|
||
returned data structure.</li>
|
||
</ul>
|
||
</div></blockquote>
|
||
</div>
|
||
<div
|
||
id="ThreadArgsSumm"
|
||
class="embedContainer"
|
||
data-exer-name="ThreadArgsSumm"
|
||
data-long-name="Thread argument questions"
|
||
data-short-name="ThreadArgsSumm"
|
||
data-frame-src="../../../Exercises/Threads/ThreadArgsSumm.html?selfLoggingEnabled=false&localMode=true&module=ThreadArgs&JXOP-debug=true&JOP-lang=en&JXOP-code=java"
|
||
data-frame-width="950"
|
||
data-frame-height="550"
|
||
data-external="false"
|
||
data-points="1.0"
|
||
data-required="True"
|
||
data-showhide="show"
|
||
data-threshold="3"
|
||
data-type="ka"
|
||
data-exer-id="">
|
||
|
||
<div class="center">
|
||
<div id="ThreadArgsSumm_iframe"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<div class="container">
|
||
|
||
<div class="mt-4 container center">
|
||
«  <a id="prevmod1" href="POSIXThreads.html">6.4. POSIX Thread Library</a>
|
||
  ::  
|
||
<a class="uplink" href="index.html">Contents</a>
|
||
  ::  
|
||
<a id="nextmod1" href="ImplicitThreads.html">6.6. Implicit Threading and Language-based Threads</a>  »
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
<br />
|
||
|
||
<div class="row jmu-dark-purple-bg">
|
||
<div class="col-md-12">
|
||
<center>
|
||
<a id="contact_us" class="btn button-link-no-blue jmu-gold" rel="nofollow" href="mailto:webmaster@opencsf.org" role="button">Contact Us</a>
|
||
<a id="license" class="btn button-link-no-blue jmu-gold" rel="nofollow" href="https://w3.cs.jmu.edu/kirkpams/OpenCSF/lib/license.html" target="_blank">License</a>
|
||
</center>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
|
||
|
||
|
||
|
||
<script src="_static/js/popper.js-1.14.7-min.js" integrity="sha384-UO2eT0CpHqdSJQ6hJty5KVphtPhzWj9WO1clHTMGa3JDZwrnQq4sF86dIHNDz0W1" crossorigin="anonymous"></script>
|
||
<script src="_static/js/bootstrap.min.js" integrity="sha384-JjSmVgyd0p3pXB1rRibZUAYoIIy6OrQ6VrjIEaFf/nJGzIxFDsf4x0xIM+B07jRM" crossorigin="anonymous"></script>
|
||
</body>
|
||
</html> |