<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Issue 2189: Throwing swap breaks unordered containers' state</title>
<meta property="og:title" content="Issue 2189: Throwing swap breaks unordered containers' state">
<meta property="og:description" content="C++ library issue. Status: Open">
<meta property="og:url" content="https://cplusplus.github.io/LWG/issue2189.html">
<meta property="og:type" content="website">
<meta property="og:image" content="http://cplusplus.github.io/LWG/images/cpp_logo.png">
<meta property="og:image:alt" content="C++ logo">
<style>
  p {text-align:justify}
  li {text-align:justify}
  pre code.backtick::before { content: "`" }
  pre code.backtick::after { content: "`" }
  blockquote.note
  {
    background-color:#E0E0E0;
    padding-left: 15px;
    padding-right: 15px;
    padding-top: 1px;
    padding-bottom: 1px;
  }
  ins {background-color:#A0FFA0}
  del {background-color:#FFA0A0}
  table.issues-index { border: 1px solid; border-collapse: collapse; }
  table.issues-index th { text-align: center; padding: 4px; border: 1px solid; }
  table.issues-index td { padding: 4px; border: 1px solid; }
  table.issues-index td:nth-child(1) { text-align: right; }
  table.issues-index td:nth-child(2) { text-align: left; }
  table.issues-index td:nth-child(3) { text-align: left; }
  table.issues-index td:nth-child(4) { text-align: left; }
  table.issues-index td:nth-child(5) { text-align: center; }
  table.issues-index td:nth-child(6) { text-align: center; }
  table.issues-index td:nth-child(7) { text-align: left; }
  table.issues-index td:nth-child(5) span.no-pr { color: red; }
  @media (prefers-color-scheme: dark) {
     html {
        color: #ddd;
        background-color: black;
     }
     ins {
        background-color: #225522
     }
     del {
        background-color: #662222
     }
     a {
        color: #6af
     }
     a:visited {
        color: #6af
     }
     blockquote.note
     {
        background-color: rgba(255, 255, 255, .10)
     }
  }
</style>
</head>
<body>
<hr>
<p><em>This page is a snapshot from the LWG issues list, see the <a href="lwg-active.html">Library Active Issues List</a> for more information and the meaning of <a href="lwg-active.html#Open">Open</a> status.</em></p>
<h3 id="2189"><a href="lwg-active.html#2189">2189</a>. Throwing <code>swap</code> breaks unordered containers' state</h3>
<p><b>Section:</b> 23.2.8.2 <a href="https://wg21.link/unord.req.except">[unord.req.except]</a> <b>Status:</b> <a href="lwg-active.html#Open">Open</a>
 <b>Submitter:</b> Alisdair Meredith <b>Opened:</b> 2012-09-23 <b>Last modified:</b> 2019-07-22</p>
<p><b>Priority: </b>3
</p>
<p><b>View all issues with</b> <a href="lwg-status.html#Open">Open</a> status.</p>
<p><b>Discussion:</b></p>

<p>
The hash functor and key-comparison functor of unordered containers are allowed to throw on <code>swap</code>.
</p>
<p>
23.2.8.2 <a href="https://wg21.link/unord.req.except">[unord.req.except]</a>p3 "For unordered associative containers, no <code>swap</code> function throws
an exception unless that exception is thrown by the swap of the container's Hash or Pred object (if any)."
</p>
<p>
In such a case we must offer the basic exception safety guarantee, where both objects are left in valid
but unspecified states, and no resources are leaked.  This yields a corrupt, un-usable container if the
first <code>swap</code> succeeds, but the second fails by throwing, as the functors form a matched pair.
</p>
<p>
So our basic scenario is first, swap the allocators if the allocators propagate on swap, according to
<code>allocator_traits</code>.  Next we swap the pointers to our internal hash table data structures, so that
they match the allocators that allocated them.  (Typically, this operation cannot throw).  Now our containers
are back in a safely destructible state if an exception follows.
</p>
<p>
Next, let's say we swap the hash functor, and that throws.  We have a corrupt data structure, in that the
buckets are not correctly indexed by the correct functors, lookups will give unpredicatable results etc.
We can safely restore a usable state by forcibly clearing each container - which does not leak resources
and leaves us with two (empty but) usable containers.
</p>
<p>
Now let us assume that the hasher swap succeeds.  Next we swap the equality comparator functor, and this
too could throw. The important point to bear in mind is that these two functors form an important pairing
- two objects that compare equal by the equality functor must also hash to the same value.  If we swap
one without the other, we most likely leave the container in an unusable state, even if we clear out all
elements.
</p>
<p>
1. A colleague pointed out that the solution for this is to dynamically allocate the two functors, and then
we need only swap pointers, which is not a throwing operation.  And if we don't want to allocate on default
construction (a common QoI request), we might consider moving to a dynamically allocated functors whenever
<code>swap</code> is called, or on first insertion.  Of course, allocating memory in <code>swap</code> is a whole
new can of worms, but this does not really sound like the design we had intended.
</p>

<p>
2. The simplest option is to say that we do not support hasher or equality functors that throw on ADL
<code>swap</code>.  Note that the requirement is simply to not throw, rather than to be explicitly
marked as <code>noexcept</code>.  Throwing functors are allowed, so long as we never use values that
would actually manifest a throw when used in an unordered container.
</p>

<p>
Pablo went on to give me several more options, to be sure we have a full set to consider:
</p>
<p>
3. Disallow one or the other functor from throwing.  In that case, the 
possibly-throwing functor must be swapped first, then the other functor, 
the allocator, and the data pointer(s) afterwards (in any order -- there 
was a TC that allocator assignment and swap may not throw if the 
corresponding propagation trait is true.). Of course, the question 
becomes: which functor is allowed to throw and which one is not?
</p>
<p>
4. Require that any successful functor <code>swap</code> be reliably reversible.  
This is very inventive.  I know of no other place in the standard where 
such a requirement is stated, though I have occasionally wanted such a 
guarantee.
</p>
<p>
5. Allow a failed swap to leave the containers in a state where future 
insertions may fail for reasons other than is currently allowed.  
Specifically, if the hash and equality functors are out of sync, all 
insertions will fail.  Presumably some "incompletely swapped" exception 
would be thrown.  This is "slightly" inventive, although people have been 
discussing "radioactive" states for a while.
</p>

<p><i>[2013-03-15 Issues Teleconference]</i></p>

<p>
Moved to Open.
</p>

<p><i>[2019 Cologne Wednesday night]</i></p>

<p>Billy to write resolution for option #2. This may require a paper.</p>


<p id="res-2189"><b>Proposed resolution:</b></p>






</body>
</html>
