<!DOCTYPE html>
<html><head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
        <meta charset="UTF-8">
        <title>P2616R0: Making std::atomic notification/wait operations usable in more situations</title>
        <style>
/* From extension vscode.github */
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

.vscode-dark img[src$=\#gh-light-mode-only],
.vscode-light img[src$=\#gh-dark-mode-only] {
	display: none;
}

/* From extension vscode.markdown-math */
@font-face{font-family:KaTeX_AMS;font-style:normal;font-weight:400;src:url(fonts/KaTeX_AMS-Regular.woff2) format("woff2"),url(fonts/KaTeX_AMS-Regular.woff) format("woff"),url(fonts/KaTeX_AMS-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Caligraphic-Bold.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Bold.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Caligraphic;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Caligraphic-Regular.woff2) format("woff2"),url(fonts/KaTeX_Caligraphic-Regular.woff) format("woff"),url(fonts/KaTeX_Caligraphic-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Fraktur-Bold.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Bold.woff) format("woff"),url(fonts/KaTeX_Fraktur-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Fraktur;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Fraktur-Regular.woff2) format("woff2"),url(fonts/KaTeX_Fraktur-Regular.woff) format("woff"),url(fonts/KaTeX_Fraktur-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:700;src:url(fonts/KaTeX_Main-Bold.woff2) format("woff2"),url(fonts/KaTeX_Main-Bold.woff) format("woff"),url(fonts/KaTeX_Main-Bold.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Main-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Main-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Main-BoldItalic.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Main-Italic.woff2) format("woff2"),url(fonts/KaTeX_Main-Italic.woff) format("woff"),url(fonts/KaTeX_Main-Italic.ttf) format("truetype")}@font-face{font-family:KaTeX_Main;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Main-Regular.woff2) format("woff2"),url(fonts/KaTeX_Main-Regular.woff) format("woff"),url(fonts/KaTeX_Main-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:700;src:url(fonts/KaTeX_Math-BoldItalic.woff2) format("woff2"),url(fonts/KaTeX_Math-BoldItalic.woff) format("woff"),url(fonts/KaTeX_Math-BoldItalic.ttf) format("truetype")}@font-face{font-family:KaTeX_Math;font-style:italic;font-weight:400;src:url(fonts/KaTeX_Math-Italic.woff2) format("woff2"),url(fonts/KaTeX_Math-Italic.woff) format("woff"),url(fonts/KaTeX_Math-Italic.ttf) format("truetype")}@font-face{font-family:"KaTeX_SansSerif";font-style:normal;font-weight:700;src:url(fonts/KaTeX_SansSerif-Bold.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Bold.woff) format("woff"),url(fonts/KaTeX_SansSerif-Bold.ttf) format("truetype")}@font-face{font-family:"KaTeX_SansSerif";font-style:italic;font-weight:400;src:url(fonts/KaTeX_SansSerif-Italic.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Italic.woff) format("woff"),url(fonts/KaTeX_SansSerif-Italic.ttf) format("truetype")}@font-face{font-family:"KaTeX_SansSerif";font-style:normal;font-weight:400;src:url(fonts/KaTeX_SansSerif-Regular.woff2) format("woff2"),url(fonts/KaTeX_SansSerif-Regular.woff) format("woff"),url(fonts/KaTeX_SansSerif-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Script;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Script-Regular.woff2) format("woff2"),url(fonts/KaTeX_Script-Regular.woff) format("woff"),url(fonts/KaTeX_Script-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size1;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size1-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size1-Regular.woff) format("woff"),url(fonts/KaTeX_Size1-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size2;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size2-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size2-Regular.woff) format("woff"),url(fonts/KaTeX_Size2-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size3;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size3-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size3-Regular.woff) format("woff"),url(fonts/KaTeX_Size3-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Size4;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Size4-Regular.woff2) format("woff2"),url(fonts/KaTeX_Size4-Regular.woff) format("woff"),url(fonts/KaTeX_Size4-Regular.ttf) format("truetype")}@font-face{font-family:KaTeX_Typewriter;font-style:normal;font-weight:400;src:url(fonts/KaTeX_Typewriter-Regular.woff2) format("woff2"),url(fonts/KaTeX_Typewriter-Regular.woff) format("woff"),url(fonts/KaTeX_Typewriter-Regular.ttf) format("truetype")}.katex{text-rendering:auto;font:normal 1.21em KaTeX_Main,Times New Roman,serif;line-height:1.2;text-indent:0}.katex *{-ms-high-contrast-adjust:none!important;border-color:currentColor}.katex .katex-version:after{content:"0.13.24"}.katex .katex-mathml{clip:rect(1px,1px,1px,1px);border:0;height:1px;overflow:hidden;padding:0;position:absolute;width:1px}.katex .katex-html>.newline{display:block}.katex .base{position:relative;white-space:nowrap;width:-webkit-min-content;width:-moz-min-content;width:min-content}.katex .base,.katex .strut{display:inline-block}.katex .textbf{font-weight:700}.katex .textit{font-style:italic}.katex .textrm{font-family:KaTeX_Main}.katex .textsf{font-family:KaTeX_SansSerif}.katex .texttt{font-family:KaTeX_Typewriter}.katex .mathnormal{font-family:KaTeX_Math;font-style:italic}.katex .mathit{font-family:KaTeX_Main;font-style:italic}.katex .mathrm{font-style:normal}.katex .mathbf{font-family:KaTeX_Main;font-weight:700}.katex .boldsymbol{font-family:KaTeX_Math;font-style:italic;font-weight:700}.katex .amsrm,.katex .mathbb,.katex .textbb{font-family:KaTeX_AMS}.katex .mathcal{font-family:KaTeX_Caligraphic}.katex .mathfrak,.katex .textfrak{font-family:KaTeX_Fraktur}.katex .mathtt{font-family:KaTeX_Typewriter}.katex .mathscr,.katex .textscr{font-family:KaTeX_Script}.katex .mathsf,.katex .textsf{font-family:KaTeX_SansSerif}.katex .mathboldsf,.katex .textboldsf{font-family:KaTeX_SansSerif;font-weight:700}.katex .mathitsf,.katex .textitsf{font-family:KaTeX_SansSerif;font-style:italic}.katex .mainrm{font-family:KaTeX_Main;font-style:normal}.katex .vlist-t{border-collapse:collapse;display:inline-table;table-layout:fixed}.katex .vlist-r{display:table-row}.katex .vlist{display:table-cell;position:relative;vertical-align:bottom}.katex .vlist>span{display:block;height:0;position:relative}.katex .vlist>span>span{display:inline-block}.katex .vlist>span>.pstrut{overflow:hidden;width:0}.katex .vlist-t2{margin-right:-2px}.katex .vlist-s{display:table-cell;font-size:1px;min-width:2px;vertical-align:bottom;width:2px}.katex .vbox{align-items:baseline;display:inline-flex;flex-direction:column}.katex .hbox{width:100%}.katex .hbox,.katex .thinbox{display:inline-flex;flex-direction:row}.katex .thinbox{max-width:0;width:0}.katex .msupsub{text-align:left}.katex .mfrac>span>span{text-align:center}.katex .mfrac .frac-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline,.katex .hline,.katex .mfrac .frac-line,.katex .overline .overline-line,.katex .rule,.katex .underline .underline-line{min-height:1px}.katex .mspace{display:inline-block}.katex .clap,.katex .llap,.katex .rlap{position:relative;width:0}.katex .clap>.inner,.katex .llap>.inner,.katex .rlap>.inner{position:absolute}.katex .clap>.fix,.katex .llap>.fix,.katex .rlap>.fix{display:inline-block}.katex .llap>.inner{right:0}.katex .clap>.inner,.katex .rlap>.inner{left:0}.katex .clap>.inner>span{margin-left:-50%;margin-right:50%}.katex .rule{border:0 solid;display:inline-block;position:relative}.katex .hline,.katex .overline .overline-line,.katex .underline .underline-line{border-bottom-style:solid;display:inline-block;width:100%}.katex .hdashline{border-bottom-style:dashed;display:inline-block;width:100%}.katex .sqrt>.root{margin-left:.27777778em;margin-right:-.55555556em}.katex .fontsize-ensurer.reset-size1.size1,.katex .sizing.reset-size1.size1{font-size:1em}.katex .fontsize-ensurer.reset-size1.size2,.katex .sizing.reset-size1.size2{font-size:1.2em}.katex .fontsize-ensurer.reset-size1.size3,.katex .sizing.reset-size1.size3{font-size:1.4em}.katex .fontsize-ensurer.reset-size1.size4,.katex .sizing.reset-size1.size4{font-size:1.6em}.katex .fontsize-ensurer.reset-size1.size5,.katex .sizing.reset-size1.size5{font-size:1.8em}.katex .fontsize-ensurer.reset-size1.size6,.katex .sizing.reset-size1.size6{font-size:2em}.katex .fontsize-ensurer.reset-size1.size7,.katex .sizing.reset-size1.size7{font-size:2.4em}.katex .fontsize-ensurer.reset-size1.size8,.katex .sizing.reset-size1.size8{font-size:2.88em}.katex .fontsize-ensurer.reset-size1.size9,.katex .sizing.reset-size1.size9{font-size:3.456em}.katex .fontsize-ensurer.reset-size1.size10,.katex .sizing.reset-size1.size10{font-size:4.148em}.katex .fontsize-ensurer.reset-size1.size11,.katex .sizing.reset-size1.size11{font-size:4.976em}.katex .fontsize-ensurer.reset-size2.size1,.katex .sizing.reset-size2.size1{font-size:.83333333em}.katex .fontsize-ensurer.reset-size2.size2,.katex .sizing.reset-size2.size2{font-size:1em}.katex .fontsize-ensurer.reset-size2.size3,.katex .sizing.reset-size2.size3{font-size:1.16666667em}.katex .fontsize-ensurer.reset-size2.size4,.katex .sizing.reset-size2.size4{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size2.size5,.katex .sizing.reset-size2.size5{font-size:1.5em}.katex .fontsize-ensurer.reset-size2.size6,.katex .sizing.reset-size2.size6{font-size:1.66666667em}.katex .fontsize-ensurer.reset-size2.size7,.katex .sizing.reset-size2.size7{font-size:2em}.katex .fontsize-ensurer.reset-size2.size8,.katex .sizing.reset-size2.size8{font-size:2.4em}.katex .fontsize-ensurer.reset-size2.size9,.katex .sizing.reset-size2.size9{font-size:2.88em}.katex .fontsize-ensurer.reset-size2.size10,.katex .sizing.reset-size2.size10{font-size:3.45666667em}.katex .fontsize-ensurer.reset-size2.size11,.katex .sizing.reset-size2.size11{font-size:4.14666667em}.katex .fontsize-ensurer.reset-size3.size1,.katex .sizing.reset-size3.size1{font-size:.71428571em}.katex .fontsize-ensurer.reset-size3.size2,.katex .sizing.reset-size3.size2{font-size:.85714286em}.katex .fontsize-ensurer.reset-size3.size3,.katex .sizing.reset-size3.size3{font-size:1em}.katex .fontsize-ensurer.reset-size3.size4,.katex .sizing.reset-size3.size4{font-size:1.14285714em}.katex .fontsize-ensurer.reset-size3.size5,.katex .sizing.reset-size3.size5{font-size:1.28571429em}.katex .fontsize-ensurer.reset-size3.size6,.katex .sizing.reset-size3.size6{font-size:1.42857143em}.katex .fontsize-ensurer.reset-size3.size7,.katex .sizing.reset-size3.size7{font-size:1.71428571em}.katex .fontsize-ensurer.reset-size3.size8,.katex .sizing.reset-size3.size8{font-size:2.05714286em}.katex .fontsize-ensurer.reset-size3.size9,.katex .sizing.reset-size3.size9{font-size:2.46857143em}.katex .fontsize-ensurer.reset-size3.size10,.katex .sizing.reset-size3.size10{font-size:2.96285714em}.katex .fontsize-ensurer.reset-size3.size11,.katex .sizing.reset-size3.size11{font-size:3.55428571em}.katex .fontsize-ensurer.reset-size4.size1,.katex .sizing.reset-size4.size1{font-size:.625em}.katex .fontsize-ensurer.reset-size4.size2,.katex .sizing.reset-size4.size2{font-size:.75em}.katex .fontsize-ensurer.reset-size4.size3,.katex .sizing.reset-size4.size3{font-size:.875em}.katex .fontsize-ensurer.reset-size4.size4,.katex .sizing.reset-size4.size4{font-size:1em}.katex .fontsize-ensurer.reset-size4.size5,.katex .sizing.reset-size4.size5{font-size:1.125em}.katex .fontsize-ensurer.reset-size4.size6,.katex .sizing.reset-size4.size6{font-size:1.25em}.katex .fontsize-ensurer.reset-size4.size7,.katex .sizing.reset-size4.size7{font-size:1.5em}.katex .fontsize-ensurer.reset-size4.size8,.katex .sizing.reset-size4.size8{font-size:1.8em}.katex .fontsize-ensurer.reset-size4.size9,.katex .sizing.reset-size4.size9{font-size:2.16em}.katex .fontsize-ensurer.reset-size4.size10,.katex .sizing.reset-size4.size10{font-size:2.5925em}.katex .fontsize-ensurer.reset-size4.size11,.katex .sizing.reset-size4.size11{font-size:3.11em}.katex .fontsize-ensurer.reset-size5.size1,.katex .sizing.reset-size5.size1{font-size:.55555556em}.katex .fontsize-ensurer.reset-size5.size2,.katex .sizing.reset-size5.size2{font-size:.66666667em}.katex .fontsize-ensurer.reset-size5.size3,.katex .sizing.reset-size5.size3{font-size:.77777778em}.katex .fontsize-ensurer.reset-size5.size4,.katex .sizing.reset-size5.size4{font-size:.88888889em}.katex .fontsize-ensurer.reset-size5.size5,.katex .sizing.reset-size5.size5{font-size:1em}.katex .fontsize-ensurer.reset-size5.size6,.katex .sizing.reset-size5.size6{font-size:1.11111111em}.katex .fontsize-ensurer.reset-size5.size7,.katex .sizing.reset-size5.size7{font-size:1.33333333em}.katex .fontsize-ensurer.reset-size5.size8,.katex .sizing.reset-size5.size8{font-size:1.6em}.katex .fontsize-ensurer.reset-size5.size9,.katex .sizing.reset-size5.size9{font-size:1.92em}.katex .fontsize-ensurer.reset-size5.size10,.katex .sizing.reset-size5.size10{font-size:2.30444444em}.katex .fontsize-ensurer.reset-size5.size11,.katex .sizing.reset-size5.size11{font-size:2.76444444em}.katex .fontsize-ensurer.reset-size6.size1,.katex .sizing.reset-size6.size1{font-size:.5em}.katex .fontsize-ensurer.reset-size6.size2,.katex .sizing.reset-size6.size2{font-size:.6em}.katex .fontsize-ensurer.reset-size6.size3,.katex .sizing.reset-size6.size3{font-size:.7em}.katex .fontsize-ensurer.reset-size6.size4,.katex .sizing.reset-size6.size4{font-size:.8em}.katex .fontsize-ensurer.reset-size6.size5,.katex .sizing.reset-size6.size5{font-size:.9em}.katex .fontsize-ensurer.reset-size6.size6,.katex .sizing.reset-size6.size6{font-size:1em}.katex .fontsize-ensurer.reset-size6.size7,.katex .sizing.reset-size6.size7{font-size:1.2em}.katex .fontsize-ensurer.reset-size6.size8,.katex .sizing.reset-size6.size8{font-size:1.44em}.katex .fontsize-ensurer.reset-size6.size9,.katex .sizing.reset-size6.size9{font-size:1.728em}.katex .fontsize-ensurer.reset-size6.size10,.katex .sizing.reset-size6.size10{font-size:2.074em}.katex .fontsize-ensurer.reset-size6.size11,.katex .sizing.reset-size6.size11{font-size:2.488em}.katex .fontsize-ensurer.reset-size7.size1,.katex .sizing.reset-size7.size1{font-size:.41666667em}.katex .fontsize-ensurer.reset-size7.size2,.katex .sizing.reset-size7.size2{font-size:.5em}.katex .fontsize-ensurer.reset-size7.size3,.katex .sizing.reset-size7.size3{font-size:.58333333em}.katex .fontsize-ensurer.reset-size7.size4,.katex .sizing.reset-size7.size4{font-size:.66666667em}.katex .fontsize-ensurer.reset-size7.size5,.katex .sizing.reset-size7.size5{font-size:.75em}.katex .fontsize-ensurer.reset-size7.size6,.katex .sizing.reset-size7.size6{font-size:.83333333em}.katex .fontsize-ensurer.reset-size7.size7,.katex .sizing.reset-size7.size7{font-size:1em}.katex .fontsize-ensurer.reset-size7.size8,.katex .sizing.reset-size7.size8{font-size:1.2em}.katex .fontsize-ensurer.reset-size7.size9,.katex .sizing.reset-size7.size9{font-size:1.44em}.katex .fontsize-ensurer.reset-size7.size10,.katex .sizing.reset-size7.size10{font-size:1.72833333em}.katex .fontsize-ensurer.reset-size7.size11,.katex .sizing.reset-size7.size11{font-size:2.07333333em}.katex .fontsize-ensurer.reset-size8.size1,.katex .sizing.reset-size8.size1{font-size:.34722222em}.katex .fontsize-ensurer.reset-size8.size2,.katex .sizing.reset-size8.size2{font-size:.41666667em}.katex .fontsize-ensurer.reset-size8.size3,.katex .sizing.reset-size8.size3{font-size:.48611111em}.katex .fontsize-ensurer.reset-size8.size4,.katex .sizing.reset-size8.size4{font-size:.55555556em}.katex .fontsize-ensurer.reset-size8.size5,.katex .sizing.reset-size8.size5{font-size:.625em}.katex .fontsize-ensurer.reset-size8.size6,.katex .sizing.reset-size8.size6{font-size:.69444444em}.katex .fontsize-ensurer.reset-size8.size7,.katex .sizing.reset-size8.size7{font-size:.83333333em}.katex .fontsize-ensurer.reset-size8.size8,.katex .sizing.reset-size8.size8{font-size:1em}.katex .fontsize-ensurer.reset-size8.size9,.katex .sizing.reset-size8.size9{font-size:1.2em}.katex .fontsize-ensurer.reset-size8.size10,.katex .sizing.reset-size8.size10{font-size:1.44027778em}.katex .fontsize-ensurer.reset-size8.size11,.katex .sizing.reset-size8.size11{font-size:1.72777778em}.katex .fontsize-ensurer.reset-size9.size1,.katex .sizing.reset-size9.size1{font-size:.28935185em}.katex .fontsize-ensurer.reset-size9.size2,.katex .sizing.reset-size9.size2{font-size:.34722222em}.katex .fontsize-ensurer.reset-size9.size3,.katex .sizing.reset-size9.size3{font-size:.40509259em}.katex .fontsize-ensurer.reset-size9.size4,.katex .sizing.reset-size9.size4{font-size:.46296296em}.katex .fontsize-ensurer.reset-size9.size5,.katex .sizing.reset-size9.size5{font-size:.52083333em}.katex .fontsize-ensurer.reset-size9.size6,.katex .sizing.reset-size9.size6{font-size:.5787037em}.katex .fontsize-ensurer.reset-size9.size7,.katex .sizing.reset-size9.size7{font-size:.69444444em}.katex .fontsize-ensurer.reset-size9.size8,.katex .sizing.reset-size9.size8{font-size:.83333333em}.katex .fontsize-ensurer.reset-size9.size9,.katex .sizing.reset-size9.size9{font-size:1em}.katex .fontsize-ensurer.reset-size9.size10,.katex .sizing.reset-size9.size10{font-size:1.20023148em}.katex .fontsize-ensurer.reset-size9.size11,.katex .sizing.reset-size9.size11{font-size:1.43981481em}.katex .fontsize-ensurer.reset-size10.size1,.katex .sizing.reset-size10.size1{font-size:.24108004em}.katex .fontsize-ensurer.reset-size10.size2,.katex .sizing.reset-size10.size2{font-size:.28929605em}.katex .fontsize-ensurer.reset-size10.size3,.katex .sizing.reset-size10.size3{font-size:.33751205em}.katex .fontsize-ensurer.reset-size10.size4,.katex .sizing.reset-size10.size4{font-size:.38572806em}.katex .fontsize-ensurer.reset-size10.size5,.katex .sizing.reset-size10.size5{font-size:.43394407em}.katex .fontsize-ensurer.reset-size10.size6,.katex .sizing.reset-size10.size6{font-size:.48216008em}.katex .fontsize-ensurer.reset-size10.size7,.katex .sizing.reset-size10.size7{font-size:.57859209em}.katex .fontsize-ensurer.reset-size10.size8,.katex .sizing.reset-size10.size8{font-size:.69431051em}.katex .fontsize-ensurer.reset-size10.size9,.katex .sizing.reset-size10.size9{font-size:.83317261em}.katex .fontsize-ensurer.reset-size10.size10,.katex .sizing.reset-size10.size10{font-size:1em}.katex .fontsize-ensurer.reset-size10.size11,.katex .sizing.reset-size10.size11{font-size:1.19961427em}.katex .fontsize-ensurer.reset-size11.size1,.katex .sizing.reset-size11.size1{font-size:.20096463em}.katex .fontsize-ensurer.reset-size11.size2,.katex .sizing.reset-size11.size2{font-size:.24115756em}.katex .fontsize-ensurer.reset-size11.size3,.katex .sizing.reset-size11.size3{font-size:.28135048em}.katex .fontsize-ensurer.reset-size11.size4,.katex .sizing.reset-size11.size4{font-size:.32154341em}.katex .fontsize-ensurer.reset-size11.size5,.katex .sizing.reset-size11.size5{font-size:.36173633em}.katex .fontsize-ensurer.reset-size11.size6,.katex .sizing.reset-size11.size6{font-size:.40192926em}.katex .fontsize-ensurer.reset-size11.size7,.katex .sizing.reset-size11.size7{font-size:.48231511em}.katex .fontsize-ensurer.reset-size11.size8,.katex .sizing.reset-size11.size8{font-size:.57877814em}.katex .fontsize-ensurer.reset-size11.size9,.katex .sizing.reset-size11.size9{font-size:.69453376em}.katex .fontsize-ensurer.reset-size11.size10,.katex .sizing.reset-size11.size10{font-size:.83360129em}.katex .fontsize-ensurer.reset-size11.size11,.katex .sizing.reset-size11.size11{font-size:1em}.katex .delimsizing.size1{font-family:KaTeX_Size1}.katex .delimsizing.size2{font-family:KaTeX_Size2}.katex .delimsizing.size3{font-family:KaTeX_Size3}.katex .delimsizing.size4{font-family:KaTeX_Size4}.katex .delimsizing.mult .delim-size1>span{font-family:KaTeX_Size1}.katex .delimsizing.mult .delim-size4>span{font-family:KaTeX_Size4}.katex .nulldelimiter{display:inline-block;width:.12em}.katex .delimcenter,.katex .op-symbol{position:relative}.katex .op-symbol.small-op{font-family:KaTeX_Size1}.katex .op-symbol.large-op{font-family:KaTeX_Size2}.katex .accent>.vlist-t,.katex .op-limits>.vlist-t{text-align:center}.katex .accent .accent-body{position:relative}.katex .accent .accent-body:not(.accent-full){width:0}.katex .overlay{display:block}.katex .mtable .vertical-separator{display:inline-block;min-width:1px}.katex .mtable .arraycolsep{display:inline-block}.katex .mtable .col-align-c>.vlist-t{text-align:center}.katex .mtable .col-align-l>.vlist-t{text-align:left}.katex .mtable .col-align-r>.vlist-t{text-align:right}.katex .svg-align{text-align:left}.katex svg{fill:currentColor;stroke:currentColor;fill-rule:nonzero;fill-opacity:1;stroke-width:1;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;display:block;height:inherit;position:absolute;width:100%}.katex svg path{stroke:none}.katex img{border-style:none;max-height:none;max-width:none;min-height:0;min-width:0}.katex .stretchy{display:block;overflow:hidden;position:relative;width:100%}.katex .stretchy:after,.katex .stretchy:before{content:""}.katex .hide-tail{overflow:hidden;position:relative;width:100%}.katex .halfarrow-left{left:0;overflow:hidden;position:absolute;width:50.2%}.katex .halfarrow-right{overflow:hidden;position:absolute;right:0;width:50.2%}.katex .brace-left{left:0;overflow:hidden;position:absolute;width:25.1%}.katex .brace-center{left:25%;overflow:hidden;position:absolute;width:50%}.katex .brace-right{overflow:hidden;position:absolute;right:0;width:25.1%}.katex .x-arrow-pad{padding:0 .5em}.katex .cd-arrow-pad{padding:0 .55556em 0 .27778em}.katex .mover,.katex .munder,.katex .x-arrow{text-align:center}.katex .boxpad{padding:0 .3em}.katex .fbox,.katex .fcolorbox{border:.04em solid;box-sizing:border-box}.katex .cancel-pad{padding:0 .2em}.katex .cancel-lap{margin-left:-.2em;margin-right:-.2em}.katex .sout{border-bottom-style:solid;border-bottom-width:.08em}.katex .angl{border-right:.049em solid;border-top:.049em solid;box-sizing:border-box;margin-right:.03889em}.katex .anglpad{padding:0 .03889em}.katex .eqn-num:before{content:"(" counter(katexEqnNo) ")";counter-increment:katexEqnNo}.katex .mml-eqn-num:before{content:"(" counter(mmlEqnNo) ")";counter-increment:mmlEqnNo}.katex .mtr-glue{width:50%}.katex .cd-vert-arrow{display:inline-block;position:relative}.katex .cd-label-left{display:inline-block;position:absolute;right:calc(50% + .3em);text-align:left}.katex .cd-label-right{display:inline-block;left:calc(50% + .3em);position:absolute;text-align:right}.katex-display{display:block;margin:1em 0;text-align:center}.katex-display>.katex{display:block;text-align:center;white-space:nowrap}.katex-display>.katex>.katex-html{display:block;position:relative}.katex-display>.katex>.katex-html>.tag{position:absolute;right:0}.katex-display.leqno>.katex>.katex-html>.tag{left:0;right:auto}.katex-display.fleqn>.katex{padding-left:2em;text-align:left}body{counter-reset:katexEqnNo mmlEqnNo}

/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

.katex-error {
	color: var(--vscode-editorError-foreground);
}

/* From extension bierner.markdown-preview-github-styles */
html,
body,
.github-markdown-body {
    padding: 0 !important;
    max-width: auto !important;
}

.github-markdown-body {
    background-color: var(--color-canvas-default);
    color: var(--color-fg-default);
}

.github-markdown-body {
    border: 1px solid transparent;
    box-sizing: border-box;
    margin: 0 auto;
    width: 100% !important;
}

.github-markdown-content {
    padding: 32px !important;
    max-width: 980px;
    min-width: 200px;
    margin: 0 auto;
}


.vscode-body.scrollBeyondLastLine {
    margin-bottom: 0;
}
.vscode-body.scrollBeyondLastLine .github-markdown-body {
    padding-bottom: calc(100vh + 10px) !important;
}

.github-markdown-body blockquote {
    background-color: initial;
}

.github-markdown-body pre {
    color: initial;
}

.github-markdown-body code {
    color: inherit;
}

.github-markdown-body pre code {
    color: initial;
}

.github-markdown-body code > div {
    background: none
}

.github-markdown-body.showEditorSelection .code-line .code-line:hover:before {
	border-left: none;
}

.github-markdown-body p,
.github-markdown-body blockquote,
.github-markdown-body ul,
.github-markdown-body ol,
.github-markdown-body dl,
.github-markdown-body table,
.github-markdown-body pre {
  margin-top: 16px;
  margin-bottom: 16px;
}

/* Generated from 'node_modules/github-markdown-css/github-markdown.css' */

.github-markdown-light,
.vscode-body.vscode-light .github-markdown-auto {
  color-scheme: light;
  --color-prettylights-syntax-comment: #6e7781;
  --color-prettylights-syntax-constant: #0550ae;
  --color-prettylights-syntax-entity: #8250df;
  --color-prettylights-syntax-storage-modifier-import: #24292f;
  --color-prettylights-syntax-entity-tag: #116329;
  --color-prettylights-syntax-keyword: #cf222e;
  --color-prettylights-syntax-string: #0a3069;
  --color-prettylights-syntax-variable: #953800;
  --color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
  --color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
  --color-prettylights-syntax-invalid-illegal-bg: #82071e;
  --color-prettylights-syntax-carriage-return-text: #f6f8fa;
  --color-prettylights-syntax-carriage-return-bg: #cf222e;
  --color-prettylights-syntax-string-regexp: #116329;
  --color-prettylights-syntax-markup-list: #3b2300;
  --color-prettylights-syntax-markup-heading: #0550ae;
  --color-prettylights-syntax-markup-italic: #24292f;
  --color-prettylights-syntax-markup-bold: #24292f;
  --color-prettylights-syntax-markup-deleted-text: #82071e;
  --color-prettylights-syntax-markup-deleted-bg: #FFEBE9;
  --color-prettylights-syntax-markup-inserted-text: #116329;
  --color-prettylights-syntax-markup-inserted-bg: #dafbe1;
  --color-prettylights-syntax-markup-changed-text: #953800;
  --color-prettylights-syntax-markup-changed-bg: #ffd8b5;
  --color-prettylights-syntax-markup-ignored-text: #eaeef2;
  --color-prettylights-syntax-markup-ignored-bg: #0550ae;
  --color-prettylights-syntax-meta-diff-range: #8250df;
  --color-prettylights-syntax-brackethighlighter-angle: #57606a;
  --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
  --color-prettylights-syntax-constant-other-reference-link: #0a3069;
  --color-fg-default: #24292f;
  --color-fg-muted: #57606a;
  --color-fg-subtle: #6e7781;
  --color-canvas-default: #ffffff;
  --color-canvas-subtle: #f6f8fa;
  --color-border-default: #d0d7de;
  --color-border-muted: hsla(210,18%,87%,1);
  --color-neutral-muted: rgba(175,184,193,0.2);
  --color-accent-fg: #0969da;
  --color-accent-emphasis: #0969da;
  --color-danger-fg: #cf222e;
}

.github-markdown-dark,
.vscode-body.vscode-dark .github-markdown-auto {
  color-scheme: dark;
  --color-prettylights-syntax-comment: #8b949e;
  --color-prettylights-syntax-constant: #79c0ff;
  --color-prettylights-syntax-entity: #d2a8ff;
  --color-prettylights-syntax-storage-modifier-import: #c9d1d9;
  --color-prettylights-syntax-entity-tag: #7ee787;
  --color-prettylights-syntax-keyword: #ff7b72;
  --color-prettylights-syntax-string: #a5d6ff;
  --color-prettylights-syntax-variable: #ffa657;
  --color-prettylights-syntax-brackethighlighter-unmatched: #f85149;
  --color-prettylights-syntax-invalid-illegal-text: #f0f6fc;
  --color-prettylights-syntax-invalid-illegal-bg: #8e1519;
  --color-prettylights-syntax-carriage-return-text: #f0f6fc;
  --color-prettylights-syntax-carriage-return-bg: #b62324;
  --color-prettylights-syntax-string-regexp: #7ee787;
  --color-prettylights-syntax-markup-list: #f2cc60;
  --color-prettylights-syntax-markup-heading: #1f6feb;
  --color-prettylights-syntax-markup-italic: #c9d1d9;
  --color-prettylights-syntax-markup-bold: #c9d1d9;
  --color-prettylights-syntax-markup-deleted-text: #ffdcd7;
  --color-prettylights-syntax-markup-deleted-bg: #67060c;
  --color-prettylights-syntax-markup-inserted-text: #aff5b4;
  --color-prettylights-syntax-markup-inserted-bg: #033a16;
  --color-prettylights-syntax-markup-changed-text: #ffdfb6;
  --color-prettylights-syntax-markup-changed-bg: #5a1e02;
  --color-prettylights-syntax-markup-ignored-text: #c9d1d9;
  --color-prettylights-syntax-markup-ignored-bg: #1158c7;
  --color-prettylights-syntax-meta-diff-range: #d2a8ff;
  --color-prettylights-syntax-brackethighlighter-angle: #8b949e;
  --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;
  --color-prettylights-syntax-constant-other-reference-link: #a5d6ff;
  --color-fg-default: #c9d1d9;
  --color-fg-muted: #8b949e;
  --color-fg-subtle: #484f58;
  --color-canvas-default: #0d1117;
  --color-canvas-subtle: #161b22;
  --color-border-default: #30363d;
  --color-border-muted: #21262d;
  --color-neutral-muted: rgba(110,118,129,0.4);
  --color-accent-fg: #58a6ff;
  --color-accent-emphasis: #1f6feb;
  --color-danger-fg: #f85149;
}

@media (prefers-color-scheme: dark) {
  .github-markdown-system {
    color-scheme: dark;
    --color-prettylights-syntax-comment: #8b949e;
    --color-prettylights-syntax-constant: #79c0ff;
    --color-prettylights-syntax-entity: #d2a8ff;
    --color-prettylights-syntax-storage-modifier-import: #c9d1d9;
    --color-prettylights-syntax-entity-tag: #7ee787;
    --color-prettylights-syntax-keyword: #ff7b72;
    --color-prettylights-syntax-string: #a5d6ff;
    --color-prettylights-syntax-variable: #ffa657;
    --color-prettylights-syntax-brackethighlighter-unmatched: #f85149;
    --color-prettylights-syntax-invalid-illegal-text: #f0f6fc;
    --color-prettylights-syntax-invalid-illegal-bg: #8e1519;
    --color-prettylights-syntax-carriage-return-text: #f0f6fc;
    --color-prettylights-syntax-carriage-return-bg: #b62324;
    --color-prettylights-syntax-string-regexp: #7ee787;
    --color-prettylights-syntax-markup-list: #f2cc60;
    --color-prettylights-syntax-markup-heading: #1f6feb;
    --color-prettylights-syntax-markup-italic: #c9d1d9;
    --color-prettylights-syntax-markup-bold: #c9d1d9;
    --color-prettylights-syntax-markup-deleted-text: #ffdcd7;
    --color-prettylights-syntax-markup-deleted-bg: #67060c;
    --color-prettylights-syntax-markup-inserted-text: #aff5b4;
    --color-prettylights-syntax-markup-inserted-bg: #033a16;
    --color-prettylights-syntax-markup-changed-text: #ffdfb6;
    --color-prettylights-syntax-markup-changed-bg: #5a1e02;
    --color-prettylights-syntax-markup-ignored-text: #c9d1d9;
    --color-prettylights-syntax-markup-ignored-bg: #1158c7;
    --color-prettylights-syntax-meta-diff-range: #d2a8ff;
    --color-prettylights-syntax-brackethighlighter-angle: #8b949e;
    --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58;
    --color-prettylights-syntax-constant-other-reference-link: #a5d6ff;
    --color-fg-default: #c9d1d9;
    --color-fg-muted: #8b949e;
    --color-fg-subtle: #484f58;
    --color-canvas-default: #0d1117;
    --color-canvas-subtle: #161b22;
    --color-border-default: #30363d;
    --color-border-muted: #21262d;
    --color-neutral-muted: rgba(110,118,129,0.4);
    --color-accent-fg: #58a6ff;
    --color-accent-emphasis: #1f6feb;
    --color-danger-fg: #f85149;
  }
}

@media (prefers-color-scheme: light) {
  .github-markdown-system {
    color-scheme: light;
    --color-prettylights-syntax-comment: #6e7781;
    --color-prettylights-syntax-constant: #0550ae;
    --color-prettylights-syntax-entity: #8250df;
    --color-prettylights-syntax-storage-modifier-import: #24292f;
    --color-prettylights-syntax-entity-tag: #116329;
    --color-prettylights-syntax-keyword: #cf222e;
    --color-prettylights-syntax-string: #0a3069;
    --color-prettylights-syntax-variable: #953800;
    --color-prettylights-syntax-brackethighlighter-unmatched: #82071e;
    --color-prettylights-syntax-invalid-illegal-text: #f6f8fa;
    --color-prettylights-syntax-invalid-illegal-bg: #82071e;
    --color-prettylights-syntax-carriage-return-text: #f6f8fa;
    --color-prettylights-syntax-carriage-return-bg: #cf222e;
    --color-prettylights-syntax-string-regexp: #116329;
    --color-prettylights-syntax-markup-list: #3b2300;
    --color-prettylights-syntax-markup-heading: #0550ae;
    --color-prettylights-syntax-markup-italic: #24292f;
    --color-prettylights-syntax-markup-bold: #24292f;
    --color-prettylights-syntax-markup-deleted-text: #82071e;
    --color-prettylights-syntax-markup-deleted-bg: #FFEBE9;
    --color-prettylights-syntax-markup-inserted-text: #116329;
    --color-prettylights-syntax-markup-inserted-bg: #dafbe1;
    --color-prettylights-syntax-markup-changed-text: #953800;
    --color-prettylights-syntax-markup-changed-bg: #ffd8b5;
    --color-prettylights-syntax-markup-ignored-text: #eaeef2;
    --color-prettylights-syntax-markup-ignored-bg: #0550ae;
    --color-prettylights-syntax-meta-diff-range: #8250df;
    --color-prettylights-syntax-brackethighlighter-angle: #57606a;
    --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f;
    --color-prettylights-syntax-constant-other-reference-link: #0a3069;
    --color-fg-default: #24292f;
    --color-fg-muted: #57606a;
    --color-fg-subtle: #6e7781;
    --color-canvas-default: #ffffff;
    --color-canvas-subtle: #f6f8fa;
    --color-border-default: #d0d7de;
    --color-border-muted: hsla(210,18%,87%,1);
    --color-neutral-muted: rgba(175,184,193,0.2);
    --color-accent-fg: #0969da;
    --color-accent-emphasis: #0969da;
    --color-danger-fg: #cf222e;
  }
}

.github-markdown-body {
  -ms-text-size-adjust: 100%;
  -webkit-text-size-adjust: 100%;
  margin: 0;
  color: var(--color-fg-default);
  background-color: var(--color-canvas-default);
  font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji";
  font-size: 16px;
  line-height: 1.5;
  word-wrap: break-word;
}

.github-markdown-body .octicon {
  display: inline-block;
  fill: currentColor;
  vertical-align: text-bottom;
}

.github-markdown-body h1:hover .anchor .octicon-link:before,
.github-markdown-body h2:hover .anchor .octicon-link:before,
.github-markdown-body h3:hover .anchor .octicon-link:before,
.github-markdown-body h4:hover .anchor .octicon-link:before,
.github-markdown-body h5:hover .anchor .octicon-link:before,
.github-markdown-body h6:hover .anchor .octicon-link:before {
  width: 16px;
  height: 16px;
  content: ' ';
  display: inline-block;
  background-color: currentColor;
  -webkit-mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
  mask-image: url("data:image/svg+xml,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' version='1.1' aria-hidden='true'><path fill-rule='evenodd' d='M7.775 3.275a.75.75 0 001.06 1.06l1.25-1.25a2 2 0 112.83 2.83l-2.5 2.5a2 2 0 01-2.83 0 .75.75 0 00-1.06 1.06 3.5 3.5 0 004.95 0l2.5-2.5a3.5 3.5 0 00-4.95-4.95l-1.25 1.25zm-4.69 9.64a2 2 0 010-2.83l2.5-2.5a2 2 0 012.83 0 .75.75 0 001.06-1.06 3.5 3.5 0 00-4.95 0l-2.5 2.5a3.5 3.5 0 004.95 4.95l1.25-1.25a.75.75 0 00-1.06-1.06l-1.25 1.25a2 2 0 01-2.83 0z'></path></svg>");
}

.github-markdown-body details,
.github-markdown-body figcaption,
.github-markdown-body figure {
  display: block;
}

.github-markdown-body summary {
  display: list-item;
}

.github-markdown-body a {
  background-color: transparent;
  color: var(--color-accent-fg);
  text-decoration: none;
}

.github-markdown-body a:active,
.github-markdown-body a:hover {
  outline-width: 0;
}

.github-markdown-body abbr[title] {
  border-bottom: none;
  -webkit-text-decoration: underline dotted;
  text-decoration: underline dotted;
}

.github-markdown-body b,
.github-markdown-body strong {
  font-weight: 600;
}

.github-markdown-body dfn {
  font-style: italic;
}

.github-markdown-body h1 {
  margin: .67em 0;
  font-weight: 600;
  padding-bottom: .3em;
  font-size: 2em;
  border-bottom: 1px solid var(--color-border-muted);
}

.github-markdown-body mark {
  background-color: #ff0;
  color: var(--color-text-primary);
}

.github-markdown-body small {
  font-size: 90%;
}

.github-markdown-body sub,
.github-markdown-body sup {
  font-size: 75%;
  line-height: 0;
  position: relative;
  vertical-align: baseline;
}

.github-markdown-body sub {
  bottom: -0.25em;
}

.github-markdown-body sup {
  top: -0.5em;
}

.github-markdown-body img {
  border-style: none;
  max-width: 100%;
  box-sizing: content-box;
  background-color: var(--color-canvas-default);
}

.github-markdown-body code,
.github-markdown-body kbd,
.github-markdown-body pre,
.github-markdown-body samp {
  font-family: monospace,monospace;
  font-size: 1em;
}

.github-markdown-body figure {
  margin: 1em 40px;
}

.github-markdown-body hr {
  box-sizing: content-box;
  overflow: hidden;
  background: transparent;
  border-bottom: 1px solid var(--color-border-muted);
  height: .25em;
  padding: 0;
  margin: 24px 0;
  background-color: var(--color-border-default);
  border: 0;
}

.github-markdown-body html [type=button],
.github-markdown-body [type=reset],
.github-markdown-body [type=submit] {
  -webkit-appearance: button;
}

.github-markdown-body [type=button]::-moz-focus-inner,
.github-markdown-body [type=reset]::-moz-focus-inner,
.github-markdown-body [type=submit]::-moz-focus-inner {
  border-style: none;
  padding: 0;
}

.github-markdown-body [type=button]:-moz-focusring,
.github-markdown-body [type=reset]:-moz-focusring,
.github-markdown-body [type=submit]:-moz-focusring {
  outline: 1px dotted ButtonText;
}

.github-markdown-body [type=checkbox],
.github-markdown-body [type=radio] {
  box-sizing: border-box;
  padding: 0;
}

.github-markdown-body [type=number]::-webkit-inner-spin-button,
.github-markdown-body [type=number]::-webkit-outer-spin-button {
  height: auto;
}

.github-markdown-body [type=search] {
  -webkit-appearance: textfield;
  outline-offset: -2px;
}

.github-markdown-body [type=search]::-webkit-search-cancel-button,
.github-markdown-body [type=search]::-webkit-search-decoration {
  -webkit-appearance: none;
}

.github-markdown-body ::-webkit-input-placeholder {
  color: inherit;
  opacity: .54;
}

.github-markdown-body ::-webkit-file-upload-button {
  -webkit-appearance: button;
  font: inherit;
}

.github-markdown-body a:hover {
  text-decoration: underline;
}

.github-markdown-body hr::before {
  display: table;
  content: "";
}

.github-markdown-body hr::after {
  display: table;
  clear: both;
  content: "";
}

.github-markdown-body table {
  border-spacing: 0;
  border-collapse: collapse;
  display: block;
  width: max-content;
  max-width: 100%;
  overflow: auto;
}

.github-markdown-body td,
.github-markdown-body th {
  padding: 0;
}

.github-markdown-body details summary {
  cursor: pointer;
}

.github-markdown-body details:not([open])>*:not(summary) {
  display: none !important;
}

.github-markdown-body kbd {
  display: inline-block;
  padding: 3px 5px;
  font: 11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
  line-height: 10px;
  color: var(--color-fg-default);
  vertical-align: middle;
  background-color: var(--color-canvas-subtle);
  border: solid 1px var(--color-neutral-muted);
  border-bottom-color: var(--color-neutral-muted);
  border-radius: 6px;
  box-shadow: inset 0 -1px 0 var(--color-neutral-muted);
}

.github-markdown-body h1,
.github-markdown-body h2,
.github-markdown-body h3,
.github-markdown-body h4,
.github-markdown-body h5,
.github-markdown-body h6 {
  margin-top: 24px;
  margin-bottom: 16px;
  font-weight: 600;
  line-height: 1.25;
}

.github-markdown-body h2 {
  font-weight: 600;
  padding-bottom: .3em;
  font-size: 1.5em;
  border-bottom: 1px solid var(--color-border-muted);
}

.github-markdown-body h3 {
  font-weight: 600;
  font-size: 1.25em;
}

.github-markdown-body h4 {
  font-weight: 600;
  font-size: 1em;
}

.github-markdown-body h5 {
  font-weight: 600;
  font-size: .875em;
}

.github-markdown-body h6 {
  font-weight: 600;
  font-size: .85em;
  color: var(--color-fg-muted);
}

.github-markdown-body p {
  margin-top: 0;
  margin-bottom: 10px;
}

.github-markdown-body blockquote {
  margin: 0;
  padding: 0 1em;
  color: var(--color-fg-muted);
  border-left: .25em solid var(--color-border-default);
}

.github-markdown-body ul,
.github-markdown-body ol {
  margin-top: 0;
  margin-bottom: 0;
  padding-left: 2em;
}

.github-markdown-body ol ol,
.github-markdown-body ul ol {
  list-style-type: lower-roman;
}

.github-markdown-body ul ul ol,
.github-markdown-body ul ol ol,
.github-markdown-body ol ul ol,
.github-markdown-body ol ol ol {
  list-style-type: lower-alpha;
}

.github-markdown-body dd {
  margin-left: 0;
}

.github-markdown-body tt,
.github-markdown-body code {
  font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
  font-size: 12px;
}

.github-markdown-body pre {
  margin-top: 0;
  margin-bottom: 0;
  font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace;
  font-size: 12px;
  word-wrap: normal;
}

.github-markdown-body :-ms-input-placeholder {
  color: var(--color-fg-subtle);
  opacity: 1;
}

.github-markdown-body ::-ms-input-placeholder {
  color: var(--color-fg-subtle);
  opacity: 1;
}

.github-markdown-body ::placeholder {
  color: var(--color-fg-subtle);
  opacity: 1;
}

.github-markdown-body .pl-c {
  color: var(--color-prettylights-syntax-comment);
}

.github-markdown-body .pl-c1,
.github-markdown-body .pl-s .pl-v {
  color: var(--color-prettylights-syntax-constant);
}

.github-markdown-body .pl-e,
.github-markdown-body .pl-en {
  color: var(--color-prettylights-syntax-entity);
}

.github-markdown-body .pl-smi,
.github-markdown-body .pl-s .pl-s1 {
  color: var(--color-prettylights-syntax-storage-modifier-import);
}

.github-markdown-body .pl-ent {
  color: var(--color-prettylights-syntax-entity-tag);
}

.github-markdown-body .pl-k {
  color: var(--color-prettylights-syntax-keyword);
}

.github-markdown-body .pl-s,
.github-markdown-body .pl-pds,
.github-markdown-body .pl-s .pl-pse .pl-s1,
.github-markdown-body .pl-sr,
.github-markdown-body .pl-sr .pl-cce,
.github-markdown-body .pl-sr .pl-sre,
.github-markdown-body .pl-sr .pl-sra {
  color: var(--color-prettylights-syntax-string);
}

.github-markdown-body .pl-v,
.github-markdown-body .pl-smw {
  color: var(--color-prettylights-syntax-variable);
}

.github-markdown-body .pl-bu {
  color: var(--color-prettylights-syntax-brackethighlighter-unmatched);
}

.github-markdown-body .pl-ii {
  color: var(--color-prettylights-syntax-invalid-illegal-text);
  background-color: var(--color-prettylights-syntax-invalid-illegal-bg);
}

.github-markdown-body .pl-c2 {
  color: var(--color-prettylights-syntax-carriage-return-text);
  background-color: var(--color-prettylights-syntax-carriage-return-bg);
}

.github-markdown-body .pl-sr .pl-cce {
  font-weight: bold;
  color: var(--color-prettylights-syntax-string-regexp);
}

.github-markdown-body .pl-ml {
  color: var(--color-prettylights-syntax-markup-list);
}

.github-markdown-body .pl-mh,
.github-markdown-body .pl-mh .pl-en,
.github-markdown-body .pl-ms {
  font-weight: bold;
  color: var(--color-prettylights-syntax-markup-heading);
}

.github-markdown-body .pl-mi {
  font-style: italic;
  color: var(--color-prettylights-syntax-markup-italic);
}

.github-markdown-body .pl-mb {
  font-weight: bold;
  color: var(--color-prettylights-syntax-markup-bold);
}

.github-markdown-body .pl-md {
  color: var(--color-prettylights-syntax-markup-deleted-text);
  background-color: var(--color-prettylights-syntax-markup-deleted-bg);
}

.github-markdown-body .pl-mi1 {
  color: var(--color-prettylights-syntax-markup-inserted-text);
  background-color: var(--color-prettylights-syntax-markup-inserted-bg);
}

.github-markdown-body .pl-mc {
  color: var(--color-prettylights-syntax-markup-changed-text);
  background-color: var(--color-prettylights-syntax-markup-changed-bg);
}

.github-markdown-body .pl-mi2 {
  color: var(--color-prettylights-syntax-markup-ignored-text);
  background-color: var(--color-prettylights-syntax-markup-ignored-bg);
}

.github-markdown-body .pl-mdr {
  font-weight: bold;
  color: var(--color-prettylights-syntax-meta-diff-range);
}

.github-markdown-body .pl-ba {
  color: var(--color-prettylights-syntax-brackethighlighter-angle);
}

.github-markdown-body .pl-sg {
  color: var(--color-prettylights-syntax-sublimelinter-gutter-mark);
}

.github-markdown-body .pl-corl {
  text-decoration: underline;
  color: var(--color-prettylights-syntax-constant-other-reference-link);
}

.github-markdown-body [data-catalyst] {
  display: block;
}

.github-markdown-body g-emoji {
  font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol";
  font-size: 1em;
  font-style: normal !important;
  font-weight: 400;
  line-height: 1;
  vertical-align: -0.075em;
}

.github-markdown-body g-emoji img {
  width: 1em;
  height: 1em;
}

.github-markdown-body::before {
  display: table;
  content: "";
}

.github-markdown-body::after {
  display: table;
  clear: both;
  content: "";
}

.github-markdown-body>*:first-child {
  margin-top: 0 !important;
}

.github-markdown-body>*:last-child {
  margin-bottom: 0 !important;
}

.github-markdown-body a:not([href]) {
  color: inherit;
  text-decoration: none;
}

.github-markdown-body .absent {
  color: var(--color-danger-fg);
}

.github-markdown-body .anchor {
  float: left;
  padding-right: 4px;
  margin-left: -20px;
  line-height: 1;
}

.github-markdown-body .anchor:focus {
  outline: none;
}

.github-markdown-body p,
.github-markdown-body blockquote,
.github-markdown-body ul,
.github-markdown-body ol,
.github-markdown-body dl,
.github-markdown-body table,
.github-markdown-body pre,
.github-markdown-body details {
  margin-top: 0;
  margin-bottom: 16px;
}

.github-markdown-body blockquote>:first-child {
  margin-top: 0;
}

.github-markdown-body blockquote>:last-child {
  margin-bottom: 0;
}

.github-markdown-body sup>a::before {
  content: "[";
}

.github-markdown-body sup>a::after {
  content: "]";
}

.github-markdown-body h1 .octicon-link,
.github-markdown-body h2 .octicon-link,
.github-markdown-body h3 .octicon-link,
.github-markdown-body h4 .octicon-link,
.github-markdown-body h5 .octicon-link,
.github-markdown-body h6 .octicon-link {
  color: var(--color-fg-default);
  vertical-align: middle;
  visibility: hidden;
}

.github-markdown-body h1:hover .anchor,
.github-markdown-body h2:hover .anchor,
.github-markdown-body h3:hover .anchor,
.github-markdown-body h4:hover .anchor,
.github-markdown-body h5:hover .anchor,
.github-markdown-body h6:hover .anchor {
  text-decoration: none;
}

.github-markdown-body h1:hover .anchor .octicon-link,
.github-markdown-body h2:hover .anchor .octicon-link,
.github-markdown-body h3:hover .anchor .octicon-link,
.github-markdown-body h4:hover .anchor .octicon-link,
.github-markdown-body h5:hover .anchor .octicon-link,
.github-markdown-body h6:hover .anchor .octicon-link {
  visibility: visible;
}

.github-markdown-body h1 tt,
.github-markdown-body h1 code,
.github-markdown-body h2 tt,
.github-markdown-body h2 code,
.github-markdown-body h3 tt,
.github-markdown-body h3 code,
.github-markdown-body h4 tt,
.github-markdown-body h4 code,
.github-markdown-body h5 tt,
.github-markdown-body h5 code,
.github-markdown-body h6 tt,
.github-markdown-body h6 code {
  padding: 0 .2em;
  font-size: inherit;
}

.github-markdown-body ul.no-list,
.github-markdown-body ol.no-list {
  padding: 0;
  list-style-type: none;
}

.github-markdown-body ol[type="1"] {
  list-style-type: decimal;
}

.github-markdown-body ol[type=a] {
  list-style-type: lower-alpha;
}

.github-markdown-body ol[type=i] {
  list-style-type: lower-roman;
}

.github-markdown-body div>ol:not([type]) {
  list-style-type: decimal;
}

.github-markdown-body ul ul,
.github-markdown-body ul ol,
.github-markdown-body ol ol,
.github-markdown-body ol ul {
  margin-top: 0;
  margin-bottom: 0;
}

.github-markdown-body li>p {
  margin-top: 16px;
}

.github-markdown-body li+li {
  margin-top: .25em;
}

.github-markdown-body dl {
  padding: 0;
}

.github-markdown-body dl dt {
  padding: 0;
  margin-top: 16px;
  font-size: 1em;
  font-style: italic;
  font-weight: 600;
}

.github-markdown-body dl dd {
  padding: 0 16px;
  margin-bottom: 16px;
}

.github-markdown-body table th {
  font-weight: 600;
}

.github-markdown-body table th,
.github-markdown-body table td {
  padding: 6px 13px;
  border: 1px solid var(--color-border-default);
}

.github-markdown-body table tr {
  background-color: var(--color-canvas-default);
  border-top: 1px solid var(--color-border-muted);
}

.github-markdown-body table tr:nth-child(2n) {
  background-color: var(--color-canvas-subtle);
}

.github-markdown-body table img {
  background-color: transparent;
}

.github-markdown-body img[align=right] {
  padding-left: 20px;
}

.github-markdown-body img[align=left] {
  padding-right: 20px;
}

.github-markdown-body .emoji {
  max-width: none;
  vertical-align: text-top;
  background-color: transparent;
}

.github-markdown-body span.frame {
  display: block;
  overflow: hidden;
}

.github-markdown-body span.frame>span {
  display: block;
  float: left;
  width: auto;
  padding: 7px;
  margin: 13px 0 0;
  overflow: hidden;
  border: 1px solid var(--color-border-default);
}

.github-markdown-body span.frame span img {
  display: block;
  float: left;
}

.github-markdown-body span.frame span span {
  display: block;
  padding: 5px 0 0;
  clear: both;
  color: var(--color-fg-default);
}

.github-markdown-body span.align-center {
  display: block;
  overflow: hidden;
  clear: both;
}

.github-markdown-body span.align-center>span {
  display: block;
  margin: 13px auto 0;
  overflow: hidden;
  text-align: center;
}

.github-markdown-body span.align-center span img {
  margin: 0 auto;
  text-align: center;
}

.github-markdown-body span.align-right {
  display: block;
  overflow: hidden;
  clear: both;
}

.github-markdown-body span.align-right>span {
  display: block;
  margin: 13px 0 0;
  overflow: hidden;
  text-align: right;
}

.github-markdown-body span.align-right span img {
  margin: 0;
  text-align: right;
}

.github-markdown-body span.float-left {
  display: block;
  float: left;
  margin-right: 13px;
  overflow: hidden;
}

.github-markdown-body span.float-left span {
  margin: 13px 0 0;
}

.github-markdown-body span.float-right {
  display: block;
  float: right;
  margin-left: 13px;
  overflow: hidden;
}

.github-markdown-body span.float-right>span {
  display: block;
  margin: 13px auto 0;
  overflow: hidden;
  text-align: right;
}

.github-markdown-body code,
.github-markdown-body tt {
  padding: .2em .4em;
  margin: 0;
  font-size: 85%;
  background-color: var(--color-neutral-muted);
  border-radius: 6px;
}

.github-markdown-body code br,
.github-markdown-body tt br {
  display: none;
}

.github-markdown-body del code {
  text-decoration: inherit;
}

.github-markdown-body pre code {
  font-size: 100%;
}

.github-markdown-body pre>code {
  padding: 0;
  margin: 0;
  word-break: normal;
  white-space: pre;
  background: transparent;
  border: 0;
}

.github-markdown-body .highlight {
  margin-bottom: 16px;
}

.github-markdown-body .highlight pre {
  margin-bottom: 0;
  word-break: normal;
}

.github-markdown-body .highlight pre,
.github-markdown-body pre {
  padding: 16px;
  overflow: auto;
  font-size: 85%;
  line-height: 1.45;
  background-color: var(--color-canvas-subtle);
  border-radius: 6px;
}

.github-markdown-body pre code,
.github-markdown-body pre tt {
  display: inline;
  max-width: auto;
  padding: 0;
  margin: 0;
  overflow: visible;
  line-height: inherit;
  word-wrap: normal;
  background-color: transparent;
  border: 0;
}

.github-markdown-body .csv-data td,
.github-markdown-body .csv-data th {
  padding: 5px;
  overflow: hidden;
  font-size: 12px;
  line-height: 1;
  text-align: left;
  white-space: nowrap;
}

.github-markdown-body .csv-data .blob-num {
  padding: 10px 8px 9px;
  text-align: right;
  background: var(--color-canvas-default);
  border: 0;
}

.github-markdown-body .csv-data tr {
  border-top: 0;
}

.github-markdown-body .csv-data th {
  font-weight: 600;
  background: var(--color-canvas-subtle);
  border-top: 0;
}

.github-markdown-body .footnotes {
  font-size: 12px;
  color: var(--color-fg-muted);
  border-top: 1px solid var(--color-border-default);
}

.github-markdown-body .footnotes ol {
  padding-left: 16px;
}

.github-markdown-body .footnotes li {
  position: relative;
}

.github-markdown-body .footnotes li:target::before {
  position: absolute;
  top: -8px;
  right: -8px;
  bottom: -8px;
  left: -24px;
  pointer-events: none;
  content: "";
  border: 2px solid var(--color-accent-emphasis);
  border-radius: 6px;
}

.github-markdown-body .footnotes li:target {
  color: var(--color-fg-default);
}

.github-markdown-body .footnotes .data-footnote-backref g-emoji {
  font-family: monospace;
}

.github-markdown-body [hidden] {
  display: none !important;
}

.github-markdown-body ::-webkit-calendar-picker-indicator {
  filter: invert(50%);
}
/*!
  Theme: GitHub
  Description: Highlight-js port of github.com theme
  Author: github.com
  Maintainer: @SNDST00M, @mjbvz
  Updated: 2021-12-08
*/
.markdown-body pre code.hljs {
  display: block;
  overflow-x: auto;
  padding: 16px;
}

.markdown-body code.hljs {
  padding: 3px 5px;
}

/*!
  Theme: GitHub
  Description: Theme as seen on github.com
  Author: github.com
  Maintainer: @Hirse
  Updated: 2021-05-15

  Outdated base version: https://github.com/primer/github-syntax-dark
  Current colors taken from GitHub's CSS
*/
.markdown-body .hljs {
  color: var(--color-fg-default);
  background: var(--color-canvas-subtle);
}

.markdown-body .hljs-doctag,
.markdown-body .hljs-keyword,
.markdown-body .hljs-meta .hljs-keyword,
.markdown-body .hljs-template-tag,
.markdown-body .hljs-template-variable,
.markdown-body .hljs-type,
.markdown-body .hljs-variable.language_ {
  color: var(--color-prettylights-syntax-keyword);
}

.markdown-body .hljs-title,
.markdown-body .hljs-title.class_,
.markdown-body .hljs-title.class_.inherited__,
.markdown-body .hljs-title.function_ {
  color: var(--color-prettylights-syntax-entity);
}

.markdown-body .hljs-attr,
.markdown-body .hljs-attribute,
.markdown-body .hljs-literal,
.markdown-body .hljs-meta,
.markdown-body .hljs-number,
.markdown-body .hljs-operator,
.markdown-body .hljs-selector-attr,
.markdown-body .hljs-selector-class,
.markdown-body .hljs-selector-id,
.markdown-body .hljs-variable {
  color: var(--color-prettylights-syntax-constant);
}

.markdown-body .hljs-meta .hljs-string,
.markdown-body .hljs-regexp,
.markdown-body .hljs-string {
  color: var(--color-prettylights-syntax-string);
}

.markdown-body .hljs-built_in,
.markdown-body .hljs-symbol {
  color: var(--color-prettylights-syntax-variable);
}

.markdown-body .hljs-code,
.markdown-body .hljs-comment,
.markdown-body .hljs-formula {
  color: var(--color-prettylights-syntax-comment);
}

.markdown-body .hljs-name,
.markdown-body .hljs-quote,
.markdown-body .hljs-selector-pseudo,
.markdown-body .hljs-selector-tag {
  color: var(--color-prettylights-syntax-entity-tag);
}

.markdown-body .hljs-subst {
  color: var(--color-prettylights-syntax-constant);
}

.markdown-body .hljs-section {
  color: var(--color-prettylights-syntax-markup-heading);
  font-weight: bold;
}

.markdown-body .hljs-bullet {
  color: var(--color-prettylights-syntax-markup-list);
}

.markdown-body .hljs-emphasis {
  color: var(--color-prettylights-syntax-constant);
  font-style: italic;
}

.markdown-body .hljs-strong {
  color: var(--color-prettylights-syntax-constant);
  font-weight: bold;
}

.markdown-body .hljs-addition {
  color: var(--color-prettylights-syntax-markup-inserted-text);
  background-color: var(--color-prettylights-syntax-markup-inserted-bg);
}

.markdown-body .hljs-deletion {
  color: var(--color-prettylights-syntax-markup-deleted-text);
  background-color: var(--color-prettylights-syntax-markup-deleted-bg);
}

</style>
        
        <link rel="stylesheet" href="P2616R0_files/markdown.css">
<link rel="stylesheet" href="P2616R0_files/highlight.css">
<style>
            body {
                font-family: -apple-system, BlinkMacSystemFont, 'Segoe WPC', 'Segoe UI', system-ui, 'Ubuntu', 'Droid Sans', sans-serif;
                font-size: 14px;
                line-height: 1.6;
            }
        </style>
        <style>
.task-list-item { list-style-type: none; } .task-list-item-checkbox { margin-left: -20px; vertical-align: middle; }
</style>
        
    </head>
    <body class="vscode-body vscode-light">
        <div class="github-markdown-body github-markdown-auto">
            <div class="github-markdown-content"><table>
<thead>
<tr>
<th>document</th>
<th>title</th>
<th>author</th>
<th>date</th>
<th>target</th>
<th>audience</th>
</tr>
</thead>
<tbody>
<tr>
<td>P2616R0</td>
<td>Making std::atomic notification/wait operations usable in more situations</td>
<td>Lewis Baker <a href="mailto:lewissbaker@gmail.com">lewissbaker@gmail.com</a></td>
<td>2022-07-05</td>
<td>C++26</td>
<td>SG1</td>
</tr>
</tbody>
</table>
<ul>
<li><a href="#abstract">Abstract</a></li>
<li><a href="#motivation">Motivation</a></li>
<li><a href="#option-1---allow-namespace-scope-function-stdatomic_notify_oneall-to-accept-pointer-to-potentially-destroyed-object">Option 1 - Allow namespace-scope function <code>std::atomic_notify_one/all</code> to accept pointer to potentially-destroyed object</a></li>
<li><a href="#option-2---add-fused-store-and-notify-methods-for-each-atomic-operation">Option 2 - Add fused store-and-notify methods for each atomic operation</a>
<ul>
<li><a href="#other-implementations-considered">Other implementations considered</a></li>
</ul>
</li>
<li><a href="#conclusion">Conclusion</a></li>
<li><a href="#references">References</a></li>
</ul>
<h1 id="abstract">Abstract</h1>
<p>C++20 added support to <code>std::atomic</code> for <code>notify_one()</code>, <code>notify_all()</code> and <code>wait()</code> operations.</p>
<p>However, the separation of the notify operation from atomic-store operations makes it difficult
and/or inefficient to use these operations for building certain kinds of synchronisation
primitives (e.g. <code>std::counting_semaphore</code>) in a portable way.</p>
<p>In cases where a thread signalling a synchronisation primitive might release another thread
waiting for that signal which then subsequently destroys the synchronisation primitive
(for example in implementation of the <code>sync_wait()</code> primitive from P2300), a naiive usage of
<code>std::atomic</code> that stores a value and then calls <code>std::atomic::notify_one()</code> might end up
calling <code>notify_one()</code> on a destroyed object if the waiting thread sees the store and
immediately completes without waiting to be notified and destroys the atomic object before
the signalling thread completes the call to <code>notify_one()</code>.</p>
<p>This paper proposes two potential directions for solving this, for consideration by
the concurrency sub-group.</p>
<ol>
<li>Change the semantics of namespace-scope <code>std::atomic_notify_one()</code> and <code>std::atomic_notify_all()</code>
to allow passing a pointer to a <code>std::atomic</code> object whose lifetime may have ended.</li>
<li>For each operation that can result in a store to the atomic variable, add a new overload
that takes a <code>std::memory_notification</code> enum value and that fuses the store operation
with a corresponding notify operation such that the operations are (as if) performed atomically.</li>
</ol>
<p>The first option is arguably a simpler change to the specification and is possibly the preferable
approach if concerns about pointer providence and the potential for undefined-behaviour passing
pointers to destroyed objects can be overcome with core language changes proposed by the "pointer zap"
papers (P1726 and P2188).</p>
<p>The second option is a much larger interface change to <code>std::atomic</code>, involving adding about
20 new overloads to the <code>std::atomic</code> and <code>std::atomic_ref</code> classes and additional overloads
of the namespace-scope atomic functions. However, this option does not have the same requirements
for core language changes and thus can provide a solution independent of core-language changes.</p>
<p>I am seeking guidance from the Concurrency sub-group on the preferred direction for addressing
the issue.</p>
<h1 id="motivation">Motivation</h1>
<p>Consider the following simplified case:</p>
<pre><code class="language-c++"><div><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
  thread_pool tp;

  {
    std::atomic&lt;<span class="hljs-keyword">int</span>&gt; x{<span class="hljs-number">0</span>}; <span class="hljs-comment">// #1</span>
    tp.<span class="hljs-built_in">execute</span>([&amp;] {       <span class="hljs-comment">// #2</span>
      x.<span class="hljs-built_in">store</span>(<span class="hljs-number">1</span>);          <span class="hljs-comment">// #3</span>
      x.<span class="hljs-built_in">notify_one</span>();      <span class="hljs-comment">// #4</span>
    });
    x.<span class="hljs-built_in">wait</span>(<span class="hljs-number">0</span>);             <span class="hljs-comment">// #5</span>
    <span class="hljs-built_in">assert</span>(x.<span class="hljs-built_in">load</span>() == <span class="hljs-number">1</span>);
  }                        <span class="hljs-comment">// #6</span>
}
</div></code></pre>
<p>Let T1 be the thread-pool thread that executes the lambda and T2 be the thread executing <code>main()</code>.</p>
<p>We might end up with a situation where the following sequence of events is executed:</p>
<pre><code><code><div>T2: constructs the atomic `x` and initialises to 0 (#1)
T2: enqueues the task to execute on the T1 (#2)
T1: dequeues the task and invokes the lambda
T1: stores the value 1 to the `x` (#3)
T2: executes `x.wait(0)`, sees value is now 1 and returns immediately (#5)
T2: destroys the object `x` (#6)
T1: executes `x.notify_one()` on a now destroyed object `x` (#4)
</div></code></code></pre>
<p>To work around this potential for operating on a dangling reference, we can use two
separate atomic variables - <code>wait()</code> on one and then spin-wait on the other.</p>
<pre><code class="language-c++"><div><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
  thread_pool tp;

  {
    std::atomic&lt;<span class="hljs-keyword">int</span>&gt; x{<span class="hljs-number">0</span>};
    std::atomic_flag f{<span class="hljs-literal">true</span>};
    tp.<span class="hljs-built_in">execute</span>([&amp;] {
      x.<span class="hljs-built_in">store</span>(<span class="hljs-number">1</span>);
      x.<span class="hljs-built_in">notify_one</span>();
      f.<span class="hljs-built_in">clear</span>();
    });
    x.<span class="hljs-built_in">wait</span>(<span class="hljs-number">0</span>);        <span class="hljs-comment">// efficient-wait</span>
    <span class="hljs-keyword">while</span> (f.<span class="hljs-built_in">test</span>()); <span class="hljs-comment">// spin-wait</span>
    <span class="hljs-built_in">assert</span>(x.<span class="hljs-built_in">load</span>() == <span class="hljs-number">1</span>);
  }
}
</div></code></pre>
<p>This has a couple of downsides, however:</p>
<ul>
<li>It needs to perform two atomic operations instead of one</li>
<li>The spin-wait can still result in occasional long busy-wait times, wasting CPU resources
e.g. if the signalling thread is context-switched out by the OS after the call to
<code>x.notify_one()</code> but before the call to <code>f.clear()</code>, the main thread will busy-wait
spin until the signalling thread is rescheduled.</li>
</ul>
<p>For this particular situation, we could instead use a <code>std::binary_semaphore</code>, as its
<code>acquire()</code> and <code>release()</code> operations are defined as being atomic, and thus they do
not have the same lifetime issues as the first <code>std::atomic</code> implementation.</p>
<pre><code class="language-c++"><div><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
  thread_pool tp;

  {
    std::binary_semaphore sem{<span class="hljs-number">0</span>};
    tp.<span class="hljs-built_in">execute</span>([&amp;] {
      sem.<span class="hljs-built_in">release</span>();
    });
    sem.<span class="hljs-built_in">acquire</span>();
  }   
}
</div></code></pre>
<p>However, if we then ask the question "Can we implement <code>std::counting_semaphore</code> using <code>std::atomic</code>?",
we quickly run into the same lifetime questions regarding use of <code>notify_one/all()</code> methods.</p>
<p>Indeed, if we look at some of the standard library implementations of <code>std::counting_semaphore</code> we
see that they do actually follow the same pattern as above for the <code>release()</code> operation - an atomic
store followed by a call to either <code>notify_all()</code> or <code>notify_one()</code> on the atomic object.</p>
<ul>
<li>See <a href="https://github.com/llvm/llvm-project/blob/643df8fa8ef58d883cbb554c7e71910dc8a8673c/libcxx/include/semaphore#L90-L99">libc++ counting_semaphore::release() implementation</a>.</li>
<li>See <a href="https://github.com/gcc-mirror/gcc/blob/8467574d8daac47e0cf5b694f6c012aad8d630a6/libstdc%2B%2B-v3/include/bits/semaphore_base.h#L248-L259">libstdc++ counting_semaphore::release() implementation</a></li>
<li>See <a href="https://github.com/microsoft/STL/blob/c34f24920e463a71791c2ee3d2bed14926518965/stl/inc/semaphore#L74-L112">msvc counting_semaphore::release() implementation</a></li>
</ul>
<p>So why don't they run into the same lifetime issues?</p>
<p>The reason is that in all major standard library implementations of <code>std::atomic::notify_all()</code> and
<code>std::atomic::notify_one()</code> depend only on the address of the atomic object, but do not actually
access any data-members of the atomic object.</p>
<p>The underlying implementations of these notifying functions typically boil down to a call to:</p>
<ul>
<li><code>WakeByAddressSingle()</code> / <code>WakeByAddressAll()</code> on Windows platforms</li>
<li><code>futex()</code> with <code>futex_op</code> set to <code>FUTEX_WAKE</code> on Linux platforms</li>
<li><code>__ulock_wake()</code> on Mac OS platforms</li>
<li>Use of one of a collection of statically-allocated condition-variables, with the particular one
chosen based on the bit-representation of the provided address.</li>
</ul>
<p>All of these depend only on the bit-representation of address of the atomic variable,
which makes calling <code>count.notify_all()</code> work, even though the <code>count</code> object may have
since been destroyed, as it doesn't try to dereference the pointer.</p>
<p>Standard library implementations have extra knowledge about the semantics of the <code>std::atomic::notify_one/all()</code>
methods which they can leverage to allow safely implementing <code>std::counting_semaphore</code> in terms of
<code>std::atomic</code> operations. However, users of the standard library which want to be portable cannot make
these same assumptions - the standard library does not currently require that all implementations
have this same behaviour, and so users must resort to other, more portable means.</p>
<p>Instead of requiring users to work-around these limitations of the interface, we should provide
some standard/portable way for users to safely modify an atomic value and notify waiting threads
of the change.</p>
<h1 id="option-1---allow-namespace-scope-function-stdatomic_notify_oneall-to-accept-pointer-to-potentially-destroyed-object">Option 1 - Allow namespace-scope function <code>std::atomic_notify_one/all</code> to accept pointer to potentially-destroyed object</h1>
<p>The first option for solving this proposal is to change the semantics of <code>std::atomic_notify_all</code> and
<code>std::atomic_notify_one</code> functions to allow accepting a pointer to a potentially-destroyed object.
i.e. have them guarantee that they will not try to access the object pointed-to by the provided
pointer.</p>
<p>With this change, the above example could be written as follows:</p>
<pre><code class="language-c++"><div><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
  thread_pool tp;

  {
    std::atomic&lt;<span class="hljs-keyword">int</span>&gt; x{<span class="hljs-number">0</span>};
    tp.<span class="hljs-built_in">execute</span>([&amp;] {
      <span class="hljs-keyword">auto</span>* px = &amp;x;
      x.<span class="hljs-built_in">store</span>(<span class="hljs-number">1</span>);
      std::<span class="hljs-built_in">atomic_notify_one</span>(px);
    });
    x.<span class="hljs-built_in">wait</span>(<span class="hljs-number">0</span>);
    <span class="hljs-built_in">assert</span>(x.<span class="hljs-built_in">load</span>() == <span class="hljs-number">1</span>);
  }
}
</div></code></pre>
<p>The example here takes the address of <code>x</code> before executing the <code>x.store(1)</code> operation as the
object <code>x</code> may not be valid after the <code>x.store(1)</code> statement is executed.</p>
<p>However, this is arguably not significantly different to just taking the address of <code>x</code> after
the store. Indeed, the validity of this code depends on whether or not it is valid to use the
pointer <code>px</code> at a point in the program where the pointed-to object is potentially destroyed.</p>
<p>There has been much debate about the semantics of using pointers-to-destroyed objects in the papers:</p>
<ul>
<li><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1726r5.pdf">P1726R5</a> "Pointer lifetime-end zap and provenance, too" (Paul McKenney, Maged Michael, et. al.)</li>
<li><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2188r1.html">P2188R1</a> "Zap the Zap: Pointers are sometimes just bags of bits"</li>
</ul>
<p>The storage duration section <code>[basic.stc.general]</code>, paragraph 4 reads:</p>
<blockquote>
<p>When the end of the duration of a region of storage is reached, the values of all pointers
representing the address of any part of that region of storage become invalid pointer values (<code>[basic.compound]</code>).
Indirection through an invalid pointer value and passing an invalid pointer value to a deallocation
function have undefined behavior. Any other use of an invalid pointer value has implementation-defined behavior. <code>[26]</code>
<code>[26]</code> Some implementations might define that copying an invalid pointer value causes a system-generated
runtime fault.</p>
</blockquote>
<p>Thus, depending on the outcomes of the discussions of these papers, this proposed solution may or may not
be valid. According to the current wording of <code>[basic.stc.general]</code>, passing the pointer to the
<code>std::atomic_notify_one()</code> function has implementation-defined behaviour.</p>
<p>We would need wording changes to <code>[basic.stc.general]</code> that makes it valid to pass the address of
a potentially destroyed object of type <code>std::atomic&lt;T&gt;</code> to  the <code>std::atomic_notify_one/all</code> function.
This may be too big or too wide-ranging a change to add just for enabling <code>std::atomic&lt;T&gt;::notify_one/all</code>
operations.</p>
<h1 id="option-2---add-fused-store-and-notify-methods-for-each-atomic-operation">Option 2 - Add fused store-and-notify methods for each atomic operation</h1>
<p>The alternative to the above approach is to instead define new methods on a <code>std::atomic</code> object
that atomically perform the store + notify steps such that there is no potential for invoking
undefined/implementation-defined behaviour.</p>
<p>The implementation itself may still be taking the address of the object and performing separate
store + notify steps. However, as whether or not this is valid is implementation defined, presumably
the implementation can do this in such a way that it does not invoke the undefined behaviour that
would be involved if user-code were to attempt the same thing.</p>
<p>This option proposes adding the enum <code>memory_notification</code>: and the following overloads to the <code>std::atomic</code> class:</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">namespace</span> std {
  <span class="hljs-class"><span class="hljs-keyword">enum</span> <span class="hljs-keyword">class</span> <span class="hljs-title">memory_notification</span> :</span> unspecified {
    notify_none = unspecified,
    notify_one = unspecified,
    notify_all = unspecified
  };
  <span class="hljs-keyword">inline</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> memory_notify_none = memory_notification::notify_none;
  <span class="hljs-keyword">inline</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> memory_notify_one = memory_notification::notify_one;
  <span class="hljs-keyword">inline</span> <span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">auto</span> memory_notify_all = memory_notification::notify_all;
}
</div></code></pre>
<p>Adding the following overloads to the primary template for <code>std::atomic</code> class:</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">namespace</span> std {
  <span class="hljs-keyword">template</span>&lt;<span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">T</span>&gt;</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">atomic</span> {</span>
    <span class="hljs-comment">// ... existing methods omitted for brevity</span>

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">store</span><span class="hljs-params">(T, memory_notification, memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">store</span><span class="hljs-params">(T, memory_notification, memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">noexcept</span></span>;

    <span class="hljs-function">T <span class="hljs-title">exchange</span><span class="hljs-params">(T, memory_notification, memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function">T <span class="hljs-title">exchange</span><span class="hljs-params">(T, memory_notification, memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">noexcept</span></span>;
    
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_weak</span><span class="hljs-params">(T&amp;, T, memory_notification, memory_order, memory_order)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_weak</span><span class="hljs-params">(T&amp;, T, memory_notification, memory_order, memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_strong</span><span class="hljs-params">(T&amp;, T, memory_notification, memory_order, memory_order)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_strong</span><span class="hljs-params">(T&amp;, T, memory_notification, memory_order, memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;

    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_weak</span><span class="hljs-params">(T&amp;, T, memory_notification,
                               memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_weak</span><span class="hljs-params">(T&amp;, T, memory_notification,
                               memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_strong</span><span class="hljs-params">(T&amp;, T, memory_notification,
                                 memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_strong</span><span class="hljs-params">(T&amp;, T, memory_notification,
                                 memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">noexcept</span></span>;
  };
}
</div></code></pre>
<p>Adding the following methods to specialisations of <code>std::atomic</code> for integral types</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">namespace</span> std {
  <span class="hljs-keyword">template</span>&lt;&gt; <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">atomic</span>&lt;</span>integral&gt; {
    <span class="hljs-comment">// ... existing methods omitted for brevity</span>

    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">store</span><span class="hljs-params">(integral, memory_notification, memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">store</span><span class="hljs-params">(integral, memory_notification, memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">noexcept</span></span>;

    <span class="hljs-function">integral <span class="hljs-title">exchange</span><span class="hljs-params">(integral, memory_notification, memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function">integral <span class="hljs-title">exchange</span><span class="hljs-params">(integral, memory_notification, memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">noexcept</span></span>;

    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_weak</span><span class="hljs-params">(integral&amp;, integral, memory_notification,
                               memory_order, memory_order)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_weak</span><span class="hljs-params">(integral&amp;, integral, memory_notification,
                               memory_order, memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_strong</span><span class="hljs-params">(integral&amp;, integral, memory_notification,
                                 memory_order, memory_order)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_strong</span><span class="hljs-params">(integral&amp;, integral, memory_notification,
                                 memory_order, memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;

    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_weak</span><span class="hljs-params">(integral&amp;, integral, memory_notification,
                               memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_weak</span><span class="hljs-params">(integral&amp;, integral,
                               memory_notification, memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_strong</span><span class="hljs-params">(integral&amp;, integral, memory_notification,
                                 memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">volatile</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-keyword">bool</span> <span class="hljs-title">compare_exchange_strong</span><span class="hljs-params">(integral&amp;, integral, memory_notification,
                                 memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">noexcept</span></span>;

    <span class="hljs-function">integral <span class="hljs-title">fetch_add</span><span class="hljs-params">(integral, memory_notification,
                       memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function">integral <span class="hljs-title">fetch_sub</span><span class="hljs-params">(integral, memory_notification,
                       memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function">integral <span class="hljs-title">fetch_and</span><span class="hljs-params">(integral, memory_notification,
                       memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function">integral <span class="hljs-title">fetch_or</span><span class="hljs-params">(integral, memory_notification,
                      memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function">integral <span class="hljs-title">fetch_xor</span><span class="hljs-params">(integral, memory_notification,
                       memory_order = memory_order::seq_cst)</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">noexcept</span></span>;
  };
</div></code></pre>
<p>Adding similar overloads to <code>std::atomic_ref</code> (omitted for brevity)</p>
<p>Adding the following namespace-scope overloads:</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">namespace</span> std {
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">void</span> <span class="hljs-title">atomic_store</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">void</span> <span class="hljs-title">atomic_store</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">void</span> <span class="hljs-title">atomic_store_explicit</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                               memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">void</span> <span class="hljs-title">atomic_store_explicit</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                               memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_exchange</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_exchange</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_exchange_explicit</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                               memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_exchange_explicit</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                               memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">bool</span> <span class="hljs-title">atomic_compare_exchange_weak</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*,
                                      <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type*,
                                      <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type,
                                      memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">bool</span> <span class="hljs-title">atomic_compare_exchange_weak</span><span class="hljs-params">(atomic&lt;T&gt;*,
                                      <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type*,
                                      <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type,
                                      memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">bool</span> <span class="hljs-title">atomic_compare_exchange_strong</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*,
                                        <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type*,
                                        <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type,
                                        memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">bool</span> <span class="hljs-title">atomic_compare_exchange_strong</span><span class="hljs-params">(atomic&lt;T&gt;*,
                                        <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type*,
                                        <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type,
                                        memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">bool</span> <span class="hljs-title">atomic_compare_exchange_weak_explicit</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*,
                                               <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type*,
                                               <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type,
                                               memory_notification,
                                               memory_order, memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">bool</span> <span class="hljs-title">atomic_compare_exchange_weak_explicit</span><span class="hljs-params">(atomic&lt;T&gt;*,
                                               <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type*,
                                               <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type,
                                               memory_notification,
                                               memory_order, memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">bool</span> <span class="hljs-title">atomic_compare_exchange_strong_explicit</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*,
                                                 <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type*,
                                                 <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type,
                                                 memory_notification,
                                                 memory_order, memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    <span class="hljs-keyword">bool</span> <span class="hljs-title">atomic_compare_exchange_strong_explicit</span><span class="hljs-params">(atomic&lt;T&gt;*,
                                                 <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type*,
                                                 <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type,
                                                 memory_notification,
                                                 memory_order, memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;

  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_add</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::difference_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_add</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::difference_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_add_explicit</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::difference_type, memory_notification,
                                memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_add_explicit</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::difference_type, memory_notification,
                                memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_sub</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::difference_type, memory_notification,)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_sub</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::difference_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_sub_explicit</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::difference_type, memory_notification,
                                memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_sub_explicit</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::difference_type, memory_notification,
                                memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_and</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_and</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_and_explicit</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                                memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_and_explicit</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                                memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_or</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_or</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_or_explicit</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                               memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_or_explicit</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                               memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_xor</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_xor</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_xor_explicit</span><span class="hljs-params">(<span class="hljs-keyword">volatile</span> atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                                memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
  <span class="hljs-function"><span class="hljs-keyword">template</span>&lt;class T&gt;
    T <span class="hljs-title">atomic_fetch_xor_explicit</span><span class="hljs-params">(atomic&lt;T&gt;*, <span class="hljs-keyword">typename</span> atomic&lt;T&gt;::value_type, memory_notification,
                                memory_order)</span> <span class="hljs-keyword">noexcept</span></span>;
}
</div></code></pre>
<p>Where each of the overloads taking a <code>memory_notification</code> is equivalent to executing
the corresponding overload without the <code>memory_notification</code> parameter, but with the
semantic that if the operation stores a value to the atomic variable then it
atomically performs the store and a call to the correspoding <code>notify_one</code> or
<code>notify_all</code> methods (depending on the value of the <code>memory_notification</code> parameter).</p>
<p>For example, the <code>store()</code> overload has the semantics of performing the following
body as if performed atomically.</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-keyword">void</span> atomic&lt;T&gt;::<span class="hljs-built_in">store</span>(T value, memory_notification notify, memory_order order) <span class="hljs-keyword">noexcept</span> {
    <span class="hljs-comment">// As if it atomically performs the following operations</span>
    <span class="hljs-built_in">store</span>(value, order);
    <span class="hljs-built_in"><span class="hljs-keyword">switch</span></span>(notify) {
    <span class="hljs-keyword">case</span> memory_notification::notify_one: <span class="hljs-built_in">notify_one</span>(); <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> memory_notification::notify_all: <span class="hljs-built_in">notify_all</span>(); <span class="hljs-keyword">break</span>;
    }
}
</div></code></pre>
<p>And for the <code>compare_exchange_strong()</code> operation, it would atomically perform the
notification if the compare-exchange operation succeeds.</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> T&gt;
<span class="hljs-keyword">bool</span> atomic&lt;T&gt;::<span class="hljs-built_in">compare_exchange_strong</span>(T&amp; old_val, T new_val, memory_notification notify,
                                        memory_order mo_success, memory_order mo_failure) <span class="hljs-keyword">noexcept</span> {
    <span class="hljs-comment">// As if it atomically performs the following operations</span>
    <span class="hljs-keyword">bool</span> result = <span class="hljs-built_in">compare_exchange_strong</span>(old_val, new_val, mo_success, mo_failure);
    <span class="hljs-keyword">if</span> (result) {
        <span class="hljs-built_in"><span class="hljs-keyword">switch</span></span> (notify) {
        <span class="hljs-keyword">case</span> memory_notification::notify_one: <span class="hljs-built_in">notify_one</span>(); <span class="hljs-keyword">break</span>;
        <span class="hljs-keyword">case</span> memory_notification::notify_all: <span class="hljs-built_in">notify_all</span>(); <span class="hljs-keyword">break</span>;
        }
    }
    <span class="hljs-keyword">return</span> result;
}
</div></code></pre>
<p>In practice, existing implementations should be able to call through to the underlying OS
syscall after performing the atomic operation (i.e. call one of <code>WakeByAddressSingle/All()</code>
or <code>futex()</code> depending on the platform), or on platforms without the OS primitives, in
terms of operations on statically-allocated synchronisation state (e.g. using a mutex/condition-variable).</p>
<p>For example: On Windows 8 or later</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">template</span>&lt;&gt;
<span class="hljs-keyword">void</span> atomic&lt;<span class="hljs-keyword">int</span>&gt;::<span class="hljs-built_in">store</span>(<span class="hljs-keyword">int</span> value, memory_notification notify, memory_order order) <span class="hljs-keyword">noexcept</span> {
    <span class="hljs-keyword">void</span>* address = <span class="hljs-keyword">static_cast</span>&lt;<span class="hljs-keyword">void</span>*&gt;(<span class="hljs-keyword">this</span>);
    <span class="hljs-built_in">store</span>(value, order);
    <span class="hljs-built_in"><span class="hljs-keyword">switch</span></span> (notify) {
    <span class="hljs-keyword">case</span> memory_notification::notify_one: <span class="hljs-built_in">WakeByAddressSingle</span>(address); <span class="hljs-keyword">break</span>;
    <span class="hljs-keyword">case</span> memory_notification::notify_all: <span class="hljs-built_in">WakeByAddressAll</span>(address); <span class="hljs-keyword">break</span>;
    }
}
</div></code></pre>
<p>For example: On Linux 2.6.22 or later</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">template</span>&lt;&gt;
<span class="hljs-keyword">void</span> atomic&lt;<span class="hljs-keyword">int</span>&gt;::<span class="hljs-built_in">store</span>(<span class="hljs-keyword">int</span> value, memory_notification notify, memory_order order) <span class="hljs-keyword">noexcept</span> {
    <span class="hljs-keyword">void</span>* address = <span class="hljs-keyword">static_cast</span>&lt;<span class="hljs-keyword">void</span>*&gt;(<span class="hljs-keyword">this</span>);
    <span class="hljs-built_in">store</span>(value, order);
    <span class="hljs-keyword">if</span> (notify != memory_notification_none) {
        (<span class="hljs-keyword">void</span>)<span class="hljs-built_in">syscall</span>(SYS_futex, address, FUTEX_WAKE_PRIVATE,
                      notify == memory_notification_one ? <span class="hljs-number">1</span> : INT_MAX);
    }
}
</div></code></pre>
<p>Note that the above implementations still rely on the address being valid to copy and
pass as a parameter to the syscall, even after the <code>store()</code> has potentially caused the
address to become invalid due to the object being destroyed on another thread.
However, whether or not this is valid is implementation defined and so presumably
implementations that take this approach can ensure this is indeed valid.</p>
<p>For example: On platforms without native OS support for address-based notification</p>
<pre><code class="language-c++"><div><span class="hljs-class"><span class="hljs-keyword">struct</span> __<span class="hljs-title">wait_state</span> {</span>
    std::atomic&lt;<span class="hljs-keyword">uint64_t</span>&gt; _M_waiters{<span class="hljs-number">0</span>};
    std::mutex _M_mut;
    std::condition_variable _M_cv;
    std::<span class="hljs-keyword">uint64_t</span> _M_version{<span class="hljs-number">0</span>};

    <span class="hljs-keyword">static</span> __wait_state&amp; __for_address(<span class="hljs-keyword">void</span>* __address) <span class="hljs-keyword">noexcept</span> {
        <span class="hljs-keyword">constexpr</span> std::<span class="hljs-keyword">uintptr_t</span> __count = <span class="hljs-number">16</span>;
        <span class="hljs-keyword">static</span> __wait_state __w[__count];
        <span class="hljs-keyword">auto</span> __key = (<span class="hljs-keyword">reinterpret_cast</span>&lt;std::<span class="hljs-keyword">uintptr_t</span>&gt;(__address) &gt;&gt; <span class="hljs-number">2</span>) % __count;
        <span class="hljs-keyword">return</span> __w[__key];
    }

    <span class="hljs-keyword">void</span> __notify() <span class="hljs-keyword">noexcept</span> {
        <span class="hljs-keyword">if</span> (_M_waiters.<span class="hljs-built_in">load</span>() != <span class="hljs-number">0</span>) {
            {
                std::lock_guard __lk{_M_mut};
                ++_M_version;
            }
            _M_cv.<span class="hljs-built_in">notify_all</span>();
        }
    }

    <span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> _Pred&gt;
    <span class="hljs-keyword">void</span> __wait(_Pred __pred) <span class="hljs-keyword">noexcept</span> {
        <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> __i = <span class="hljs-number">0</span>; __i &lt; <span class="hljs-number">10</span>; ++__i) {
            <span class="hljs-keyword">if</span> (__pred()) <span class="hljs-keyword">return</span>;
            __yield_thread();
        }

        _M_waiters.<span class="hljs-built_in">fetch_add</span>(<span class="hljs-number">1</span>, std::memory_order_seq_cst);
        std::<span class="hljs-keyword">uint64_t</span> __prev_version = [&amp;] {
            std::unique_lock __lk{_M_mut};
            <span class="hljs-keyword">return</span> _M_version;
        }();
        <span class="hljs-keyword">while</span> (!__pred()) {
            std::unique_lock __lk{_M_mut};
            <span class="hljs-keyword">if</span> (_M_version == __prev_version) {
                _M_cv.<span class="hljs-built_in">wait</span>(__lk);
            }
            __prev_version = _M_version;
        }
        _M_waiters.<span class="hljs-built_in">fetch_sub</span>(<span class="hljs-number">1</span>, std::memory_order_release);
    }
}

<span class="hljs-keyword">template</span>&lt;&gt;
<span class="hljs-keyword">void</span> atomic&lt;<span class="hljs-keyword">int</span>&gt;::<span class="hljs-built_in">store</span>(<span class="hljs-keyword">int</span> __value, memory_notification __notify, memory_order __mo) <span class="hljs-keyword">noexcept</span> {
    <span class="hljs-keyword">auto</span>&amp; __w = __wait_state::__for_address(<span class="hljs-keyword">this</span>);
    <span class="hljs-built_in">store</span>(__value, __mo);
    <span class="hljs-keyword">if</span> (__notify != memory_notification_none) {
        __w.__notify();
    }
}

<span class="hljs-keyword">template</span>&lt;&gt;
<span class="hljs-keyword">void</span> atomic&lt;<span class="hljs-keyword">int</span>&gt;::<span class="hljs-built_in">wait</span>(<span class="hljs-keyword">int</span> __old, memory_order __mo) <span class="hljs-keyword">noexcept</span> {
    <span class="hljs-keyword">auto</span> __pred = [__mo, __old, <span class="hljs-keyword">this</span>]() <span class="hljs-keyword">noexcept</span> { <span class="hljs-keyword">return</span> <span class="hljs-keyword">this</span>-&gt;<span class="hljs-built_in">load</span>(__mo) != __old; };
    <span class="hljs-keyword">auto</span>&amp; __w = __wait_state::__for_address(<span class="hljs-keyword">this</span>);
    __w.<span class="hljs-built_in">wait</span>(__pred);
}
</div></code></pre>
<h2 id="other-implementations-considered">Other implementations considered</h2>
<p>Other variations on option 2 were considered and rejected.</p>
<p>Instead of adding overloads of existing function names, we could have added separately named
functions. e.g. <code>store_and_notify_one</code> and <code>store_and_notify_all</code>.</p>
<p>This was rejected as it adds a large number of new names to the <code>std::atomic</code> interface.
It also makes it more difficult to conditionally either notify-one or notify-all.</p>
<p>e.g. An implementation of <code>std::counting_semaphore::release()</code> can be the following if
using the enum-based overload solution:</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">template</span>&lt;std::<span class="hljs-keyword">ptrdiff_t</span> LeastMaxValue&gt;
<span class="hljs-keyword">void</span> std::counting_semaphore&lt;LeastMaxValue&gt;::<span class="hljs-built_in">release</span>(<span class="hljs-keyword">ptrdiff_t</span> update) {
    counter_.<span class="hljs-built_in">fetch_add</span>(update,
                       (update == <span class="hljs-number">1</span>) ? memory_notify_one : memory_notify_all,
                       memory_order_release);
}
</div></code></pre>
<p>whereas with separate names you would need to write:</p>
<pre><code class="language-c++"><div><span class="hljs-keyword">template</span>&lt;std::<span class="hljs-keyword">ptrdiff_t</span> LeastMaxValue&gt;
<span class="hljs-keyword">void</span> std::counting_semaphore&lt;LeastMaxValue&gt;::<span class="hljs-built_in">release</span>(<span class="hljs-keyword">ptrdiff_t</span> update) {
    <span class="hljs-keyword">if</span> (update == <span class="hljs-number">1</span>) {
        counter_.<span class="hljs-built_in">fetch_add_and_notify_one</span>(update, memory_order_release);
    } <span class="hljs-keyword">else</span> {
        counter_.<span class="hljs-built_in">fetch_add_and_notify_all</span>(update, memory_order_release);
    }
}
</div></code></pre>
<h1 id="conclusion">Conclusion</h1>
<p>The current separation of <code>std::atomic::notify_one/all</code> from the store operation makes it difficult/impossible
to use efficiently for scenarios where a preceding write to the atomic object may cause the atomic object to
be destroyed.</p>
<p>This limits the ability for users to implement synchronisation primitives like <code>std::counting_semaphore</code> in
terms of <code>std::atomic</code> without relying on implementation-specific behaviour.</p>
<p>We can either pursue language improvements that make it valid to call (some of) the existing notifying functions
with the address of a potentially destroyed atomic object, or we can pursue a library solution that provides
new overloads to atomic operations that atomically store a value and notify waiting threads so that we avoid
the lifetime issues for</p>
<p>I am seeking guidance from the Concurrency sub-group on the preferred direction and can produce proposed
wording in a subsequent revision based on this guidance.</p>
<h1 id="references">References</h1>
<p>SG1 mailing list thread on the issue
<a href="https://lists.isocpp.org/parallel/2020/07/3270.php">https://lists.isocpp.org/parallel/2020/07/3270.php</a></p>
<p>P1726R5 - "Pointer lifetime-end zap and provenance, too" (Paul McKenney, Maged Michael, et. al.)
<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1726r5.pdf">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1726r5.pdf</a></p>
<p>P2188R1 - "Zap the Zap: Pointers are sometimes just bags of bits"
<a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2188r1.html">https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2188r1.html</a></p>
</div>
        </div>
        
        
    
    </body></html>