737 lines
No EOL
53 KiB
HTML
737 lines
No EOL
53 KiB
HTML
|
||
<!DOCTYPE html>
|
||
|
||
|
||
|
||
|
||
<html lang="en">
|
||
<head>
|
||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||
|
||
<title>8.3. Producer-Consumer Problem — 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="4. Readers-Writers Problem" href="ReadWrite.html" />
|
||
<link rel="prev" title="2. Basic Synchronization Design Patterns" href="SynchDesign.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="ProdCons.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="ProdCons.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/ProdCons.rst"
|
||
target="_blank" rel="nofollow">Show Source</a></li>
|
||
|
||
</ul>
|
||
</nav>
|
||
|
||
|
||
<div class="container center">
|
||
«  <a id="prevmod" href="SynchDesign.html">8.2. Basic Synchronization Design Patterns</a>
|
||
  ::  
|
||
<a class="uplink" href="index.html">Contents</a>
|
||
  ::  
|
||
<a id="nextmod" href="ReadWrite.html">8.4. Readers-Writers Problem</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 = "ProdCons";ODSA.SETTINGS.MODULE_LONG_NAME = "Producer-Consumer Problem";ODSA.SETTINGS.MODULE_CHAPTER = "Synchronization Patterns and Problems"; ODSA.SETTINGS.BUILD_DATE = "2021-06-01 15:31:51"; ODSA.SETTINGS.BUILD_CMAP = false;JSAV_OPTIONS['lang']='en';JSAV_EXERCISE_OPTIONS['code']='java';</script><div class="section" id="producer-consumer-problem">
|
||
<h1>8.3. Producer-Consumer Problem<a class="headerlink" href="ProdCons.html#producer-consumer-problem" title="Permalink to this headline">¶</a></h1>
|
||
<p>One of the most common task structures in concurrent systems is illustrated by
|
||
the <a class="reference internal" href="Glossary.html#term-producer-consumer-problem"><span class="xref std std-term">producer-consumer problem</span></a>. In this problem, threads or processes are
|
||
divided into two relative types: a producer thread is responsible for performing
|
||
an initial task that ends with creating some result and a consumer thread that
|
||
takes that initial result for some later task. Between the threads, there is a
|
||
shared array or queue that stores the results being passed. One key feature of
|
||
this problem is that the consumer removes the data from the queue and <em>consumes</em>
|
||
it by using it in some later purpose. There is no way for the consumer thread or
|
||
threads to repeatedly access data in the queue.</p>
|
||
<p>As an example, consider a researcher who discovers a previously unknown play and
|
||
they believe it may have been written by a famous author. As part of their work,
|
||
this researcher wants to know if the newly discovered text uses common words
|
||
with the same frequency that author used in other works. This work could be done
|
||
with a pipe-and-filter application that reads in the content of the texts,
|
||
builds a search index of all words based on their frequency, performs some
|
||
computation that compares the search indexes of all of the works. One thread is
|
||
assigned the tasks of reading in the contents and <em>producing</em> the list of words,
|
||
placing a word at a time in a queue. A second thread <em>consumes</em> the words by
|
||
removing them from the queue and adding them to the data used to build the
|
||
search index. This thread then becomes a producer because it provides the index
|
||
to yet another thread that will be performing the index comparisons.</p>
|
||
<div class="section" id="producer-consumer-with-unbounded-queue">
|
||
<h2>8.3.1. Producer-Consumer with Unbounded Queue<a class="headerlink" href="ProdCons.html#producer-consumer-with-unbounded-queue" title="Permalink to this headline">¶</a></h2>
|
||
<p>As a first variant on this problem, consider two threads that share an unbounded
|
||
queue. This approach can be implemented using a linked list approach for the
|
||
queue. Specifically, we can assume that there is a <code class="docutils literal notranslate"><span class="pre">queue_t</span></code> structure that
|
||
contains pointers to the front and back of the queue, where the nodes in the
|
||
queue are of type <code class="docutils literal notranslate"><span class="pre">queue_node_t</span></code>. The nodes all contain pointers to some sort
|
||
of <code class="docutils literal notranslate"><span class="pre">data_t</span></code> field. <a class="reference external" href="ProdCons.html#cl8-10">Code Listing 8.10</a> shows the framework for
|
||
enqueueing and dequeueing data.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl8-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
|
||
17
|
||
18
|
||
19
|
||
20
|
||
21
|
||
22
|
||
23
|
||
24
|
||
25
|
||
26</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 8.10:</span>
|
||
<span class="cm"> Enqueue and dequeue operations for a linked list implementation of a queue</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="kt">void</span>
|
||
<span class="nf">enqueue_unsafe</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">,</span> <span class="n">data_t</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* Create a new node and make it the new back of the queue */</span>
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">back</span><span class="o">-></span><span class="n">next</span> <span class="o">=</span> <span class="n">calloc</span> <span class="p">(</span><span class="mi">1</span><span class="p">,</span> <span class="k">sizeof</span> <span class="p">(</span><span class="n">queue_node_t</span><span class="p">));</span>
|
||
<span class="n">assert</span> <span class="p">(</span><span class="n">queue</span><span class="o">-></span><span class="n">back</span><span class="o">-></span><span class="n">next</span> <span class="o">!=</span> <span class="n">null</span><span class="p">);</span>
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">back</span> <span class="o">=</span> <span class="n">queue</span><span class="o">-></span><span class="n">back</span><span class="o">-></span><span class="n">next</span><span class="p">;</span>
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">back</span><span class="o">-></span><span class="n">data</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="n">data_t</span> <span class="o">*</span>
|
||
<span class="nf">dequeue_unsafe</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* If back = front, then the queue is empty */</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">queue</span><span class="o">-></span><span class="n">back</span> <span class="o">==</span> <span class="n">queue</span><span class="o">-></span><span class="n">front</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="n">data_t</span> <span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">queue</span><span class="o">-></span><span class="n">front</span><span class="o">-></span><span class="n">data</span><span class="p">;</span>
|
||
<span class="n">queue_node_t</span> <span class="o">*</span> <span class="n">next</span> <span class="o">=</span> <span class="n">queue</span><span class="o">-></span><span class="n">front</span><span class="o">-></span><span class="n">next</span><span class="p">;</span>
|
||
<span class="n">free</span> <span class="p">(</span><span class="n">queue</span><span class="o">-></span><span class="n">front</span><span class="p">);</span>
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">front</span> <span class="o">=</span> <span class="n">next</span><span class="p">;</span>
|
||
<span class="k">return</span> <span class="n">data</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>This implementation, which would be acceptable for a single-threaded
|
||
application, has a race condition when used in a concurrent setting.
|
||
Specifically, if one thread begins to <code class="docutils literal notranslate"><span class="pre">enqueue()</span></code> some data, another thread
|
||
that tries to <code class="docutils literal notranslate"><span class="pre">dequeue()</span></code> the data at the same time may get a <code class="docutils literal notranslate"><span class="pre">NULL</span></code> pointer
|
||
because the first thread has not yet reached the line the advances the back
|
||
pointer.</p>
|
||
<p>The solution here would be to refactor these functions to become a monitor as
|
||
shown in <a class="reference external" href="ProdCons.html#cl8-11">Code Listing 8.11</a>. Each function would be passed a
|
||
reference to a shared mutex that would be locked on entry and released just
|
||
before the function returns. This solution eliminates the race condition
|
||
regarding the timing of access to the queue’s <code class="docutils literal notranslate"><span class="pre">back</span></code> field. Additionally, this
|
||
solution is generalizable regardless of the number of producers and consumers.
|
||
If there are multiple producers trying to <code class="docutils literal notranslate"><span class="pre">enqueue()</span></code> data, the mutex ensures
|
||
that they will not try to manipulate the queue at that same time. For brevity,
|
||
<a class="reference external" href="ProdCons.html#cl8-11">Code Listing 8.11</a> calls the functions from <a class="reference external" href="ProdCons.html#cl8-10">Code Listing 8.10</a>.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-2 mb-3 notranslate"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 8.11:</span>
|
||
<span class="cm"> A synchronized version of linked list enqueueing and dequeueing</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="kt">void</span>
|
||
<span class="nf">enqueue</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">,</span> <span class="n">data_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="n">pthread_mutex_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="n">pthread_mutex_lock</span> <span class="p">(</span><span class="n">lock</span><span class="p">);</span>
|
||
<span class="n">enqueue_unsafe</span> <span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
|
||
<span class="n">pthread_mutex_unlock</span> <span class="p">(</span><span class="n">lock</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="n">data_t</span> <span class="o">*</span>
|
||
<span class="nf">dequeue</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">,</span> <span class="n">pthread_mutex_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="n">pthread_mutex_lock</span> <span class="p">(</span><span class="n">lock</span><span class="p">);</span>
|
||
<span class="n">data_t</span> <span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">dequeue_unsafe</span> <span class="p">(</span><span class="n">queue</span><span class="p">);</span>
|
||
<span class="n">pthread_mutex_unlock</span> <span class="p">(</span><span class="n">lock</span><span class="p">);</span>
|
||
<span class="k">return</span> <span class="n">data</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</div>
|
||
</div>
|
||
<div class="section" id="single-producer-single-consumer-solution-using-a-bounded-queue">
|
||
<h2>8.3.2. Single Producer-Single Consumer Solution Using a Bounded Queue<a class="headerlink" href="ProdCons.html#single-producer-single-consumer-solution-using-a-bounded-queue" title="Permalink to this headline">¶</a></h2>
|
||
<p>In many cases, the unbounded queue of the previous section is neither feasible
|
||
nor desirable. For instance, this approach makes it very easy to launch a
|
||
<a class="reference internal" href="Glossary.html#term-denial-of-service"><span class="xref std std-term">denial-of-service</span></a> attack against the application, particularly if this
|
||
code is used in a server. <a class="reference external" href="ProdCons.html#cl8-12">Code Listing 8.12</a> shows a single line of
|
||
code that quickly exhausts the program’s dynamic memory resources, leading to a
|
||
system crash.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl8-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</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 8.12:</span>
|
||
<span class="cm"> A trivial denial-of-service attack against the code in 8.11</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="k">while</span> <span class="p">(</span><span class="mi">1</span><span class="p">)</span>
|
||
<span class="n">enqueue</span> <span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">data</span><span class="p">,</span> <span class="n">lock</span><span class="p">);</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>Given this weakness, systems software typically imposes constraints on the
|
||
number of items in the queue. One approach would be to use semaphores with the
|
||
linked list queue above. Another approach is to use a finite circular array as
|
||
the basis. For this approach, we will still assume the use of a <code class="docutils literal notranslate"><span class="pre">queue_t</span></code> data
|
||
type to represent the queue, however the internal implementation uses an array
|
||
instead of linked nodes. <a href="ProdCons.html#circarray">Figure 8.3.1</a> illustrates the
|
||
structure of the queue.</p>
|
||
<div class="figure mb-2 align-right" id="id6" style="width: 40%">
|
||
<span id="circarray"></span><a class="reference internal image-reference" href="_images/CSF-Images.8.1.png"><img class="p-3 mb-2 align-center border border-dark rounded-lg" alt="A circular queue using an array" src="_images/CSF-Images.8.1.png" style="width: 90%;" /></a>
|
||
<p class="caption align-center px-3"><span class="caption-text"> Figure 8.3.1: A circular queue using an array</span></p>
|
||
</div>
|
||
<p><a class="reference external" href="ProdCons.html#cl8-13">Code Listing 8.13</a> shows the unsafe version of the <code class="docutils literal notranslate"><span class="pre">enqueue()</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">dequeue()</span></code> operations for an array implementation of a queue. Within the
|
||
<code class="docutils literal notranslate"><span class="pre">queue_t</span></code> data type, contents is an array of pointers to the enqueued data,
|
||
while <code class="docutils literal notranslate"><span class="pre">front</span></code> and <code class="docutils literal notranslate"><span class="pre">back</span></code> are indexes into this array. When either of the
|
||
indexes are incremented, the operations must recalculate the value modulo the
|
||
array size to ensure that the values stay within the bounds of the array. This
|
||
approach creates a circular structure, where the spaces in the array can be
|
||
reused. Once the back index reaches the end of the array, it would return to the
|
||
first position and continue from there.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl8-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</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 8.13:</span>
|
||
<span class="cm"> Enqueue and dequeue operations for an array queue implementation</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="kt">void</span>
|
||
<span class="nf">enqueue_unsafe</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">,</span> <span class="n">data_t</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="cm">/* Store the data in the array and advance the index */</span>
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">contents</span><span class="p">[</span><span class="n">queue</span><span class="o">-></span><span class="n">back</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span>
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">back</span> <span class="o">%=</span> <span class="n">queue</span><span class="o">-></span><span class="n">capacity</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="n">data_t</span> <span class="o">*</span>
|
||
<span class="nf">dequeue_unsafe</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="n">data_t</span> <span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">queue</span><span class="o">-></span><span class="n">contents</span><span class="p">[</span><span class="n">queue</span><span class="o">-></span><span class="n">front</span><span class="o">++</span><span class="p">];</span>
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">front</span> <span class="o">%=</span> <span class="n">queue</span><span class="o">-></span><span class="n">capacity</span><span class="p">;</span>
|
||
<span class="k">return</span> <span class="n">data</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>The implementation in <a class="reference external" href="ProdCons.html#cl8-13">Code Listing 8.13</a> has a fundamental flaw in
|
||
both operations, as neither enforces the limited capacity. This design choice
|
||
was intentional, as this code was meant to encapsulate the enqueueing and
|
||
dequeueing behavior. That is, we can now reason through the synchronization
|
||
issues for the producer-consumer problem without regard for the specific queue
|
||
implementation.</p>
|
||
<p>To build the rationale for the solution to the producer-consumer problem,
|
||
consider <a class="reference external" href="ProdCons.html#cl8-14">Code Listing 8.14</a>. This implementation attempts to keep
|
||
track of the number of items (<code class="docutils literal notranslate"><span class="pre">queue->size</span></code>) in the queue, comparing it with
|
||
values that indicate whether the queue is full or empty.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl8-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
|
||
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 8.14:</span>
|
||
<span class="cm"> An unsuccessful attempt at solving producer-consumer</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="kt">void</span>
|
||
<span class="nf">enqueue_failure</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">,</span> <span class="n">data_t</span> <span class="o">*</span><span class="n">data</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">queue</span><span class="o">-></span><span class="n">counter</span> <span class="o">==</span> <span class="n">queue</span><span class="o">-></span><span class="n">capacity</span><span class="p">)</span> <span class="k">return</span><span class="p">;</span>
|
||
<span class="n">enqueue_unsafe</span> <span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">counter</span><span class="o">++</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="n">data_t</span> <span class="o">*</span>
|
||
<span class="nf">dequeue_failure</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span> <span class="n">queue</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="k">if</span> <span class="p">(</span><span class="n">queue</span><span class="o">-></span><span class="n">counter</span> <span class="o">==</span> <span class="mi">0</span><span class="p">)</span>
|
||
<span class="k">return</span> <span class="nb">NULL</span><span class="p">;</span>
|
||
<span class="n">data_t</span> <span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">dequeue_unsafe</span> <span class="p">(</span><span class="n">queue</span><span class="p">);</span>
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">counter</span><span class="o">--</span><span class="p">;</span>
|
||
<span class="k">return</span> <span class="n">data</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>The attempted solution in <a class="reference external" href="ProdCons.html#cl8-14">Code Listing 8.14</a> fails because it has
|
||
race conditions on the queue’s <code class="docutils literal notranslate"><span class="pre">counter</span></code> variable. If the producer is
|
||
attempting to enqueue an item at the same moment the consumer is dequeueing one,
|
||
the outcome depends on whether the producer’s check for available space happens
|
||
before or after the consumer decrements the <code class="docutils literal notranslate"><span class="pre">counter</span></code>. A similar race
|
||
condition arises if the queue is empty and both functions are called. This race
|
||
condition could be fixed by wrapping the accesses to the <code class="docutils literal notranslate"><span class="pre">counter</span></code> with a
|
||
mutex.</p>
|
||
<p><a class="reference external" href="ProdCons.html#cl8-14">Code Listing 8.14</a> has another flaw from the producer’s perspective:
|
||
<strong>there is no indication that there was a failure to enqueue the item</strong>.
|
||
Addressing this failure would require changing the function’s interface to
|
||
return a status to indicate success or failure. This approach, however, imposes
|
||
an undesirable burden on the user. That is, the programmer who is building the
|
||
producer and consumer threads or processes must build in a fail-safe mechanism
|
||
to respond accordingly if the <code class="docutils literal notranslate"><span class="pre">enqueue()</span></code> or <code class="docutils literal notranslate"><span class="pre">dequeue()</span></code> operations fail.</p>
|
||
<p>A better, more user-friendly solution to the producer-consumer problem is to use
|
||
semaphores. Note that semaphores incorporate the key functionality that we need:
|
||
<strong>atomic incrementing and decrementing of a counter variable</strong>. The previous
|
||
approach was trying to re-invent this already-solved problem. <a class="reference external" href="ProdCons.html#cl8-15">Code Listing 8.15</a> shows the framework for solving the producer-consumer problem for a
|
||
single producer and single consumer.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl8-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</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 8.15:</span>
|
||
<span class="cm"> Solution for a single producer and single consumer</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="kt">void</span>
|
||
<span class="nf">enqueue</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">,</span> <span class="n">data_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">space</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">items</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="n">sem_wait</span> <span class="p">(</span><span class="n">space</span><span class="p">);</span>
|
||
<span class="n">enqueue_unsafe</span> <span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
|
||
<span class="n">sem_post</span> <span class="p">(</span><span class="n">items</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="n">data_t</span> <span class="o">*</span>
|
||
<span class="nf">dequeue</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span> <span class="n">queue</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">space</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">items</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="n">sem_wait</span> <span class="p">(</span><span class="n">items</span><span class="p">);</span>
|
||
<span class="n">data_t</span> <span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">dequeue_unsafe</span> <span class="p">(</span><span class="n">queue</span><span class="p">);</span>
|
||
<span class="n">sem_post</span> <span class="p">(</span><span class="n">space</span><span class="p">);</span>
|
||
<span class="k">return</span> <span class="n">data</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>The structure of this approach is to use signaling with two semaphores. The key
|
||
insight here is that there are actually <em>two</em> bounds that need enforced: a
|
||
maximum and a minimum. If the queue is full, then the producer needs to wait
|
||
until there is a space available. That is, the producer must call
|
||
<code class="docutils literal notranslate"><span class="pre">sem_wait(space)</span></code> and wait if necessary. The <code class="docutils literal notranslate"><span class="pre">space</span></code> semaphore is
|
||
initialized to the capacity of the queue, so it would only be 0 if that many
|
||
items are already in the queue. Once the consumer has removed an item from the
|
||
queue, it performs the counterpart call <code class="docutils literal notranslate"><span class="pre">sem_post(space)</span></code> to alert the
|
||
producer that a space is available. In short, the <code class="docutils literal notranslate"><span class="pre">space</span></code> semaphore enforces
|
||
the maximum capacity of the queue.</p>
|
||
<p>At the same time, the consumer must not attempt to remove an item from the queue
|
||
if none have been enqueued. Depending on the internal state of the queue, this
|
||
attempt to dequeue an item might return old data that has previously be removed
|
||
or an unexpected <code class="docutils literal notranslate"><span class="pre">NULL</span></code> pointer. The <code class="docutils literal notranslate"><span class="pre">items</span></code> semaphore, which is initialized
|
||
to 0 and represents the number of enqueued items, enforces this constraint. If
|
||
the queue is empty, this semaphore would have an internal value of 0, causing
|
||
the consumer to block when it calls <code class="docutils literal notranslate"><span class="pre">sem_wait(items)</span></code>. Once the producer puts
|
||
an item into the queue, it would perform the corresponding <code class="docutils literal notranslate"><span class="pre">sem_post(items)</span></code>
|
||
that would increment the semaphore and unblock the consumer.</p>
|
||
</div>
|
||
<div class="section" id="multiple-producers-solution-using-a-bounded-queue">
|
||
<h2>8.3.3. Multiple Producers Solution Using a Bounded Queue<a class="headerlink" href="ProdCons.html#multiple-producers-solution-using-a-bounded-queue" title="Permalink to this headline">¶</a></h2>
|
||
<p>The solution in the previous section works successfully if there is only a
|
||
single producer. However, if there are multiple producers, the previous solution
|
||
will not work and should not be used. The problem is the line highlighted in
|
||
<a class="reference external" href="ProdCons.html#cl8-16">Code Listing 8.16</a>. Recall that post-increments in C are not atomic
|
||
operations. That is, the internal execution at the machine language level
|
||
involves a three-step process: load the value into a register, increment the
|
||
value in the register, and store the result back into memory. If two threads try
|
||
to perform this increment at the same time, the increments could interfere with
|
||
each other.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl8-16"><table class="highlighttable"><tr><td class="linenos px-0 mx-0"><div class="linenodiv"><pre class="mb-0">1
|
||
2
|
||
3
|
||
4
|
||
5</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 8.16:</span>
|
||
<span class="cm"> Non-atomic increments are always race conditions</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="n">queue</span><span class="o">-></span><span class="n">contents</span><span class="p">[</span><span class="n">queue</span><span class="o">-></span><span class="n">back</span><span class="o">++</span><span class="p">]</span> <span class="o">=</span> <span class="n">data</span><span class="p">;</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>The solution in this case would be to add a lock as shown in <a class="reference external" href="ProdCons.html#cl8-17">Code Listing 8.17</a>. Acquiring and releasing the lock as shown here minimizes the size
|
||
of the critical section. That is, the operations on the semaphores do not
|
||
require protection. Including the calls to <code class="docutils literal notranslate"><span class="pre">sem_wait()</span></code> and <code class="docutils literal notranslate"><span class="pre">sem_post()</span></code>
|
||
within the critical section for the lock would unnecessarily prevent the other
|
||
producers from manipulating the semaphores, partially eliminating the benefit of
|
||
using a semaphore.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl8-17"><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</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 8.17:</span>
|
||
<span class="cm"> Solution for a single producer and single consumer</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="kt">void</span>
|
||
<span class="nf">enqueue</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">,</span> <span class="n">data_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">space</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">items</span><span class="p">,</span>
|
||
<span class="n">pthread_mutex_t</span> <span class="o">*</span><span class="n">lock</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="n">sem_wait</span> <span class="p">(</span><span class="n">space</span><span class="p">);</span>
|
||
<span class="n">pthread_mutex_lock</span> <span class="p">(</span><span class="n">lock</span><span class="p">);</span>
|
||
<span class="n">enqueue_unsafe</span> <span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
|
||
<span class="n">pthread_mutex_unlock</span> <span class="p">(</span><span class="n">lock</span><span class="p">);</span>
|
||
<span class="n">sem_post</span> <span class="p">(</span><span class="n">items</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<p>Note that this approach does not require any modification to the <code class="docutils literal notranslate"><span class="pre">dequeue()</span></code>
|
||
operation used by the consumer. Since there is only a single consumer, there is
|
||
no race condition on incrementing the <code class="docutils literal notranslate"><span class="pre">queue->front</span></code> variable. This solution
|
||
could be extended to multiple consumers by introducing a second lock for
|
||
consumers used in <code class="docutils literal notranslate"><span class="pre">dequeue()</span></code> as shown in <a class="reference external" href="ProdCons.html#cl8-18">Code Listing 8.18</a>. It
|
||
is important, in this case, to use separate locks rather than simply reusing the
|
||
same lock for both producers and consumers. When both the <code class="docutils literal notranslate"><span class="pre">space</span></code> and
|
||
<code class="docutils literal notranslate"><span class="pre">items</span></code> semaphores have positive values, then the front and back of the queue
|
||
are guaranteed to be in different places. That is, there are multiple items in
|
||
the queue, so the producer and consumer within the critical sections would not
|
||
be interfering with each other. Consequently, there is no reason that the
|
||
producer needs to lock out the consumer, or vice versa.</p>
|
||
<div class="highlight-c border border-dark rounded-lg bg-light px-0 mb-3 notranslate" id="cl8-18"><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</pre></div></td><td class="code"><div class="highlight bg-light"><pre class="mb-0"><span></span><span class="cm">/* Code Listing 8.18:</span>
|
||
<span class="cm"> Solution for a multiple producers and consumers</span>
|
||
<span class="cm"> */</span>
|
||
|
||
<span class="kt">void</span>
|
||
<span class="nf">enqueue</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span><span class="n">queue</span><span class="p">,</span> <span class="n">data_t</span> <span class="o">*</span><span class="n">data</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">space</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">items</span><span class="p">,</span>
|
||
<span class="n">pthread_mutex_t</span> <span class="o">*</span><span class="n">producer_lock</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="n">sem_wait</span> <span class="p">(</span><span class="n">space</span><span class="p">);</span>
|
||
<span class="n">pthread_mutex_lock</span> <span class="p">(</span><span class="n">producer_lock</span><span class="p">);</span>
|
||
<span class="n">enqueue_unsafe</span> <span class="p">(</span><span class="n">queue</span><span class="p">,</span> <span class="n">data</span><span class="p">);</span>
|
||
<span class="n">pthread_mutex_unlock</span> <span class="p">(</span><span class="n">producer_lock</span><span class="p">);</span>
|
||
<span class="n">sem_post</span> <span class="p">(</span><span class="n">items</span><span class="p">);</span>
|
||
<span class="p">}</span>
|
||
|
||
<span class="n">data_t</span> <span class="o">*</span>
|
||
<span class="nf">dequeue</span> <span class="p">(</span><span class="n">queue_t</span> <span class="o">*</span> <span class="n">queue</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">space</span><span class="p">,</span> <span class="n">sem_t</span> <span class="o">*</span><span class="n">items</span><span class="p">,</span>
|
||
<span class="n">pthread_mutex_t</span> <span class="o">*</span><span class="n">consumer_lock</span><span class="p">)</span>
|
||
<span class="p">{</span>
|
||
<span class="n">sem_wait</span> <span class="p">(</span><span class="n">items</span><span class="p">);</span>
|
||
<span class="n">pthread_mutex_lock</span> <span class="p">(</span><span class="n">consumer_lock</span><span class="p">);</span>
|
||
<span class="n">data_t</span> <span class="o">*</span> <span class="n">data</span> <span class="o">=</span> <span class="n">dequeue_unsafe</span> <span class="p">(</span><span class="n">queue</span><span class="p">);</span>
|
||
<span class="n">pthread_mutex_unlock</span> <span class="p">(</span><span class="n">consumer_lock</span><span class="p">);</span>
|
||
<span class="n">sem_post</span> <span class="p">(</span><span class="n">space</span><span class="p">);</span>
|
||
<span class="k">return</span> <span class="n">data</span><span class="p">;</span>
|
||
<span class="p">}</span>
|
||
</pre></div>
|
||
</td></tr></table></div>
|
||
<div
|
||
id="SynchProbProdSumm"
|
||
class="embedContainer"
|
||
data-exer-name="SynchProbProdSumm"
|
||
data-long-name="Producer Consumer Summary Questions"
|
||
data-short-name="SynchProbProdSumm"
|
||
data-frame-src="../../../Exercises/SynchProblems/SynchProbProdSumm.html?selfLoggingEnabled=false&localMode=true&module=ProdCons&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="SynchProbProdSumm_iframe"></div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
|
||
</div>
|
||
|
||
|
||
|
||
<div class="container">
|
||
|
||
<div class="mt-4 container center">
|
||
«  <a id="prevmod1" href="SynchDesign.html">8.2. Basic Synchronization Design Patterns</a>
|
||
  ::  
|
||
<a class="uplink" href="index.html">Contents</a>
|
||
  ::  
|
||
<a id="nextmod1" href="ReadWrite.html">8.4. Readers-Writers Problem</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> |