<!DOCTYPE html>

<html lang="en">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black">
    <meta name="mobile-web-app-capable" content="yes">
    <title>
        A Boring Thread Attributes Interface - HackMD
    </title>
    <link rel="icon" type="image/png" href="https://hackmd.io/favicon.png">
    <link rel="apple-touch-icon" href="https://hackmd.io/apple-touch-icon.png">

    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha256-916EbMg70RQy9LHiGkXzG8hSg9EdNy97GazNG/aiY1w=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css" integrity="sha256-eZrrJcwDc/3uDhsdt61sL2oOBY362qM3lon1gyExkL0=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ionicons/2.0.1/css/ionicons.min.css" integrity="sha256-3iu9jgsy9TpTwXKb7bNQzqWekRX7pPK+2OLj3R922fo=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/octicons/3.5.0/octicons.min.css" integrity="sha256-QiWfLIsCT02Sdwkogf6YMiQlj4NE84MKkzEMkZnMGdg=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.5.1/themes/prism.min.css" integrity="sha256-vtR0hSWRc3Tb26iuN2oZHt3KRUomwTufNIf5/4oeCyg=" crossorigin="anonymous" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@hackmd/emojify.js@2.1.0/dist/css/basic/emojify.min.css" integrity="sha256-UOrvMOsSDSrW6szVLe8ZDZezBxh5IoIfgTwdNDgTjiU=" crossorigin="anonymous" />
    <style>
        @import url(https://fonts.googleapis.com/css?family=Roboto:300,300i,400,400i,500,500i|Source+Code+Pro:300,400,500|Source+Sans+Pro:300,300i,400,400i,600,600i|Source+Serif+Pro&subset=latin-ext);.hljs{background:#fff;color:#333;display:block;overflow-x:auto;padding:.5em}.hljs-comment,.hljs-meta{color:#969896}.hljs-emphasis,.hljs-quote,.hljs-string,.hljs-strong,.hljs-template-variable,.hljs-variable{color:#df5000}.hljs-keyword,.hljs-selector-tag,.hljs-type{color:#a71d5d}.hljs-attribute,.hljs-bullet,.hljs-literal,.hljs-number,.hljs-symbol{color:#0086b3}.hljs-built_in,.hljs-builtin-name{color:#005cc5}.hljs-name,.hljs-section{color:#63a35c}.hljs-tag{color:#333}.hljs-attr,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-id,.hljs-selector-pseudo,.hljs-title{color:#795da3}.hljs-addition{background-color:#eaffea;color:#55a532}.hljs-deletion{background-color:#ffecec;color:#bd2c00}.hljs-link{text-decoration:underline}.markdown-body{word-wrap:break-word;font-size:16px;line-height:1.5}.markdown-body:after,.markdown-body:before{content:"";display:table}.markdown-body:after{clear:both}.markdown-body>:first-child{margin-top:0!important}.markdown-body>:last-child{margin-bottom:0!important}.markdown-body a:not([href]){color:inherit;text-decoration:none}.markdown-body .absent{color:#c00}.markdown-body .anchor{float:left;line-height:1;margin-left:-20px;padding-right:4px}.markdown-body .anchor:focus{outline:none}.markdown-body blockquote,.markdown-body dl,.markdown-body ol,.markdown-body p,.markdown-body pre,.markdown-body table,.markdown-body ul{margin-bottom:16px;margin-top:0}.markdown-body hr{background-color:#e7e7e7;border:0;height:.25em;margin:24px 0;padding:0}.markdown-body blockquote{border-left:.25em solid #ddd;color:#777;font-size:16px;padding:0 1em}.markdown-body blockquote>:first-child{margin-top:0}.markdown-body blockquote>:last-child{margin-bottom:0}.markdown-body kbd,.popover kbd{background-color:#fcfcfc;border:1px solid;border-color:#ccc #ccc #bbb;border-radius:3px;box-shadow:inset 0 -1px 0 #bbb;color:#555;display:inline-block;font-size:11px;line-height:10px;padding:3px 5px;vertical-align:middle}.markdown-body .loweralpha{list-style-type:lower-alpha}.markdown-body h1,.markdown-body h2,.markdown-body h3,.markdown-body h4,.markdown-body h5,.markdown-body h6{font-weight:600;line-height:1.25;margin-bottom:16px;margin-top:24px}.markdown-body h1 .octicon-link,.markdown-body h2 .octicon-link,.markdown-body h3 .octicon-link,.markdown-body h4 .octicon-link,.markdown-body h5 .octicon-link,.markdown-body h6 .octicon-link{color:#000;vertical-align:middle;visibility:hidden}.markdown-body h1:hover .anchor,.markdown-body h2:hover .anchor,.markdown-body h3:hover .anchor,.markdown-body h4:hover .anchor,.markdown-body h5:hover .anchor,.markdown-body h6:hover .anchor{text-decoration:none}.markdown-body h1:hover .anchor .octicon-link,.markdown-body h2:hover .anchor .octicon-link,.markdown-body h3:hover .anchor .octicon-link,.markdown-body h4:hover .anchor .octicon-link,.markdown-body h5:hover .anchor .octicon-link,.markdown-body h6:hover .anchor .octicon-link{visibility:visible}.markdown-body h1 code,.markdown-body h1 tt,.markdown-body h2 code,.markdown-body h2 tt,.markdown-body h3 code,.markdown-body h3 tt,.markdown-body h4 code,.markdown-body h4 tt,.markdown-body h5 code,.markdown-body h5 tt,.markdown-body h6 code,.markdown-body h6 tt{font-size:inherit}.markdown-body h1{font-size:2em}.markdown-body h1,.markdown-body h2{border-bottom:1px solid #eee;padding-bottom:.3em}.markdown-body h2{font-size:1.5em}.markdown-body h3{font-size:1.25em}.markdown-body h4{font-size:1em}.markdown-body h5{font-size:.875em}.markdown-body h6{color:#777;font-size:.85em}.markdown-body ol,.markdown-body ul{padding-left:2em}.markdown-body ol.no-list,.markdown-body ul.no-list{list-style-type:none;padding:0}.markdown-body ol ol,.markdown-body ol ul,.markdown-body ul ol,.markdown-body ul ul{margin-bottom:0;margin-top:0}.markdown-body li>p{margin-top:16px}.markdown-body li+li{padding-top:.25em}.markdown-body dl{padding:0}.markdown-body dl dt{font-size:1em;font-style:italic;font-weight:700;margin-top:16px;padding:0}.markdown-body dl dd{margin-bottom:16px;padding:0 16px}.markdown-body table{display:block;overflow:auto;width:100%;word-break:normal;word-break:keep-all}.markdown-body table th{font-weight:700}.markdown-body table td,.markdown-body table th{border:1px solid #ddd;padding:6px 13px}.markdown-body table tr{background-color:#fff;border-top:1px solid #ccc}.markdown-body table tr:nth-child(2n){background-color:#f8f8f8}.markdown-body img{background-color:#fff;box-sizing:initial;max-width:100%}.markdown-body img[align=right]{padding-left:20px}.markdown-body img[align=left]{padding-right:20px}.markdown-body .emoji{background-color:initial;max-width:none;vertical-align:text-top}.markdown-body span.frame{display:block;overflow:hidden}.markdown-body span.frame>span{border:1px solid #ddd;display:block;float:left;margin:13px 0 0;overflow:hidden;padding:7px;width:auto}.markdown-body span.frame span img{display:block;float:left}.markdown-body span.frame span span{clear:both;color:#333;display:block;padding:5px 0 0}.markdown-body span.align-center{clear:both;display:block;overflow:hidden}.markdown-body span.align-center>span{display:block;margin:13px auto 0;overflow:hidden;text-align:center}.markdown-body span.align-center span img{margin:0 auto;text-align:center}.markdown-body span.align-right{clear:both;display:block;overflow:hidden}.markdown-body span.align-right>span{display:block;margin:13px 0 0;overflow:hidden;text-align:right}.markdown-body span.align-right span img{margin:0;text-align:right}.markdown-body span.float-left{display:block;float:left;margin-right:13px;overflow:hidden}.markdown-body span.float-left span{margin:13px 0 0}.markdown-body span.float-right{display:block;float:right;margin-left:13px;overflow:hidden}.markdown-body span.float-right>span{display:block;margin:13px auto 0;overflow:hidden;text-align:right}.markdown-body code,.markdown-body tt{background-color:#0000000a;border-radius:3px;font-size:85%;margin:0;padding:.2em 0}.markdown-body code:after,.markdown-body code:before,.markdown-body tt:after,.markdown-body tt:before{content:"\00a0";letter-spacing:-.2em}.markdown-body code br,.markdown-body tt br{display:none}.markdown-body del code{text-decoration:inherit}.markdown-body pre{word-wrap:normal}.markdown-body pre>code{background:#0000;border:0;font-size:100%;margin:0;padding:0;white-space:pre;word-break:normal}.markdown-body .highlight{margin-bottom:16px}.markdown-body .highlight pre{margin-bottom:0;word-break:normal}.markdown-body .highlight pre,.markdown-body pre{border-radius:3px;font-size:85%;line-height:1.45;overflow:auto}.markdown-body:not(.next-editor) pre{background-color:#f7f7f7;padding:16px}.markdown-body pre code,.markdown-body pre tt{word-wrap:normal;background-color:initial;border:0;display:inline;line-height:inherit;margin:0;max-width:auto;overflow:visible;padding:0}.markdown-body pre code:after,.markdown-body pre code:before,.markdown-body pre tt:after,.markdown-body pre tt:before{content:normal}.markdown-body .csv-data td,.markdown-body .csv-data th{font-size:12px;line-height:1;overflow:hidden;padding:5px;text-align:left;white-space:nowrap}.markdown-body .csv-data .blob-line-num{background:#fff;border:0;padding:10px 8px 9px;text-align:right}.markdown-body .csv-data tr{border-top:0}.markdown-body .csv-data th{background:#f8f8f8;border-top:0;font-weight:700}.news .alert .markdown-body blockquote{border:0;padding:0 0 0 40px}.activity-tab .news .alert .commits,.activity-tab .news .markdown-body blockquote{padding-left:0}.task-list-item{list-style-type:none}.task-list-item label{font-weight:400}.task-list-item.enabled label{cursor:pointer}.task-list-item+.task-list-item{margin-top:3px}.task-list-item-checkbox{cursor:default!important;float:left;margin:.31em 0 .2em -1.3em!important;vertical-align:middle}.markdown-body{max-width:758px;overflow:visible!important;padding-bottom:40px;padding-top:40px;position:relative}.markdown-body.next-editor{overflow-x:hidden!important}.markdown-body .emoji{vertical-align:top}.markdown-body pre{border:inherit!important}.markdown-body code{color:inherit!important}.markdown-body pre code .wrapper{display:-moz-inline-flex;display:-ms-inline-flex;display:-o-inline-flex;display:inline-flex}.markdown-body pre code .gutter{float:left;overflow:hidden;-webkit-user-select:none;user-select:none}.markdown-body pre code .gutter.linenumber{border-right:3px solid #6ce26c!important;box-sizing:initial;color:#afafaf!important;cursor:default;display:inline-block;min-width:20px;padding:0 8px 0 0;position:relative;text-align:right;z-index:4}.markdown-body pre code .gutter.linenumber>span:before{content:attr(data-linenumber)}.markdown-body pre code .code{float:left;margin:0 0 0 16px}.markdown-body .gist .line-numbers{border-bottom:none;border-left:none;border-top:none}.markdown-body .gist .line-data{border:none}.markdown-body .gist table{border-collapse:inherit!important;border-spacing:0}.markdown-body code[data-gist-id]{background:none;padding:0}.markdown-body code[data-gist-id]:after,.markdown-body code[data-gist-id]:before{content:""}.markdown-body code[data-gist-id] .blob-num{border:unset}.markdown-body code[data-gist-id] table{margin-bottom:unset;overflow:unset}.markdown-body code[data-gist-id] table tr{background:unset}.markdown-body[dir=rtl] pre{direction:ltr}.markdown-body[dir=rtl] code{direction:ltr;unicode-bidi:embed}.markdown-body .alert>p:last-child{margin-bottom:0}.markdown-body pre.abc,.markdown-body pre.flow-chart,.markdown-body pre.graphviz,.markdown-body pre.mermaid,.markdown-body pre.sequence-diagram,.markdown-body pre.vega{background-color:inherit;border-radius:0;overflow:visible;text-align:center;white-space:inherit}.markdown-body pre.abc>code,.markdown-body pre.flow-chart>code,.markdown-body pre.graphviz>code,.markdown-body pre.mermaid>code,.markdown-body pre.sequence-diagram>code,.markdown-body pre.vega>code{text-align:left}.markdown-body pre.abc>svg,.markdown-body pre.flow-chart>svg,.markdown-body pre.graphviz>svg,.markdown-body pre.mermaid>svg,.markdown-body pre.sequence-diagram>svg,.markdown-body pre.vega>svg{height:100%;max-width:100%}.markdown-body pre>code.wrap{word-wrap:break-word;white-space:pre-wrap;white-space:-moz-pre-wrap;white-space:-pre-wrap;white-space:-o-pre-wrap}.markdown-body .alert>p:last-child,.markdown-body .alert>ul:last-child{margin-bottom:0}.markdown-body summary{display:list-item}.markdown-body summary:focus{outline:none}.markdown-body details summary{cursor:pointer}.markdown-body details:not([open])>:not(summary){display:none}.markdown-body figure{margin:1em 40px}.markdown-body .mark,.markdown-body mark{background-color:#fff1a7}.vimeo,.youtube{background-color:#000;background-position:50%;background-repeat:no-repeat;background-size:contain;cursor:pointer;display:table;overflow:hidden;text-align:center}.vimeo,.youtube{position:relative;width:100%}.youtube{padding-bottom:56.25%}.vimeo img{object-fit:contain;width:100%;z-index:0}.youtube img{object-fit:cover;z-index:0}.vimeo iframe,.youtube iframe,.youtube img{height:100%;left:0;position:absolute;top:0;width:100%}.vimeo iframe,.youtube iframe{vertical-align:middle;z-index:1}.vimeo .icon,.youtube .icon{color:#fff;height:auto;left:50%;opacity:.3;position:absolute;top:50%;transform:translate(-50%,-50%);transition:opacity .2s;width:auto;z-index:0}.vimeo:hover .icon,.youtube:hover .icon{opacity:.6;transition:opacity .2s}.slideshare .inner,.speakerdeck .inner{position:relative;width:100%}.slideshare .inner iframe,.speakerdeck .inner iframe{bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%}.figma{display:table;padding-bottom:56.25%;position:relative;width:100%}.figma iframe{border:1px solid #eee;bottom:0;height:100%;left:0;position:absolute;right:0;top:0;width:100%}.markmap-container{height:300px}.markmap-container>svg{height:100%;width:100%}.MJX_Assistive_MathML{display:none}#MathJax_Message{z-index:1000!important}.ui-infobar{color:#777;margin:25px auto -25px;max-width:760px;position:relative;z-index:2}.toc .invisable-node{list-style-type:none}.ui-toc{bottom:20px;position:fixed;z-index:998}.ui-toc.both-mode{margin-left:8px}.ui-toc.both-mode .ui-toc-label{border-bottom-left-radius:0;border-top-left-radius:0;height:40px;padding:10px 4px}.ui-toc-label{background-color:#e6e6e6;border:none;color:#868686;transition:opacity .2s}.ui-toc .open .ui-toc-label{color:#fff;opacity:1;transition:opacity .2s}.ui-toc-label:focus{background-color:#ccc;color:#000;opacity:.3}.ui-toc-label:hover{background-color:#ccc;opacity:1;transition:opacity .2s}.ui-toc-dropdown{margin-bottom:20px;margin-top:20px;max-height:70vh;max-width:45vw;overflow:auto;padding-left:10px;padding-right:10px;text-align:inherit;width:25vw}.ui-toc-dropdown>.toc{max-height:calc(70vh - 100px);overflow:auto}.ui-toc-dropdown[dir=rtl] .nav{letter-spacing:.0029em;padding-right:0}.ui-toc-dropdown a{overflow:hidden;text-overflow:ellipsis;white-space:pre}.ui-toc-dropdown .nav>li>a{color:#767676;display:block;font-size:13px;font-weight:500;padding:4px 20px}.ui-toc-dropdown .nav>li:first-child:last-child>ul,.ui-toc-dropdown .toc.expand ul{display:block}.ui-toc-dropdown .nav>li>a:focus,.ui-toc-dropdown .nav>li>a:hover{background-color:initial;border-left:1px solid #000;color:#000;padding-left:19px;text-decoration:none}.ui-toc-dropdown[dir=rtl] .nav>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav>li>a:hover{border-left:none;border-right:1px solid #000;padding-right:19px}.ui-toc-dropdown .nav>.active:focus>a,.ui-toc-dropdown .nav>.active:hover>a,.ui-toc-dropdown .nav>.active>a{background-color:initial;border-left:2px solid #000;color:#000;font-weight:700;padding-left:18px}.ui-toc-dropdown[dir=rtl] .nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav>.active>a{border-left:none;border-right:2px solid #000;padding-right:18px}.ui-toc-dropdown .nav .nav{display:none;padding-bottom:10px}.ui-toc-dropdown .nav>.active>ul{display:block}.ui-toc-dropdown .nav .nav>li>a{font-size:12px;font-weight:400;padding-bottom:1px;padding-left:30px;padding-top:1px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>a{padding-right:30px}.ui-toc-dropdown .nav .nav>li>ul>li>a{font-size:12px;font-weight:400;padding-bottom:1px;padding-left:40px;padding-top:1px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a{padding-right:40px}.ui-toc-dropdown .nav .nav>li>a:focus,.ui-toc-dropdown .nav .nav>li>a:hover{padding-left:29px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav .nav>li>a:hover{padding-right:29px}.ui-toc-dropdown .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown .nav .nav>li>ul>li>a:hover{padding-left:39px}.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a:focus,.ui-toc-dropdown[dir=rtl] .nav .nav>li>ul>li>a:hover{padding-right:39px}.ui-toc-dropdown .nav .nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>a{font-weight:500;padding-left:28px}.ui-toc-dropdown[dir=rtl] .nav .nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>a{padding-right:28px}.ui-toc-dropdown .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown .nav .nav>.active>.nav>.active>a{font-weight:500;padding-left:38px}.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active:focus>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active:hover>a,.ui-toc-dropdown[dir=rtl] .nav .nav>.active>.nav>.active>a{padding-right:38px}.markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang^=ja] .markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,ＭＳ ゴシック,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang=zh-tw] .markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang=zh-cn] .markdown-body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html .markdown-body[lang^=ja]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,ＭＳ ゴシック,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html .markdown-body[lang=zh-tw]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html .markdown-body[lang=zh-cn]{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica Neue,Helvetica,Roboto,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol}html[lang^=ja] .ui-toc-dropdown{font-family:Source Sans Pro,Helvetica,Arial,Meiryo UI,MS PGothic,ＭＳ Ｐゴシック,sans-serif}html[lang=zh-tw] .ui-toc-dropdown{font-family:Source Sans Pro,Helvetica,Arial,Microsoft JhengHei UI,微軟正黑UI,sans-serif}html[lang=zh-cn] .ui-toc-dropdown{font-family:Source Sans Pro,Helvetica,Arial,Microsoft YaHei UI,微软雅黑UI,sans-serif}html .ui-toc-dropdown[lang^=ja]{font-family:Source Sans Pro,Helvetica,Arial,Meiryo UI,MS PGothic,ＭＳ Ｐゴシック,sans-serif}html .ui-toc-dropdown[lang=zh-tw]{font-family:Source Sans Pro,Helvetica,Arial,Microsoft JhengHei UI,微軟正黑UI,sans-serif}html .ui-toc-dropdown[lang=zh-cn]{font-family:Source Sans Pro,Helvetica,Arial,Microsoft YaHei UI,微软雅黑UI,sans-serif}.ui-affix-toc{max-height:70vh;max-width:15vw;overflow:auto;position:fixed;top:0}.back-to-top,.expand-toggle,.go-to-bottom{color:#999;display:block;font-size:12px;font-weight:500;margin-left:10px;margin-top:10px;padding:4px 10px}.back-to-top:focus,.back-to-top:hover,.expand-toggle:focus,.expand-toggle:hover,.go-to-bottom:focus,.go-to-bottom:hover{color:#563d7c;text-decoration:none}.back-to-top,.go-to-bottom{margin-top:0}.ui-user-icon{background-position:50%;background-repeat:no-repeat;background-size:cover;border-radius:50%;display:block;height:20px;margin-bottom:2px;margin-right:5px;margin-top:2px;width:20px}.ui-user-icon.small{display:inline-block;height:18px;margin:0 0 .2em;vertical-align:middle;width:18px}.ui-infobar>small>span{line-height:22px}.ui-infobar>small .dropdown{display:inline-block}.ui-infobar>small .dropdown a:focus,.ui-infobar>small .dropdown a:hover{text-decoration:none}.ui-more-info{color:#888;cursor:pointer;vertical-align:middle}.ui-more-info .fa{font-size:16px}.ui-connectedGithub,.ui-published-note{color:#888}.ui-connectedGithub{line-height:23px;white-space:nowrap}.ui-connectedGithub a.file-path{color:#888;padding-left:22px;text-decoration:none}.ui-connectedGithub a.file-path:active,.ui-connectedGithub a.file-path:hover{color:#888;text-decoration:underline}.ui-connectedGithub .fa{font-size:20px}.ui-published-note .fa{font-size:20px;vertical-align:top}.unselectable{-webkit-user-select:none;-o-user-select:none;user-select:none}.selectable{-webkit-user-select:text;-o-user-select:text;user-select:text}.inline-spoiler-section{cursor:pointer}.inline-spoiler-section .spoiler-text{background-color:#333;border-radius:2px}.inline-spoiler-section .spoiler-text>*{opacity:0}.inline-spoiler-section .spoiler-img{filter:blur(10px)}.inline-spoiler-section.raw{background-color:#333;border-radius:2px}.inline-spoiler-section.raw>*{opacity:0}.inline-spoiler-section.unveil{cursor:auto}.inline-spoiler-section.unveil .spoiler-text{background-color:#3333331a}.inline-spoiler-section.unveil .spoiler-text>*{opacity:1}.inline-spoiler-section.unveil .spoiler-img{filter:none}@media print{blockquote,div,img,pre,table{page-break-inside:avoid!important}a[href]:after{font-size:12px!important}}.markdown-body.slides{color:#222;position:relative;z-index:1}.markdown-body.slides:before{background-color:currentColor;bottom:0;box-shadow:0 0 0 50vw;content:"";display:block;left:0;position:absolute;right:0;top:0;z-index:-1}.markdown-body.slides section[data-markdown]{background-color:#fff;margin-bottom:1.5em;position:relative;text-align:center}.markdown-body.slides section[data-markdown] code{text-align:left}.markdown-body.slides section[data-markdown]:before{content:"";display:block;padding-bottom:56.23%}.markdown-body.slides section[data-markdown]>div:first-child{left:1em;max-height:100%;overflow:hidden;position:absolute;right:1em;top:50%;transform:translateY(-50%)}.markdown-body.slides section[data-markdown]>ul{display:inline-block}.markdown-body.slides>section>section+section:after{border:3px solid #777;content:"";height:1.5em;position:absolute;right:1em;top:-1.5em}.site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,sans-serif}html[lang^=ja] .site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,ＭＳ ゴシック,sans-serif}html[lang=zh-tw] .site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif}html[lang=zh-cn] .site-ui-font{font-family:Source Sans Pro,Helvetica,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif}body{font-smoothing:subpixel-antialiased!important;-webkit-font-smoothing:subpixel-antialiased!important;-moz-osx-font-smoothing:auto!important;-webkit-overflow-scrolling:touch;font-family:Source Sans Pro,Helvetica,Arial,sans-serif;letter-spacing:.025em}html[lang^=ja] body{font-family:Source Sans Pro,Helvetica,Arial,Hiragino Kaku Gothic Pro,ヒラギノ角ゴ Pro W3,Osaka,Meiryo,メイリオ,MS Gothic,ＭＳ ゴシック,sans-serif}html[lang=zh-tw] body{font-family:Source Sans Pro,Helvetica,Arial,PingFang TC,Microsoft JhengHei,微軟正黑,sans-serif}html[lang=zh-cn] body{font-family:Source Sans Pro,Helvetica,Arial,PingFang SC,Microsoft YaHei,微软雅黑,sans-serif}abbr[title]{border-bottom:none;text-decoration:underline;-webkit-text-decoration:underline dotted;text-decoration:underline dotted}abbr[data-original-title],abbr[title]{cursor:help}body.modal-open{overflow-y:auto;padding-right:0!important}svg{text-shadow:none}
    </style>
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
    <!--[if lt IE 9]>
    	<script src="https://cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv.min.js" integrity="sha256-3Jy/GbSLrg0o9y5Z5n1uw0qxZECH7C6OQpVBgNFYa0g=" crossorigin="anonymous"></script>
    	<script src="https://cdnjs.cloudflare.com/ajax/libs/respond.js/1.4.2/respond.min.js" integrity="sha256-g6iAfvZp+nDQ2TdTR/VVKJf3bGro4ub5fvWSWVRi2NE=" crossorigin="anonymous"></script>
		<script src="https://cdnjs.cloudflare.com/ajax/libs/es5-shim/4.5.9/es5-shim.min.js" integrity="sha256-8E4Is26QH0bD52WoQpcB+R/tcWQtpzlCojrybUd7Mxo=" crossorigin="anonymous"></script>
    <![endif]-->
</head>

<body>
    <div id="doc" class="markdown-body container-fluid comment-enabled" data-hard-breaks="true"><h1 id="A-Boring-Thread-Attributes-Interface" data-id="A-Boring-Thread-Attributes-Interface" data-original-title="" title=""><a class="anchor hidden-xs" href="#A-Boring-Thread-Attributes-Interface" title="A-Boring-Thread-Attributes-Interface"><span class="octicon octicon-link"></span></a><span>A Boring Thread Attributes Interface</span></h1><p><strong><span>Document number</span></strong><span>: P3022R0</span><br>
<strong><span>Date</span></strong><span>: 2023-10-14</span><br>
<strong><span>Authors</span></strong><span>: David Sankel &lt;</span><a href="mailto:dsankel@adobe.com" target="_blank" rel="noopener"><span>dsankel@adobe.com</span></a><span>&gt;, Darius Neațu &lt;</span><a href="mailto:dariusn@adobe.com" target="_blank" rel="noopener"><span>dariusn@adobe.com</span></a><span>&gt;</span><br>
<strong><span>Audience</span></strong><span>: Library Evolution</span></p><h2 id="Abstract" data-id="Abstract"><a class="anchor hidden-xs" href="#Abstract" title="Abstract"><span class="octicon octicon-link"></span></a><span>Abstract</span></h2><p><span>The standard library lacks facilities to configure stack sizes or names for threads. This has resulted in a proliferation of code making use of unportable, platform-specific thread libraries. This paper argues that a simple, standardized, thread attribute API is a preferred solution to this problem.</span></p><table>
<tbody><tr>
<th> P2019R3's API </th>
</tr>
<tr>
<td>
<pre><code class="C++ hljs"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-type">int</span>)</span></span>;

<span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">auto</span> thread = std::jthread::<span class="hljs-built_in">make_with_attributes</span>(
      []{ <span class="hljs-built_in">f</span>(<span class="hljs-number">3</span>); },
      std::<span class="hljs-built_in">thread_name</span>(<span class="hljs-string">"Worker"</span>),
      std::<span class="hljs-built_in">thread_stack_size</span>(<span class="hljs-number">512</span> * <span class="hljs-number">1024</span>)
    );
}
</code></pre>
</td>
</tr>
<tr>
<th> Proposed API </th>
</tr>
<tr>
<td>
<pre><code class="C++ hljs"><span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">f</span><span class="hljs-params">(<span class="hljs-type">int</span>)</span></span>;

