Skip to content

Commit 8b9b5fc

Browse files
committed
New issue from Casey: unique_lock self-move-assignment is broken
1 parent 55898e6 commit 8b9b5fc

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

xml/issue4172.xml

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
<?xml version='1.0' encoding='utf-8' standalone='no'?>
2+
<!DOCTYPE issue SYSTEM "lwg-issue.dtd">
3+
4+
<issue num="4172" status="New">
5+
<title>unique_lock self-move-assignment is broken</title>
6+
<section><sref ref="[thread.lock.unique.cons]"/></section>
7+
<submitter>Casey Carter</submitter>
8+
<date>13 Nov 2024</date>
9+
<priority>99</priority>
10+
11+
<discussion>
12+
<p>
13+
The postconditions in <sref ref="[thread.lock.unique.cons]"/> paragraph 19:
14+
15+
<blockquote>
16+
<i>Postconditions</i>:
17+
`pm == u_p.pm` and `owns == u_p.owns`
18+
(where `u_p` is the state of `u` just prior to this construction),
19+
`u.pm == 0` and `u.owns == false`.
20+
</blockquote>
21+
22+
contradict themselves if `*this` and the parameter `u` refer to the same object.
23+
(Presumably "this construction" means the assignment, and it is copy-pasta from
24+
the move constructor postconditions.) Apparently
25+
`unique_lock` didn't get the memo that we require well-defined behavior
26+
from self-move-assignment as of LWG <iref ref="2839"/>.
27+
</p>
28+
<p>
29+
Also, the move assignment operator doesn't specify what it returns.
30+
</p>
31+
</discussion>
32+
33+
<resolution>
34+
<p>
35+
This wording is relative to <paper num="N4993"/>.
36+
</p>
37+
38+
<blockquote class="note">
39+
Drafting Note: I've chosen to use the move-into-temporary-and-swap idiom here
40+
to keep things short and sweet.
41+
Since move construction, swap, and destruction are all `noexcept`,
42+
I've promoted move assignment from "<i>Throws</i>: Nothing" to `noexcept` as well.
43+
This is consistent with eliminating the implicit narrow contract condition
44+
that `*this` and `u` refer to distinct objects.
45+
</blockquote>
46+
47+
<ol>
48+
<li>
49+
<p>
50+
In the class synopsis in <sref ref="[thread.lock.unique.general]"/>,
51+
annotate the move assignment operator as `noexcept`:
52+
</p>
53+
54+
<blockquote><pre><code>
55+
namespace std {
56+
template&lt;class Mutex&gt;
57+
class unique_lock {
58+
[...]
59+
unique_lock&amp; operator=(unique_lock&amp;&amp; u) <ins>noexcept</ins>;
60+
[...]
61+
};
62+
}
63+
</code></pre></blockquote>
64+
</li>
65+
66+
<li>
67+
<p>
68+
Modify <sref ref="[thread.lock.unique.cons]"/> as follows:
69+
</p>
70+
71+
<blockquote>
72+
<pre><code>
73+
unique_lock&amp; operator=(unique_lock&amp;&amp; u) <ins>noexcept</ins>;
74+
</code></pre>
75+
<p>
76+
-18- <i>Effects</i>:
77+
<del>If `owns` calls `pm->unlock()`.</del>
78+
<ins>Equivalent to: `unique_lock{std::move(u)}.swap(*this)`.</ins>
79+
</p>
80+
<p>
81+
<ins>-?- <i>Returns</i>: `*this`.</ins>
82+
</p>
83+
<p>
84+
<del>-19- <i>Postconditions</i>:
85+
`pm == u_p.pm` and `owns == u_p.owns`
86+
(where `u_p` is the state of `u` just prior to this construction),
87+
`u.pm == 0` and `u.owns == false`.
88+
</del>
89+
</p>
90+
<p>
91+
<del>-20- [<i>Note 1</i>:
92+
With a recursive mutex it is possible for both `*this` and u to own
93+
the same mutex before the assignment.
94+
In this case, *this will own the mutex after the assignment and u will not.
95+
&mdash; <i>end note</i>]</del>
96+
</p>
97+
<p>
98+
<del>-21- Throws: Nothing.</del>
99+
</p>
100+
</blockquote>
101+
</li>
102+
</ol>
103+
104+
</resolution>
105+
106+
</issue>

0 commit comments

Comments
 (0)