<span class="hljs-function"><span class="hljs-type">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    std::jthread::attributes attrs;
    attrs.<span class="hljs-built_in">set_name</span>(<span class="hljs-string">"Worker"</span>);
    attrs.<span class="hljs-built_in">set_stack_size_hint</span>(<span class="hljs-number">512</span> * <span class="hljs-number">1024</span>);
    
    <span class="hljs-keyword">auto</span> thread = std::<span class="hljs-built_in">jthread</span>(attrs, f, <span class="hljs-number">3</span>);
}
</code></pre>
</td>
</tr>
</tbody></table><h2 id="Introduction" data-id="Introduction"><a class="anchor hidden-xs" href="#Introduction" title="Introduction"><span class="octicon octicon-link"></span></a><span>Introduction</span></h2><p><span>OSs provide many options for creating new threads. POSIX threads, for example, have over a dozen settings. Two of these stand out in their utility: thread names and thread stack sizes. Thread names simplify debugging workflows and configurable stack sizes are necessary for many-threaded and massive applications. Although most OSs support these settings, the C++ standard doesn’t expose them.</span></p><p><span>In 2016, Vincente J. Botet Escribá proposed in </span><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0320r1.pdf" target="_blank" rel="noopener"><span>P0320R1</span></a><span>[1] that OS-specific thread attributes be exposed. More recently, Corentin Jabot made the case in </span><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2019r3.pdf" target="_blank" rel="noopener"><span>P2019R3</span></a><span>[2] that thread names and stack sizes in particular should be portably supported.</span></p><p><span>Although there is general agreement on the motivation for exposing thread names and stack sizes, it is unclear what form the API should take. </span><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2019r3.pdf" target="_blank" rel="noopener"><span>P2019R3</span></a><span> introduced a design involving a factory function and individual attribute classes which can be used as optional, unordered arguments to the factory. This complication was justified by the design’s avoidance of ABI concerns when a future standard incorporates additional thread attributes.</span></p><p><span>While </span><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2019r3.pdf" target="_blank" rel="noopener"><span>P2019R3</span></a><span>’s design displays ingenuity and originality, we feel that C++ users are better served with a simpler design that has been battle tested over two decades. We speak of </span><code>Boost.Thread</code><span>’s thread attribute design.</span></p><h2 id="Our-design" data-id="Our-design"><a class="anchor hidden-xs" href="#Our-design" title="Our-design"><span class="octicon octicon-link"></span></a><span>Our design</span></h2><p><span>In our experience users prefer familiar, simple APIs to novel, complicated ones. There are few things more familiar to C++ users than creating an object and calling some setters:</span></p><pre><code class="C++ hljs">std::jthread:attributes attrs;
attrs.set_name(<span class="hljs-string">"GUI Thread"</span>);
attrs.set_stack_size_hint(<span class="hljs-number">1024</span> <span class="hljs-operator">*</span> <span class="hljs-number">6</span>);
</code></pre><p><span>In our design, creating a thread is the same as it has always been except an attributes object can be added as the first argument.</span></p><pre><code class="C++ hljs">std::jthread gui<span class="hljs-constructor">_thread(<span class="hljs-params">attrs</span>, &amp;<span class="hljs-params">gui_function</span>, <span class="hljs-params">gui_argument</span>)</span>;
</code></pre><p><span>It’s as simple as that. Compare that to the the equivalent in </span><a href="https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2019r3.pdf" target="_blank" rel="noopener"><span>P2019R3</span></a><span>’:</span></p><pre><code class="C++ hljs">auto gui_thread = std::jthread::make_with_attributes(
                  [&amp;]{ gui_function( gui_argument ); },
                    std::thread_name(<span class="hljs-string">"GUI Thread"</span>),
                    std::thread_stack_size(<span class="hljs-number">1024</span> * <span class="hljs-number">6</span>)
                  );
</code></pre><p><span>Note that,</span></p><ul>
<li><span>a factory function is used instead of a constructor,</span></li>
<li><span>the function call is wrapped in a lambda because thread function arguments are unsupported, and</span></li>
<li><span>non-positional arguments are dispatched based on type.</span></li>
</ul><p><span>While experts familiar with those techniques may appreciate the beauty, it is our experience that most C++ engineers will not. An ergonomic interface will have a much more positive impact on our current and future C++ developers.</span></p><h2 id="Considerations" data-id="Considerations"><a class="anchor hidden-xs" href="#Considerations" title="Considerations"><span class="octicon octicon-link"></span></a><span>Considerations</span></h2><h3 id="Existing-Practice" data-id="Existing-Practice"><a class="anchor hidden-xs" href="#Existing-Practice" title="Existing-Practice"><span class="octicon octicon-link"></span></a><span>Existing Practice</span></h3><p><span>Probably the oldest and the most used C threading library is </span><a href="https://man7.org/linux/man-pages/man7/pthreads.7.html" target="_blank" rel="noopener"><span>pthreads</span></a><span>. Since the first POSIX standards were defined few decades ago (around 1985), basic concepts regarding parallel processing were adopted worldwide. Some of the first concepts were: process, threads, global shared memory (data and heap segments), individual thread stack (automatic variables), and attributes shared by threads (PID, FDs).</span></p><p><span>The C pthread library defines </span><code>pthread_attr_t</code><span> as a configuration interface used at thread creation via </span><a href="https://man7.org/linux/man-pages/man3/pthread_create.3.html" target="_blank" rel="noopener"><span>pthread_create</span></a><span>. The most used attributes refer to thread detaching, scheduler inheritance and, probably the most used one, the thread stack size. Downsides of known implementations before 2000 were primarily related to portability: </span><code>pthread</code><span> functioned on Unix variants while Windows used a different API. Additionally, thread utilities were absent from the C++98 standard.</span></p><p><span>Almost two decades later (2001), the first large-used portable threading library was published - </span><a href="https://www.boost.org/doc/libs/1_83_0/doc/html/thread.html" target="_blank" rel="noopener"><span>Boost.Thread</span></a><span>. In the following two decades many more followed such as </span><code>OpenMP</code><span>, </span><code>OpenThreads</code><span>, </span><code>TBB</code><span> and </span><code>QThread</code><span>.</span></p><p><span>The </span><a href="https://www.boost.org/doc/libs/1_83_0/doc/html/thread/thread_management.html#thread.thread_management.tutorial.attributes" target="_blank" rel="noopener"><span>boost::thread::attributes</span></a><span> API inspired our proposal:</span></p><pre><code class="C++ hljs"><span class="hljs-function"><span class="hljs-keyword">template</span>&lt;<span class="hljs-keyword">typename</span> Callable&gt;
<span class="hljs-title">thread</span><span class="hljs-params">(attributes&amp; attrs, Callable func)</span></span>;

<span class="hljs-keyword">class</span> <span class="hljs-title class_">thread</span>::attributes {
<span class="hljs-keyword">public</span>:
    <span class="hljs-built_in">attributes</span>() <span class="hljs-keyword">noexcept</span>;
    ~<span class="hljs-built_in">attributes</span>()=<span class="hljs-keyword">default</span>;
    
    <span class="hljs-comment">// stack</span>
    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_stack_size</span><span class="hljs-params">(std::<span class="hljs-type">size_t</span> size)</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function">std::<span class="hljs-type">size_t</span> <span class="hljs-title">get_stack_size</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span></span>;

<span class="hljs-meta">#<span class="hljs-keyword">if</span> defined BOOST_THREAD_DEFINES_THREAD_ATTRIBUTES_NATIVE_HANDLE</span>
    <span class="hljs-keyword">typedef</span> platform-specific-type native_handle_type;
    <span class="hljs-function">native_handle_type* <span class="hljs-title">native_handle</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-type">const</span> native_handle_type* <span class="hljs-title">native_handle</span><span class="hljs-params">()</span> <span class="hljs-type">const</span> <span class="hljs-keyword">noexcept</span></span>;
<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span>

};
</code></pre><p><span>Here is typical thread creation example with </span><code>Boost.Thread</code><span>:</span></p><pre><code class="C++ hljs">boost::thread::attributes attrs;
<span class="hljs-comment">// set portable attributes</span>
<span class="hljs-comment">// ...</span>
attr.<span class="hljs-built_in">set_stack_size</span>(<span class="hljs-number">4096</span>*<span class="hljs-number">10</span>);

<span class="hljs-meta">#<span class="hljs-keyword">if</span> defined(BOOST_THREAD_PLATFORM_WIN32)</span>
    <span class="hljs-comment">// ... window version</span>
<span class="hljs-meta">#<span class="hljs-keyword">elif</span> defined(BOOST_THREAD_PLATFORM_PTHREAD)</span>
    <span class="hljs-comment">// ... pthread version</span>
    <span class="hljs-built_in">pthread_attr_setschedpolicy</span>(attr.<span class="hljs-built_in">native_handle</span>(), SCHED_RR);
<span class="hljs-meta">#<span class="hljs-keyword">else</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">error</span> <span class="hljs-string">"Boost threads unavailable on this platform"</span></span>
<span class="hljs-meta">#<span class="hljs-keyword">endif</span></span>

<span class="hljs-function">boost::thread <span class="hljs-title">thread</span><span class="hljs-params">(attrs, task_callable, <span class="hljs-comment">/* arguments for task */</span> ...)</span></span>;
</code></pre><p><span>Similar thread attribute implementations are found in other open-source, widely-used C++ threading libraries:  </span><a href="https://llvm.org/doxygen/thread_8h_source.html" target="_blank" rel="noopener"><span>LLVM</span></a><span> (thread stack size APIs), </span><a href="https://source.chromium.org/chromium/chromium/src/+/main:base/threading/thread.h" target="_blank" rel="noopener"><span>Chromium</span></a><span>(thread name &amp; stack size APIs), </span><a href="https://github.com/WebKit/webkit/blob/main/Source/WTF/wtf/Threading.h" target="_blank" rel="noopener"><span>WTF</span></a><span> (thread name, stack size &amp; scheduling policy APIs), </span><a href="https://github.com/facebook/folly/blob/main/folly/system/ThreadName.cpp#L288-L294" target="_blank" rel="noopener"><span>folly</span></a><span> (thread name APIs). In almost every case, thread attributes objects are passed to the thread constructor.</span></p><h3 id="Vendor-Extensions" data-id="Vendor-Extensions"><a class="anchor hidden-xs" href="#Vendor-Extensions" title="Vendor-Extensions"><span class="octicon octicon-link"></span></a><span>Vendor Extensions</span></h3><p><span>While non-standard, platform-specific extensions are outside the purview of the C++ standard, it is useful to note that our design does not obstruct such functionality. A vendor could, for example, create their own non-standard thread attributes class:</span></p><pre><code class="C++ hljs"><span class="hljs-keyword">class</span> <span class="hljs-title class_">posix_thread_attributes</span> {
<span class="hljs-keyword">public</span>:
    <span class="hljs-comment">// Standard</span>
    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_name</span><span class="hljs-params">(std::string_view)</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_stack_size_hint</span><span class="hljs-params">(std::<span class="hljs-type">size_t</span>)</span> <span class="hljs-keyword">noexcept</span></span>;
    
    <span class="hljs-comment">// Posix-specific</span>
    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_sched_policy</span><span class="hljs-params">(<span class="hljs-type">int</span>)</span> <span class="hljs-keyword">noexcept</span></span>;
    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_contention_scope</span><span class="hljs-params">(<span class="hljs-type">int</span>)</span> <span class="hljs-keyword">noexcept</span></span>;
    
    <span class="hljs-comment">// ...</span>
};
</code></pre><p><span>Such classes could then be used in </span><code>jthread</code><span>’s constructor like </span><code>std::jthread::attributes</code><span>.</span></p><h3 id="Future-stdjthreadattributes-enhancements" data-id="Future-stdjthreadattributes-enhancements"><a class="anchor hidden-xs" href="#Future-stdjthreadattributes-enhancements" title="Future-stdjthreadattributes-enhancements"><span class="octicon octicon-link"></span></a><span>Future </span><code>std::jthread::attributes</code><span> enhancements</span></h3><p><span>Additional thread attributes, such as priority, may find their way in future C++ revisions and we must not obstruct such additions. Fortunately, our proposed design does not force undesirable ABI breaks as one might expect. The migration path for such additions involves the creation of a backwards-compatible replacement class (e.g., </span><code>std::jthread::attributes2</code><span>) with additional attributes. Newly written code will utilize the new class and old code can be migrated when appropriate.</span></p><h3 id="Allocation-concerns" data-id="Allocation-concerns"><a class="anchor hidden-xs" href="#Allocation-concerns" title="Allocation-concerns"><span class="octicon octicon-link"></span></a><span>Allocation concerns</span></h3><p><span>Many systems cannot use the default allocator. Will the thread name attribute pose a problem in these cases? Fortunately, it is not generally necessary to allocate a </span><code>string</code><span> on the heap for </span><code>thread::attributes</code><span> objects.</span></p><ol>
<li><span>Systems without thread naming support can ignore this attribute completely.</span></li>
<li><span>Implementations of </span><code>thread::attributes</code><span> will likely use an OS-provided thread attribute handle for storing this data. </span><code>Boost.Thread</code><span> took this approach.</span></li>
<li><span>Many real-time system thread names have length limits which allows </span><code>thread::attributes</code><span> objects to store the string in place. Linux, for example, limits thread names to 15 characters (</span><a href="https://man7.org/linux/man-pages/man3/pthread_setname_np.3.html" target="_blank" rel="noopener"><span>man pthread_setname_np</span></a><span>).</span></li>
</ol><h3 id="stdthread-vs-stdjthread" data-id="stdthread-vs-stdjthread"><a class="anchor hidden-xs" href="#stdthread-vs-stdjthread" title="stdthread-vs-stdjthread"><span class="octicon octicon-link"></span></a><code>std::thread</code><span> vs </span><code>std::jthread</code></h3><p><span>We do not propose to add attributes support to </span><code>std::thread</code><span>. By adding this capability to only </span><code>std::jthread</code><span>, we give further encouragement to migrate to it.</span></p><h2 id="API-synopsis" data-id="API-synopsis"><a class="anchor hidden-xs" href="#API-synopsis" title="API-synopsis"><span class="octicon octicon-link"></span></a><span>API synopsis</span></h2><p><span>The changes discussed in this section are relative to </span><a href="https://wg21.link/N4950" target="_blank" rel="noopener"><span>N4950</span></a><span>[3].</span></p><h3 id="Create-stdjthreadattributes-Class" data-id="Create-stdjthreadattributes-Class"><a class="anchor hidden-xs" href="#Create-stdjthreadattributes-Class" title="Create-stdjthreadattributes-Class"><span class="octicon octicon-link"></span></a><span>Create </span><code>std::jthread::attributes</code><span> Class</span></h3><p><span>We propose adding a class that stores all attributes passed to </span><code>std::jthread</code><span>’s constructor (before actually starting the thread).</span></p><pre><code class="C++ hljs">+ Class thread::attributes[thread.jthread.attributes]
+ <span class="hljs-keyword">namespace</span> std {                                                               
+    <span class="hljs-keyword">class</span> <span class="hljs-title class_">jthread</span>::attributes {
+        <span class="hljs-built_in">attributes</span>() <span class="hljs-keyword">noexcept</span>;                                
+        ~<span class="hljs-built_in">attributes</span>() = <span class="hljs-keyword">default</span>;
+ 
+    <span class="hljs-keyword">public</span>:
+        <span class="hljs-comment">// set thread name</span>
+        <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_name</span><span class="hljs-params">(<span class="hljs-type">const</span> <span class="hljs-type">char</span>* name)</span> <span class="hljs-keyword">noexcept</span></span>;
+        <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_name</span><span class="hljs-params">(std::string_view name)</span> <span class="hljs-keyword">noexcept</span></span>;
+        <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_name</span><span class="hljs-params">(std::string&amp;&amp; name)</span> <span class="hljs-keyword">noexcept</span></span>;
+        <span class="hljs-comment">// set thread stack size hint</span>
+        <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">set_stack_size_hint</span><span class="hljs-params">(std::<span class="hljs-type">size_t</span> size)</span> <span class="hljs-keyword">noexcept</span></span>;
+    };
+ }
</code></pre><p><span>The public interfaces were inspired from existing practices used in </span><code>Boost.Thread</code><span> (e.g., </span><code>boost::thread::attributes</code><span>) and it matches the </span><code>std::jthread</code><span>’s current API.</span></p><h3 id="Update-stdjthread-Class" data-id="Update-stdjthread-Class"><a class="anchor hidden-xs" href="#Update-stdjthread-Class" title="Update-stdjthread-Class"><span class="octicon octicon-link"></span></a><span>Update </span><code>std::jthread</code><span> Class</span></h3><p><span>We propose multiple additions to the </span><code>std::jthread</code><span> class:</span></p><ul>
<li><span>an attribute class:</span><code>attributes</code><span> - described in previous section.</span></li>
<li><span>2 new constructors: </span><code>thread(attributes&amp;&amp; attrs)</code><span> / </span><code>thread(attributes&amp;&amp; attrs, F&amp;&amp; f, Args&amp;&amp;... args)</code><span> - constructors which accept a </span><code>std::jthread::attributes</code><span> object as the first argument.</span></li>
<li><span>a set of new getters: </span><code>get_name()</code><span> / </span><code>get_stack_size()</code><span> - the most common thread attributes used by industry (as described above); naming inspired from </span><code>get_id()</code><span>. The behaviour of the getters is implementation-defined.</span></li>
<li><span>add the </span><code>[[nodiscard]]</code><span> specifier for existing member functions: </span><code>get_id()</code><span> and </span><code>native_handle()</code><span>.</span></li>
</ul><pre><code class="C++ hljs">namespace std {
    class jthread {
    <span class="hljs-keyword">public</span>:
        <span class="hljs-comment">// types</span>
        <span class="hljs-keyword">using</span> <span class="hljs-title">id</span> <span class="hljs-operator">=</span> <span class="hljs-title">thread</span>::<span class="hljs-title">id</span>;
<span class="hljs-operator">+</span>       <span class="hljs-comment">// [thread.jthread.attributes.class]</span>
<span class="hljs-operator">+</span>       class attributes;
        <span class="hljs-keyword">using</span> <span class="hljs-title">native_handle_type</span> <span class="hljs-operator">=</span> <span class="hljs-title">thread</span>::<span class="hljs-title">native_handle_type</span>;

        <span class="hljs-comment">// [thread.jthread.cons], constructors, move, and assignment</span>
        jthread() noexcept;
<span class="hljs-operator">+</span>       jthread(attributes<span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> attrs) noexcept;
        template<span class="hljs-operator">&lt;</span>class F, class... Args<span class="hljs-operator">&gt;</span> explicit jthread(F<span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> f, Args<span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span>... args);
<span class="hljs-operator">+</span>       template<span class="hljs-operator">&lt;</span>class F, class... Args<span class="hljs-operator">&gt;</span> explicit jthread(attributes<span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> attrs, F<span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span> f, Args<span class="hljs-operator">&amp;</span><span class="hljs-operator">&amp;</span>... args);
        <span class="hljs-operator">~</span>jthread();
    
        ...
        void detach();
        [[nodiscard]] id get_id() const noexcept;
<span class="hljs-operator">+</span>       [[nodiscard]] std::string_view get_name() const noexcept;
<span class="hljs-operator">+</span>       [[nodiscard]] std::size get_stack_size() const noexcept;
<span class="hljs-operator">+</span>
        [[nodiscard]] native_handle_type native_handle();   <span class="hljs-comment">// see [thread.req.native]</span>
        ...
    };
}
</code></pre><p><span>Usage example:</span></p><pre><code class="C++ hljs"><span class="hljs-meta">#<span class="hljs-keyword">include</span> <span class="hljs-string">&lt;thread&gt;</span></span>

std::jthread::attributes attrs;
attrs.<span class="hljs-built_in">set_name</span>(<span class="hljs-string">"Worker"</span>); <span class="hljs-comment">// or use std::string_view / std::string</span>
attrs.<span class="hljs-built_in">set_stack_size_hint</span>(<span class="hljs-number">4096</span>);

<span class="hljs-function">std::jthread <span class="hljs-title">t</span><span class="hljs-params">(attrs, [](...){ ... }, ...)</span></span>;

std::<span class="hljs-built_in">println</span>(<span class="hljs-string">"Created thread with name {}."</span>, t.<span class="hljs-built_in">get_name</span>());
</code></pre><h3 id="Update-stdthis_thread-namespace" data-id="Update-stdthis_thread-namespace"><a class="anchor hidden-xs" href="#Update-stdthis_thread-namespace" title="Update-stdthis_thread-namespace"><span class="octicon octicon-link"></span></a><span>Update </span><code>std::this_thread</code><span> namespace</span></h3><p><span>We propose minor additions to the </span><code>std::this_thread</code><span> namespace:</span></p><ul>
<li><span>add same getters as in the </span><code>std::jthread</code><span> class: </span><code>get_name()</code><span> / </span><code>get_stack_size()</code><span>. The behaviour of the new getters is implementation-defined.</span></li>
<li><span>add the </span><code>[[nodiscard]]</code><span> specifier to existing member functions: </span><code>get_id()</code><span>.</span></li>
</ul><pre><code class="C++ hljs"><span class="hljs-comment">// Namespace this_thread[thread.thread.this]</span>
<span class="hljs-keyword">namespace</span> std::this_thread {
+   [[nodiscard]] <span class="hljs-function">thread::id <span class="hljs-title">get_id</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span></span>; <span class="hljs-comment">// existent function, add [[nodiscard]] specifier </span>
+   [[nodiscard]] <span class="hljs-function">std::string_view <span class="hljs-title">get_name</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span></span>;
+   [[nodiscard]] <span class="hljs-function">std::size <span class="hljs-title">get_stack_size</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span></span>;
+
    <span class="hljs-function"><span class="hljs-type">void</span> <span class="hljs-title">yield</span><span class="hljs-params">()</span> <span class="hljs-keyword">noexcept</span></span>;
    ...
}
</code></pre><p><span>Usage example:</span></p><pre><code class="C++ hljs"><span class="hljs-comment">#include &lt;thread&gt;</span>

std::jthread::attributes attrs;
attrs.set_name(<span class="hljs-string">"Worker"</span>); <span class="hljs-regexp">//</span> <span class="hljs-keyword">or</span> use std::string_view / std::string
attrs.set_stack_size_hint(<span class="hljs-number">4096</span>);

std::jthread t(attrs, [](...){ 
    std::println(<span class="hljs-string">"Hello from {} thread"</span>, std::this_thread::get_name();
}, ...);

std::println(<span class="hljs-string">"Created thread with name {}."</span>, t.get_name());

</code></pre><h2 id="References" data-id="References"><a class="anchor hidden-xs" href="#References" title="References"><span class="octicon octicon-link"></span></a><span>References</span></h2><p><span>[1] Vicente J. Botet Escribá. P0320R1: Thread Constructor Attributes. </span><a href="https://wg21.link/p0320r1" target="_blank" rel="noopener"><span>https://wg21.link/p0320r1</span></a><span>, 10/2016.</span><br>
<span>[2] Corentin Jabot. P2019R3: Thread attributes. </span><a href="https://wg21.link/p2019r3" target="_blank" rel="noopener"><span>https://wg21.link/p2019r3</span></a><span>, 5/2023.</span><br>
<span>[3] Thomas Köppe. N4950: Working Draft, Standard for Programming Language C++. </span><a href="https://wg21.link/N4950" target="_blank" rel="noopener"><span>https://wg21.link/N4950</span></a><span>, 10/2023</span><br>
<span>[4] pthreads API docs: </span><a href="https://man7.org/linux/man-pages/man7/pthreads.7.html" target="_blank" rel="noopener"><span>man 7 pthreads</span></a><br>
<span>[5] Boost.Thread API docs: </span><a href="https://www.boost.org/doc/libs/1_83_0/doc/html/thread.html" target="_blank" rel="noopener"><span>boost::thread</span></a><br>
<span>[6] LLVM thread implementation: </span><a href="https://llvm.org/doxygen/thread_8h_source.html" target="_blank" rel="noopener"><span>llvm::thread</span></a><br>
<span>[7] Chromium thread implementation: </span><a href="https://source.chromium.org/chromium/chromium/src/+/main:base/threading/thread.h" target="_blank" rel="noopener"><span>base::Thread</span></a><br>
<span>[8] WTF thread implementation: </span><a href="https://github.com/facebook/folly/blob/main/folly/system/ThreadName.h" target="_blank" rel="noopener"><span>folly::ThreadName</span></a></p></div>
    <div class="ui-toc dropup unselectable hidden-print" style="display:none;">
        <div class="pull-right dropdown">
            <a id="tocLabel" class="ui-toc-label btn btn-default" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false" title="Table of content">
                <i class="fa fa-bars"></i>
            </a>
            <ul id="ui-toc" class="ui-toc-dropdown dropdown-menu" aria-labelledby="tocLabel">
                <div class="toc"><ul class="nav">
<li class=""><a href="#A-Boring-Thread-Attributes-Interface" title="A Boring Thread Attributes Interface">A Boring Thread Attributes Interface</a><ul class="nav">
<li><a href="#Abstract" title="Abstract">Abstract</a></li>
<li><a href="#Introduction" title="Introduction">Introduction</a></li>
<li><a href="#Our-design" title="Our design">Our design</a></li>
<li><a href="#Considerations" title="Considerations">Considerations</a><ul class="nav">
<li><a href="#Existing-Practice" title="Existing Practice">Existing Practice</a></li>
<li><a href="#Vendor-Extensions" title="Vendor Extensions">Vendor Extensions</a></li>
<li><a href="#Future-stdjthreadattributes-enhancements" title="Future std::jthread::attributes enhancements">Future std::jthread::attributes enhancements</a></li>
<li><a href="#Allocation-concerns" title="Allocation concerns">Allocation concerns</a></li>
<li><a href="#stdthread-vs-stdjthread" title="std::thread vs std::jthread">std::thread vs std::jthread</a></li>
</ul>
</li>
<li><a href="#API-synopsis" title="API synopsis">API synopsis</a><ul class="nav">
<li><a href="#Create-stdjthreadattributes-Class" title="Create std::jthread::attributes Class">Create std::jthread::attributes Class</a></li>
<li><a href="#Update-stdjthread-Class" title="Update std::jthread Class">Update std::jthread Class</a></li>
<li><a href="#Update-stdthis_thread-namespace" title="Update std::this_thread namespace">Update std::this_thread namespace</a></li>
</ul>
</li>
<li class=""><a href="#References" title="References">References</a></li>
</ul>
</li>
</ul>
</div><div class="toc-menu"><a class="expand-toggle" href="#">Expand all</a><a class="back-to-top" href="#">Back to top</a><a class="go-to-bottom" href="#">Go to bottom</a></div>
            </ul>
        </div>
    </div>
    <div id="ui-toc-affix" class="ui-affix-toc ui-toc-dropdown unselectable hidden-print" data-spy="affix" style="top:17px;display:none;" null null>
        <div class="toc"><ul class="nav">
<li class=""><a href="#A-Boring-Thread-Attributes-Interface" title="A Boring Thread Attributes Interface">A Boring Thread Attributes Interface</a><ul class="nav">
<li><a href="#Abstract" title="Abstract">Abstract</a></li>
<li><a href="#Introduction" title="Introduction">Introduction</a></li>
<li><a href="#Our-design" title="Our design">Our design</a></li>
<li><a href="#Considerations" title="Considerations">Considerations</a><ul class="nav">
<li><a href="#Existing-Practice" title="Existing Practice">Existing Practice</a></li>
<li><a href="#Vendor-Extensions" title="Vendor Extensions">Vendor Extensions</a></li>
<li><a href="#Future-stdjthreadattributes-enhancements" title="Future std::jthread::attributes enhancements">Future std::jthread::attributes enhancements</a></li>
<li><a href="#Allocation-concerns" title="Allocation concerns">Allocation concerns</a></li>
<li><a href="#stdthread-vs-stdjthread" title="std::thread vs std::jthread">std::thread vs std::jthread</a></li>
</ul>
</li>
<li><a href="#API-synopsis" title="API synopsis">API synopsis</a><ul class="nav">
<li><a href="#Create-stdjthreadattributes-Class" title="Create std::jthread::attributes Class">Create std::jthread::attributes Class</a></li>
<li><a href="#Update-stdjthread-Class" title="Update std::jthread Class">Update std::jthread Class</a></li>
<li><a href="#Update-stdthis_thread-namespace" title="Update std::this_thread namespace">Update std::this_thread namespace</a></li>
</ul>
</li>
<li class=""><a href="#References" title="References">References</a></li>
</ul>
</li>
</ul>
</div><div class="toc-menu"><a class="expand-toggle" href="#">Expand all</a><a class="back-to-top" href="#">Back to top</a><a class="go-to-bottom" href="#">Go to bottom</a></div>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.1.1/jquery.min.js" integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8=" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js" integrity="sha256-U5ZEeKfGNOja007MMD3YBI0A3OSZOQbeG6z2f2Y0hu8=" crossorigin="anonymous" defer></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gist-embed/2.6.0/gist-embed.min.js" integrity="sha256-KyF2D6xPIJUW5sUDSs93vWyZm+1RzIpKCexxElmxl8g=" crossorigin="anonymous" defer></script>
    <script>
        var markdown = $(".markdown-body");
        //smooth all hash trigger scrolling
        function smoothHashScroll() {
            var hashElements = $("a[href^='#']").toArray();
            for (var i = 0; i < hashElements.length; i++) {
                var element = hashElements[i];
                var $element = $(element);
                var hash = element.hash;
                if (hash) {
                    $element.on('click', function (e) {
                        // store hash
                        var hash = this.hash;
                        if ($(hash).length <= 0) return;
                        // prevent default anchor click behavior
                        e.preventDefault();
                        // animate
                        $('body, html').stop(true, true).animate({
                            scrollTop: $(hash).offset().top
                        }, 100, "linear", function () {
                            // when done, add hash to url
                            // (default click behaviour)
                            window.location.hash = hash;
                        });
                    });
                }
            }
        }

        smoothHashScroll();
        var toc = $('.ui-toc');
        var tocAffix = $('.ui-affix-toc');
        var tocDropdown = $('.ui-toc-dropdown');
        //toc
        tocDropdown.click(function (e) {
            e.stopPropagation();
        });

        var enoughForAffixToc = true;

        function generateScrollspy() {
            $(document.body).scrollspy({
                target: ''
            });
            $(document.body).scrollspy('refresh');
            if (enoughForAffixToc) {
                toc.hide();
                tocAffix.show();
            } else {
                tocAffix.hide();
                toc.show();
            }
            $(document.body).scroll();
        }

        function windowResize() {
            //toc right
            var paddingRight = parseFloat(markdown.css('padding-right'));
            var right = ($(window).width() - (markdown.offset().left + markdown.outerWidth() - paddingRight));
            toc.css('right', right + 'px');
            //affix toc left
            var newbool;
            var rightMargin = (markdown.parent().outerWidth() - markdown.outerWidth()) / 2;
            //for ipad or wider device
            if (rightMargin >= 133) {
                newbool = true;
                var affixLeftMargin = (tocAffix.outerWidth() - tocAffix.width()) / 2;
                var left = markdown.offset().left + markdown.outerWidth() - affixLeftMargin;
                tocAffix.css('left', left + 'px');
            } else {
                newbool = false;
            }
            if (newbool != enoughForAffixToc) {
                enoughForAffixToc = newbool;
                generateScrollspy();
            }
        }
        $(window).resize(function () {
            windowResize();
        });
        $(document).ready(function () {
            windowResize();
            generateScrollspy();
        });

        //remove hash
        function removeHash() {
            window.location.hash = '';
        }

        var backtotop = $('.back-to-top');
        var gotobottom = $('.go-to-bottom');

        backtotop.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            if (scrollToTop)
                scrollToTop();
            removeHash();
        });
        gotobottom.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            if (scrollToBottom)
                scrollToBottom();
            removeHash();
        });

        var toggle = $('.expand-toggle');
        var tocExpand = false;

        checkExpandToggle();
        toggle.click(function (e) {
            e.preventDefault();
            e.stopPropagation();
            tocExpand = !tocExpand;
            checkExpandToggle();
        })

        function checkExpandToggle () {
            var toc = $('.ui-toc-dropdown .toc');
            var toggle = $('.expand-toggle');
            if (!tocExpand) {
                toc.removeClass('expand');
                toggle.text('Expand all');
            } else {
                toc.addClass('expand');
                toggle.text('Collapse all');
            }
        }

        function scrollToTop() {
            $('body, html').stop(true, true).animate({
                scrollTop: 0
            }, 100, "linear");
        }

        function scrollToBottom() {
            $('body, html').stop(true, true).animate({
                scrollTop: $(document.body)[0].scrollHeight
            }, 100, "linear");
        }
    </script>
</body>

</html>
