<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Mer's Blog</title><link>https://merisky.top/</link><description>Recent content on Mer's Blog</description><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Mon, 29 May 2023 11:50:14 +0800</lastBuildDate><atom:link href="https://merisky.top/index.xml" rel="self" type="application/rss+xml"/><item><title>Ceph-读写流程：客户端写流程分析(二)</title><link>https://merisky.top/posts/867489514/</link><pubDate>Mon, 29 May 2023 03:36:46 +0000</pubDate><guid>https://merisky.top/posts/867489514/</guid><description>&lt;p&gt;本文接上文对 &lt;code&gt;Filer::write_trunc&lt;/code&gt; 及之后的流程做了梳理和总结.&lt;/p&gt;&#10;&lt;h2 id="正文"&gt;&lt;a href="#正文" class="headerlink" title="正文"&gt;&lt;/a&gt;正文&lt;/h2&gt;&lt;p&gt;本文是 &lt;a href="https://merisky.top/posts/748254147/"&gt;Ceph 读写流程：客户端写流程分析&lt;/a&gt; 的后续，前文包括 &lt;code&gt;ll_open&lt;/code&gt; 和 &lt;code&gt;ll_write&lt;/code&gt; 的处理流程。&lt;/p&gt;&#10;&lt;p&gt;Client 通过 &lt;code&gt;Filer::write_trunc&lt;/code&gt; 写文件时会做两件事： 通过 &lt;code&gt;Striper::file_to_extents&lt;/code&gt; 做一次客户端条带化，将要写入的部分转换为对象的 extent 集合；接着通过 &lt;code&gt;objecter-&amp;gt;sg_write_trunc&lt;/code&gt; 进行写入&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="title"&gt;write_trunc&lt;/span&gt; &lt;span class="params"&gt;(...)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;&lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; vector&amp;lt;ObjectExtent&amp;gt; extents;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Striper::&lt;span class="built_in"&gt;file_to_extents&lt;/span&gt;(cct, ino, layout, offset, len, truncate_size,&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9;&#9; extents);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; objecter-&amp;gt;&lt;span class="built_in"&gt;sg_write_trunc&lt;/span&gt;(extents, snapc, bl, mtime, flags,&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9; truncate_size, truncate_seq, oncommit, op_flags);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;而在 &lt;code&gt;sg_write_trunc&lt;/code&gt; 中，objecter 会向每个 &lt;code&gt;ObjectExtent&lt;/code&gt; 对应的 OSD 发送 op 请求，这里通过 &lt;code&gt;write_trunc&lt;/code&gt; 完成，考虑 &lt;code&gt;extents.size() == 1&lt;/code&gt; 的情况如下：&lt;/p&gt;</description></item><item><title>Ceph-Fuse Perms heap-buffer-overflow 问题</title><link>https://merisky.top/posts/3177229983/</link><pubDate>Sun, 23 Apr 2023 08:20:53 +0000</pubDate><guid>https://merisky.top/posts/3177229983/</guid><description>&lt;p&gt;Ceph-fuse 带 Address-Sanitizer 跑发现只要 MDS 切换就会导致客户端 heap-buffer-overflow (ASAN 信息附在最后)&lt;/p&gt;&#10;&lt;h2 id="问题分析"&gt;&lt;a href="#问题分析" class="headerlink" title="问题分析"&gt;&lt;/a&gt;问题分析&lt;/h2&gt;&lt;p&gt;带 debuginfo 的情况下 ASAN 报的信息还是比较全的，说踩内存的地方在 &lt;code&gt;src/messages/MClientRequest.h:151&lt;/code&gt; 的 &lt;code&gt;MClientRequest::set_gid_list&lt;/code&gt; 函数中，并且指出这里尝试读了一个野指针，看下 &lt;code&gt;MClientRequest::set_gid_list&lt;/code&gt; 函数这里的行为：&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&#9;&lt;span class="function"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="title"&gt;set_gid_list&lt;/span&gt;&lt;span class="params"&gt;(&lt;span class="type"&gt;int&lt;/span&gt; count, &lt;span class="type"&gt;const&lt;/span&gt; &lt;span class="type"&gt;gid_t&lt;/span&gt; *gids)&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9;gid_list.&lt;span class="built_in"&gt;reserve&lt;/span&gt;(count);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9;&lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="type"&gt;int&lt;/span&gt; i = &lt;span class="number"&gt;0&lt;/span&gt;; i &amp;lt; count; ++i) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;→ gid_list.&lt;span class="built_in"&gt;push_back&lt;/span&gt;(gids[i]);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&#9;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&#9;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;可以看到这里问题就在 &lt;code&gt;gids&lt;/code&gt; 上，看一下这个指针是哪里传进来的：&lt;/p&gt;</description></item><item><title>Ceph MDS InodeTable 分配流程和故障分析</title><link>https://merisky.top/posts/3495093285/</link><pubDate>Fri, 14 Apr 2023 00:33:43 +0000</pubDate><guid>https://merisky.top/posts/3495093285/</guid><description>&lt;p&gt;本文对 Ceph 中 &lt;code&gt;inode&lt;/code&gt; 分配流程进行了总结，并解释了 MDS 重启过程中可能遇到的一个故障&lt;/p&gt;&#10;&lt;h2 id="inode-分配流程"&gt;&lt;a href="#inode-分配流程" class="headerlink" title="inode 分配流程"&gt;&lt;/a&gt;inode 分配流程&lt;/h2&gt;&lt;p&gt;在 Ceph 中我们创建目录、文件、软链接时都需要为其分配一个 &lt;code&gt;inode&lt;/code&gt; 作为唯一标识，这一分配过程在 &lt;code&gt;Server::prepare_new_inode&lt;/code&gt; 中完成，而分配的依据，也就是哪些 &lt;code&gt;inode&lt;/code&gt; 还没有被分配，哪些&lt;br&gt;被分配了还没有生效（落盘）则是记录在 &lt;code&gt;InoTable&lt;/code&gt; 中:&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title class_"&gt;InoTable&lt;/span&gt; : &lt;span class="keyword"&gt;public&lt;/span&gt; MDSTable &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;private&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; interval_set&amp;lt;&lt;span class="type"&gt;inodeno_t&lt;/span&gt;&amp;gt; free; &lt;span class="comment"&gt;// unused ids&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; interval_set&amp;lt;&lt;span class="type"&gt;inodeno_t&lt;/span&gt;&amp;gt; projected_free;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;...&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;其中 &lt;code&gt;free&lt;/code&gt; 记录的就是没有被分配的 &lt;code&gt;inode&lt;/code&gt; 集合，而 &lt;code&gt;projected_free&lt;/code&gt; 则是从 &lt;code&gt;free&lt;/code&gt; 中剔除了已经被分配但是还没有落盘的部分，因此 &lt;code&gt;projected_free&lt;/code&gt; 是 &lt;code&gt;free&lt;/code&gt; 的子集，同时显然分配也首先要在 &lt;code&gt;projected_free&lt;/code&gt; 中来做。&lt;/p&gt;</description></item><item><title>Ceph MDS MetaBlob 分析</title><link>https://merisky.top/posts/3797827770/</link><pubDate>Thu, 06 Apr 2023 07:28:29 +0000</pubDate><guid>https://merisky.top/posts/3797827770/</guid><description>&lt;p&gt;CephFS 中会把多种事件记录在不同类型的 &lt;code&gt;LogEvent&lt;/code&gt; 中，其中最常见的也是最重要的就是读写流程中处理客户端请求时记录的 &lt;code&gt;EUpdate&lt;/code&gt; 事件，而在 &lt;code&gt;EUpdate&lt;/code&gt; 中又会把元数据的状态保存在 &lt;code&gt;MetaBlob&lt;/code&gt; 结构中，本文就主要对 &lt;code&gt;MetaBlob&lt;/code&gt; 结构以及其中保存的内容做一个总结。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;MetaBlob&lt;/code&gt; 类定义看上去比较复杂，实际最重要的成员只有三个：&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title class_"&gt;EMetaBlob&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;public&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// my lumps. preserve the order we added them in a list.&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::vector&amp;lt;&lt;span class="type"&gt;dirfrag_t&lt;/span&gt;&amp;gt; lump_order;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::map&amp;lt;&lt;span class="type"&gt;dirfrag_t&lt;/span&gt;, dirlump&amp;gt; lump_map;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::list&amp;lt;fullbit&amp;gt; roots;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;其中 &lt;code&gt;lump_order&lt;/code&gt; 保存从根目录到父母路的 Dir 分片信息 &lt;code&gt;dirfrag_t&lt;/code&gt;，&lt;code&gt;lump_map&lt;/code&gt; 中保存 &lt;code&gt;dirfrag_t&lt;/code&gt; 到 &lt;code&gt;dirlump&lt;/code&gt; 的映射关系， &lt;code&gt;dirlump&lt;/code&gt; 中保存的就是相关的元数据信息，最后 &lt;code&gt;roots&lt;/code&gt; 保存的就是 root 节点的 &lt;code&gt;fullbit&lt;/code&gt; 信息，&lt;code&gt;fullbit&lt;/code&gt; 表示一个存在的 &lt;code&gt;dn + inode&lt;/code&gt;，相对的 &lt;code&gt;MetaBlob&lt;/code&gt; 中使用 &lt;code&gt;null_bit&lt;/code&gt; 结构保存一个不存在的 &lt;code&gt;dentry&lt;/code&gt;&lt;/p&gt;</description></item><item><title>Paxos 算法以及 Ceph 实现</title><link>https://merisky.top/posts/3640854188/</link><pubDate>Wed, 05 Apr 2023 14:57:58 +0000</pubDate><guid>https://merisky.top/posts/3640854188/</guid><description>&lt;p&gt;本文包含 Paxos、Multi-Paxos 算法的原理以及 Ceph 中对 Paxos 算法的实现。&lt;/p&gt;&#10;&lt;h2 id="Basic-Paxos-和-Multi-Paxos"&gt;&lt;a href="#Basic-Paxos-和-Multi-Paxos" class="headerlink" title="Basic Paxos 和 Multi-Paxos"&gt;&lt;/a&gt;Basic Paxos 和 Multi-Paxos&lt;/h2&gt;&lt;p&gt;Paxos 和 raft 算法看过不少次，但是始终属于一知半解，也没有个记录，这次把 Paxos 和 Multi-Paxos 的原理和 ceph 中 Paxos 的实现都记录一下：&lt;/p&gt;&#10;&lt;p&gt;首先是 Paxos 算法，朴素的 Paxos 算法是不包含 leader 角色的：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;proposer (提议者): 提出提案，包含提案编号（proposal ID）和一个要协商的值（Val）&lt;/li&gt;&#10;&lt;li&gt;acceptor (决策者)：参与决策，回应收到的来自 proposer 的提案，当多数 acceptor 赞成时提案通过，一般来说一个 proposer 同时也是一个 acceptor&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Basic Paxos 算法通过两阶段通过一个决议：&lt;/p&gt;</description></item><item><title>Ceph 读写流程：客户端写流程分析</title><link>https://merisky.top/posts/748254147/</link><pubDate>Sat, 01 Apr 2023 10:01:16 +0000</pubDate><guid>https://merisky.top/posts/748254147/</guid><description>&lt;p&gt;本文对 Ceph 客户端写流程进行了梳理和总结，包括 &lt;code&gt;ll_open&lt;/code&gt; 和 &lt;code&gt;ll_write&lt;/code&gt; 的处理流程。&lt;/p&gt;&#10;&lt;h2 id="正文"&gt;&lt;a href="#正文" class="headerlink" title="正文"&gt;&lt;/a&gt;正文&lt;/h2&gt;&lt;p&gt;首先用户需要通过 &lt;code&gt;ll_open&lt;/code&gt; 打开文件并拿到 fh，第一步先做权限检查，进入 &lt;code&gt;may_open&lt;/code&gt;, 这里会根据用户带回的 flags 确定 &lt;code&gt;want&lt;/code&gt; 是 &lt;code&gt;MAY_WRITE&lt;/code&gt; 还是 &lt;code&gt;MAY_READ&lt;/code&gt;, 接着通过一次 getattr 拿到文件的 mode, 当然这里 getattr 不是每次都发，只有 client 认为自己的所持有的文件权限不是最新的时候才会发，具体有两种情况：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;开启 acl 并且没有拿过文件的 xattr (acl 设置是外带在 xattr 中的，不在 inode 里)&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;caps_issued_mask&lt;/code&gt; 认为当前 client 的 caps 不足以保证所持有的权限为最新时&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;details class="tag-plugin folding" color="yellow" child="codeblock"&gt;&lt;summary&gt;&lt;span&gt;Client::_getattr 判断是否需要发送请求&lt;/span&gt;&lt;/summary&gt;&lt;div class="body"&gt;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;18&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="type"&gt;int&lt;/span&gt; Client::_getattr(Inode *in, &lt;span class="type"&gt;int&lt;/span&gt; mask, &lt;span class="type"&gt;const&lt;/span&gt; UserPerm&amp;amp; perms, &lt;span class="type"&gt;bool&lt;/span&gt; force)&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;bool&lt;/span&gt; yes = in-&amp;gt;&lt;span class="built_in"&gt;caps_issued_mask&lt;/span&gt;(mask, &lt;span class="literal"&gt;true&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (yes &amp;amp;&amp;amp; !force)&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;...&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// force 和 mask:&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="type"&gt;int&lt;/span&gt; Client::_getattr_for_perm(Inode *in, &lt;span class="type"&gt;const&lt;/span&gt; UserPerm&amp;amp; perms)&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;int&lt;/span&gt; mask = CEPH_STAT_CAP_MODE;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;bool&lt;/span&gt; force = &lt;span class="literal"&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (acl_type != NO_ACL) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; mask |= CEPH_STAT_CAP_XATTR;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; force = in-&amp;gt;xattr_version == &lt;span class="number"&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; _getattr(in, mask, perms, force);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&lt;/div&gt;&lt;/details&gt;&#10;&lt;p&gt;简单解释一下第二点就是如果 client 没有持有 As （开启 acl 的话还需要 Xs）那么就需要向 mds 发 getattr&lt;br&gt;拿到 perm 之后做一个 &lt;code&gt;inode_permission&lt;/code&gt; 检查一下 mode (如果设了 acl 那就检查 acl)&lt;/p&gt;</description></item><item><title>Ceph 读写流程：file_to_extents 过程分析</title><link>https://merisky.top/posts/2816966616/</link><pubDate>Thu, 30 Mar 2023 06:02:08 +0000</pubDate><guid>https://merisky.top/posts/2816966616/</guid><description>&lt;p&gt;本文介绍 Ceph 中条带化的相关概念并对 &lt;code&gt;Striper::file_to_extents&lt;/code&gt; 的流程进行了分析和总结。&lt;/p&gt;&#10;&lt;p&gt;在 &lt;code&gt;Client::_write&lt;/code&gt; 中客户端通过 &lt;code&gt;filer-&amp;gt;write_trunc&lt;/code&gt; 对文件进行写入，&lt;code&gt;write_trunc&lt;/code&gt; 其中分为两步:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;通过 &lt;code&gt;Striper::file_to_extents&lt;/code&gt; 对请求进行拆分，也就是将对文件的读写请求转换为对对象的读写请求&lt;/li&gt;&#10;&lt;li&gt;通过 &lt;code&gt;objecter-&amp;gt;sg_write_trunc&lt;/code&gt; 把数据写到 object 中&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;本文主要对其中的第一步，也就是 &lt;code&gt;Striper::file_to_extents&lt;/code&gt; 的过程进行分析。&lt;/p&gt;&#10;&lt;h2 id="Ceph-中的-Stripping"&gt;&lt;a href="#Ceph-中的-Stripping" class="headerlink" title="Ceph 中的 Stripping"&gt;&lt;/a&gt;Ceph 中的 Stripping&lt;/h2&gt;&lt;p&gt;在正式开始先简单介绍一下 Ceph 中条带化（Stripping）读写的相关概念：&lt;/p&gt;&#10;&lt;details class="tag-plugin folding" &gt;&lt;summary&gt;&lt;span&gt;为什么要使用 Stripping&lt;/span&gt;&lt;/summary&gt;&lt;div class="body"&gt;&lt;p&gt;最主要的原因就是由于单个存储设备存在吞吐量限制，例如一块机械硬盘的写入能力最大也不过 200MB&amp;#x2F;s, 在与内存、固态缓存甚至是万兆网络的吞吐量对比下无疑就是整个系统中最大的短板，那么条带化就是用来解决存储设备性能瓶颈，提高存储系统吞吐量的东西。&lt;/p&gt;&lt;p&gt;那么什么是条带化，说的准确一点就是把信息的连续部分存储在多个存储设备上，说人话就是把一个文件切成多份放在不同的磁盘上，每次对文件中一段内容的读写会同时发给多个盘，这样的话我们实际的写入速度就变成了多个磁盘的写入速度总和。&lt;/p&gt;&lt;/div&gt;&lt;/details&gt;&#10;&lt;p&gt;Ceph 中的条带化的概念大致和其他存储没有区别，只不过在 Ceph 中所有的存储最后都会落到 &lt;code&gt;object&lt;/code&gt; 也就是对象上，而对象的存储是没有条带化的，因此文件的条带化实际上是在客户端上进行的，也就是我们本文的主要内容 &lt;code&gt;file_to_extents&lt;/code&gt; 的过程&lt;/p&gt;</description></item><item><title>Ceph 挂载点压缩提示 File changed as we read it 问题分析</title><link>https://merisky.top/posts/3535257457/</link><pubDate>Mon, 27 Mar 2023 06:50:07 +0000</pubDate><guid>https://merisky.top/posts/3535257457/</guid><description>&lt;p&gt;使用 CephFS 的同学可能会发现如果在 CephFS 挂载点下去压缩文件的时候有时候会提示 &lt;code&gt;File changed as we read it&lt;/code&gt;，有时候还会导致&lt;br&gt;CRC 失败，本文对 &lt;code&gt;File changed as we read it&lt;/code&gt; 问题出现的原因进行了分析并给出了解决办法，希望能对大家有所帮助。&lt;/p&gt;&#10;&lt;h2 id="问题现象"&gt;&lt;a href="#问题现象" class="headerlink" title="问题现象"&gt;&lt;/a&gt;问题现象&lt;/h2&gt;&lt;p&gt;首先这个问题可能导致的现象比较多样，最基础的就是把一个压缩文件解压一下然后立马压缩，这时就会提示 &lt;code&gt;File changed as we read it&lt;/code&gt; 如下：&lt;/p&gt;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;[root@An1Uit ceph-fuse]&lt;span class="comment"&gt;# ./tar.sh&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;tar: cmake-3.7.2/Tests/LinkFlags: 在我们读入文件时文件发生了变化&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;tar: cmake-3.7.2/Tests/SimpleCOnly: 在我们读入文件时文件发生了变化&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;tar: cmake-3.7.2/Tests/ObjC++: 在我们读入文件时文件发生了变化&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;那除了这个现象以外还可能导致一些应用的 CRC 校验失败，或者 nfs 的 REMOTE I&amp;#x2F;O ERROR&lt;/p&gt;</description></item><item><title>Ceph MDS Stuck in Client Replay 问题分析</title><link>https://merisky.top/posts/2094044811/</link><pubDate>Sun, 26 Mar 2023 04:34:54 +0000</pubDate><guid>https://merisky.top/posts/2094044811/</guid><description>&lt;p&gt;最近一直在做 MDS 高可用方面的工作，发现 MDS （带 IO）重启时可能会长时间卡在 Client Replay 状态。这里对问题的原因做了一下分析，并给出了现有的以及未来的解决办法，希望能对大家有所帮助。&lt;/p&gt;&#10;&lt;h2 id="问题现象梳理"&gt;&lt;a href="#问题现象梳理" class="headerlink" title="问题现象梳理"&gt;&lt;/a&gt;问题现象梳理&lt;/h2&gt;&lt;p&gt;通过 &lt;code&gt;ceph -s&lt;/code&gt; 看到 MDS 的状态长时间在 &lt;code&gt;client replay&lt;/code&gt; 状态不变化：&lt;/p&gt;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;[root@node2 ~]&lt;span class="comment"&gt;# ceph -s&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; cluster:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ...&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; mds: cephfs: 1/1 &amp;#123;0=node2=up:clientreplay&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;查看 MDS 状态会发现 &lt;code&gt;clientreplay_queue&lt;/code&gt; 不为空，但是如果打开 MDS 日志会发现 MDS 什么都没做（除了心跳）：&lt;/p&gt;</description></item><item><title>如何实现一个最小堆</title><link>https://merisky.top/posts/2906464195/</link><pubDate>Sat, 25 Mar 2023 17:36:41 +0000</pubDate><guid>https://merisky.top/posts/2906464195/</guid><description>&lt;p&gt;本文介绍如何使用 C++ 实现一个通用的、线程安全的最小堆&lt;/p&gt;&#10;&lt;p&gt;顺便一提，写此文的时候突然想到了多年以前（16、17年）盛行的 Mooc，当时国内有很多类似的慕课网站，也有很多优秀的课程，我的基础数据结构和算法就是一位 ACM 铜牌的老师教的，至今受用，但是现在好像很少看到此类网站了，还是比较可惜的。&lt;/p&gt;&#10;&lt;p&gt;进入正题，最小堆本身是一个完全二叉树，保证每一个节点都不会大于其左右子节点，要实现一个最小堆的两个核心函数是 &lt;code&gt;sift_up&lt;/code&gt; 和 &lt;code&gt;sift_down&lt;/code&gt;，用于调整最小堆结构以符合定义。&lt;/p&gt;&#10;&lt;p&gt;首先给出 &lt;code&gt;MinHeap&lt;/code&gt; 的基本定义和成员变量：&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;template&lt;/span&gt; &amp;lt;std::three_way_comparable T&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title class_"&gt;MinHeap&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::shared_mutex mtx;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::vector&amp;lt;T&amp;gt; vec;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;这里 &lt;code&gt;vec&lt;/code&gt; 用于保存最小堆的各个节点（完全二叉树很适合用数组保存），而 &lt;code&gt;std::three_way_comparable&lt;/code&gt; 则是对容器元素类型的 &lt;code&gt;concept&lt;/code&gt; 约束，要求元素类型必须是可比较的，毕竟不可比较的话也就没有“最小”的概念了。&lt;/p&gt;&#10;&lt;p&gt;&lt;code&gt;sift_down&lt;/code&gt; 和 &lt;code&gt;sift_up&lt;/code&gt; 是相反的一组用于调整堆的操作，&lt;code&gt;sift_down&lt;/code&gt; 用于将一个元素向下调整至满足最小堆的条件，&lt;code&gt;sift_up&lt;/code&gt; 则相反，由此可以知道对于任何一个完全二叉数我们只需要上到下执行 &lt;code&gt;sift_down&lt;/code&gt; 或者从下到上执行 &lt;code&gt;sift_up&lt;/code&gt; 就可以使得堆中的每一个节点都满足最小堆的条件&lt;/p&gt;</description></item><item><title>C++ Coroutine: 通用异步任务 Task</title><link>https://merisky.top/posts/3692590940/</link><pubDate>Sat, 25 Mar 2023 12:19:31 +0000</pubDate><guid>https://merisky.top/posts/3692590940/</guid><description>&lt;p&gt;本文使用协程实现了一个通用的异步任务执行类 Task，支持设置回调函数并将在 Task 完成后执行回调。&lt;/p&gt;&#10;&lt;p&gt;Task、TaskPromise 和 TaskAwaiter 覆盖到了大部分的协程执行过程，把这几个类的实现理解了那基本上就可以说已经理解了 c++ 协程的工作方式。&lt;/p&gt;&#10;&lt;p&gt;阅读下面这段代码的方式建议通过 main 函数开始，对照运行结果一点一点来看。&lt;/p&gt;&#10;&lt;h2 id="Source-Code"&gt;&lt;a href="#Source-Code" class="headerlink" title="Source Code"&gt;&lt;/a&gt;Source Code&lt;/h2&gt;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;110&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;111&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;112&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;113&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;114&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;115&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;116&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;117&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;118&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;119&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;120&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;121&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;122&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;123&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;124&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;125&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;126&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;127&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;128&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;129&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;130&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;131&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;132&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;133&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;134&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;135&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;136&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;137&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;138&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;139&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;140&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;141&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;142&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;143&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;144&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;145&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;146&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;147&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;148&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;149&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;150&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;151&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;152&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;153&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;154&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;155&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;156&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;157&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;158&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;159&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;160&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;161&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;162&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;163&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;164&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;165&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;166&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;167&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;168&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;169&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;170&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;171&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;172&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;173&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;174&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;175&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;176&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;177&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;178&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;179&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;180&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;181&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;182&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;183&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;184&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;185&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;186&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;187&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;188&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;189&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;190&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;191&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;192&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;193&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;194&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;195&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;196&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;197&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;198&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;199&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;200&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;201&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;202&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;203&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;204&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;205&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;206&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;207&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;208&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;209&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;210&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;211&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;212&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;213&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;214&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;215&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;216&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;217&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;218&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;219&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;220&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;221&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;222&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;223&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;224&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;225&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;226&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;227&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;228&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;229&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;230&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;231&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;232&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;233&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;234&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;235&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;236&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;237&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;238&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;239&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;240&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;241&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;242&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;243&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;244&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;245&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;246&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;247&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;248&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;249&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;250&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;251&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;252&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;253&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;254&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;255&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;256&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;257&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;258&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;259&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;260&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;261&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;262&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;263&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;264&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;265&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;266&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;267&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;268&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;269&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;270&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;271&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;272&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;273&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;274&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;275&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;276&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;277&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;278&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;279&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;280&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;281&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;282&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;283&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;284&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;285&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;286&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;287&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;288&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;289&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;290&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;291&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;292&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;293&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;294&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;295&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;296&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;297&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;298&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;299&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;300&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;301&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;302&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;303&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;304&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;305&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;306&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;307&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;308&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;309&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;310&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;311&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;312&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;313&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;314&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;315&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;316&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;317&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;318&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;319&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;320&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;321&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;322&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;iostream&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;coroutine&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;functional&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;exception&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;optional&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;atomic&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;utility&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;chrono&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;thread&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;list&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;condition_variable&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;algorithm&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;include&lt;/span&gt; &lt;span class="string"&gt;&amp;lt;atomic&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="meta"&gt;#&lt;span class="keyword"&gt;define&lt;/span&gt; debug(info) std::cout &amp;lt;&amp;lt; __LINE__ &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot; &amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; __func__ &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;: &amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; info &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="keyword"&gt;typename&lt;/span&gt; Task&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;struct&lt;/span&gt; &lt;span class="title class_"&gt;Result&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 初始化为默认值&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="keyword"&gt;explicit&lt;/span&gt; &lt;span class="title"&gt;Result&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt; &lt;/span&gt;= &lt;span class="keyword"&gt;default&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 当 Task 正常返回时用结果初始化 Result&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="keyword"&gt;explicit&lt;/span&gt; &lt;span class="title"&gt;Result&lt;/span&gt;&lt;span class="params"&gt;(Task &amp;amp;&amp;amp;value)&lt;/span&gt; : _value(value) &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;Construct from value&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 当 Task 抛异常时用异常初始化 Result&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="keyword"&gt;explicit&lt;/span&gt; &lt;span class="title"&gt;Result&lt;/span&gt;&lt;span class="params"&gt;(std::exception_ptr &amp;amp;&amp;amp;exception_ptr)&lt;/span&gt; : _exception_ptr(exception_ptr)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;Construct from exception_ptr&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 读取结果，有异常则抛出异常&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;Task &lt;span class="title"&gt;get_or_throw&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (_exception_ptr)&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::&lt;span class="built_in"&gt;rethrow_exception&lt;/span&gt;(_exception_ptr);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; _value;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;private&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Task _value&amp;#123;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::exception_ptr _exception_ptr;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="keyword"&gt;typename&lt;/span&gt;&amp;gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title class_"&gt;Task&lt;/span&gt;, &lt;span class="keyword"&gt;typename&lt;/span&gt; R&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;struct&lt;/span&gt; &lt;span class="title class_"&gt;TaskAwaiter&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="keyword"&gt;explicit&lt;/span&gt; &lt;span class="title"&gt;TaskAwaiter&lt;/span&gt;&lt;span class="params"&gt;(Task&amp;lt;R&amp;gt; &amp;amp;&amp;amp;task)&lt;/span&gt; &lt;span class="keyword"&gt;noexcept&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; : task(std::move(task))&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;Construct from task&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;TaskAwaiter&lt;/span&gt;(TaskAwaiter &amp;amp;&amp;amp;completion) &lt;span class="keyword"&gt;noexcept&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; : &lt;span class="built_in"&gt;task&lt;/span&gt;(std::&lt;span class="built_in"&gt;exchange&lt;/span&gt;(completion.task, &amp;#123;&amp;#125;))&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;Construct from completion&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;TaskAwaiter&lt;/span&gt;(TaskAwaiter &amp;amp;) = &lt;span class="keyword"&gt;delete&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; TaskAwaiter &amp;amp;&lt;span class="keyword"&gt;operator&lt;/span&gt;=(TaskAwaiter &amp;amp;) = &lt;span class="keyword"&gt;delete&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="keyword"&gt;constexpr&lt;/span&gt; &lt;span class="type"&gt;bool&lt;/span&gt; &lt;span class="title"&gt;await_ready&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt; &lt;span class="type"&gt;const&lt;/span&gt; &lt;span class="keyword"&gt;noexcept&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="literal"&gt;false&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="title"&gt;await_suspend&lt;/span&gt;&lt;span class="params"&gt;(std::coroutine_handle&amp;lt;&amp;gt; handle)&lt;/span&gt; &lt;span class="keyword"&gt;noexcept&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; handle.&lt;span class="built_in"&gt;address&lt;/span&gt;() &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 当 task 执行完之后调用 resume&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; task.&lt;span class="built_in"&gt;finally&lt;/span&gt;([handle]() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; handle.&lt;span class="built_in"&gt;resume&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 协程恢复执行时，被等待的 Task 已经执行完，调用 get_result 来获取结果&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;R &lt;span class="title"&gt;await_resume&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt; &lt;span class="keyword"&gt;noexcept&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; task.&lt;span class="built_in"&gt;get_result&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;private&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Task&amp;lt;R&amp;gt; task;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="keyword"&gt;typename&lt;/span&gt;&amp;gt; &lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title class_"&gt;Task&lt;/span&gt;, &lt;span class="keyword"&gt;typename&lt;/span&gt; ResultType&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;struct&lt;/span&gt; &lt;span class="title class_"&gt;TaskPromise&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="keyword"&gt;auto&lt;/span&gt; &lt;span class="title"&gt;initial_suspend&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt; &lt;span class="keyword"&gt;noexcept&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; std::suspend_never&amp;#123;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="keyword"&gt;auto&lt;/span&gt; &lt;span class="title"&gt;final_suspend&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt; &lt;span class="keyword"&gt;noexcept&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; std::suspend_always&amp;#123;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;Task&amp;lt;ResultType&amp;gt; &lt;span class="title"&gt;get_return_object&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; Task&amp;#123;std::coroutine_handle&amp;lt;TaskPromise&amp;gt;::&lt;span class="built_in"&gt;from_promise&lt;/span&gt;(*&lt;span class="keyword"&gt;this&lt;/span&gt;)&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="title"&gt;unhandled_exception&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;std::lock_guard &lt;span class="title"&gt;lock&lt;/span&gt;&lt;span class="params"&gt;(completion_lock)&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; result = &lt;span class="built_in"&gt;Result&lt;/span&gt;&amp;lt;ResultType&amp;gt;(std::&lt;span class="built_in"&gt;current_exception&lt;/span&gt;());&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; completion.&lt;span class="built_in"&gt;notify_all&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 调用回调&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;notify_callbacks&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="title"&gt;return_value&lt;/span&gt;&lt;span class="params"&gt;(ResultType value)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;std::lock_guard &lt;span class="title"&gt;lock&lt;/span&gt;&lt;span class="params"&gt;(completion_lock)&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; result = &lt;span class="built_in"&gt;Result&lt;/span&gt;&amp;lt;ResultType&amp;gt;(std::&lt;span class="built_in"&gt;move&lt;/span&gt;(value));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; completion.&lt;span class="built_in"&gt;notify_all&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 调用回调&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;notify_callbacks&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;ResultType &lt;span class="title"&gt;get_result&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;from TaskPromise&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 如果 result 没有值，说明协程还没有运行完，等待值被写入再返回&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;std::unique_lock &lt;span class="title"&gt;lock&lt;/span&gt;&lt;span class="params"&gt;(completion_lock)&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (!result.&lt;span class="built_in"&gt;has_value&lt;/span&gt;()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;hasn&amp;#x27;t value now&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 等待写入值之后调用 notify_all&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; completion.&lt;span class="built_in"&gt;wait&lt;/span&gt;(lock);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125; &lt;span class="keyword"&gt;else&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;already has value now&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 如果有值，则直接返回（或者抛出异常）&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; result-&amp;gt;&lt;span class="built_in"&gt;get_or_throw&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="title"&gt;on_completed&lt;/span&gt;&lt;span class="params"&gt;(std::function&amp;lt;&lt;span class="type"&gt;void&lt;/span&gt;(Result&amp;lt;ResultType&amp;gt;)&amp;gt; &amp;amp;&amp;amp;func)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;std::unique_lock &lt;span class="title"&gt;lock&lt;/span&gt;&lt;span class="params"&gt;(completion_lock)&lt;/span&gt;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 加锁判断 result&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (result.&lt;span class="built_in"&gt;has_value&lt;/span&gt;()) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;already has value&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// result 已经有值&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; value = result.&lt;span class="built_in"&gt;value&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 解锁之后再调用 func&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; lock.&lt;span class="built_in"&gt;unlock&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;func&lt;/span&gt;(value);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125; &lt;span class="keyword"&gt;else&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;waiting for execution&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 否则添加回调函数，等待调用&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; completion_callbacks.&lt;span class="built_in"&gt;push_back&lt;/span&gt;(func);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 注意这里的模板参数&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="keyword"&gt;typename&lt;/span&gt; _ResultType&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; &lt;span class="title"&gt;await_transform&lt;/span&gt;&lt;span class="params"&gt;(Task&amp;lt;_ResultType&amp;gt; &amp;amp;&amp;amp;task)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; TaskAwaiter&amp;lt;Task, _ResultType&amp;gt;&amp;#123;std::&lt;span class="built_in"&gt;move&lt;/span&gt;(task)&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;private&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 回调列表，我们允许对同一个 Task 添加多个回调&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::list&amp;lt;std::function&amp;lt;&lt;span class="type"&gt;void&lt;/span&gt;(Result&amp;lt;ResultType&amp;gt;)&amp;gt;&amp;gt; completion_callbacks;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="type"&gt;void&lt;/span&gt; &lt;span class="title"&gt;notify_callbacks&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; value = result.&lt;span class="built_in"&gt;value&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;for&lt;/span&gt; (&lt;span class="keyword"&gt;auto&lt;/span&gt; &amp;amp;callback : completion_callbacks) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;call callback function from completion_callbacks&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;callback&lt;/span&gt;(value);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 调用完成，清空回调&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; completion_callbacks.&lt;span class="built_in"&gt;clear&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 使用 std::optional 可以区分协程是否执行完成&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::optional&amp;lt;Result&amp;lt;ResultType&amp;gt;&amp;gt; result;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::mutex completion_lock;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::condition_variable completion;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;template&lt;/span&gt;&amp;lt;&lt;span class="keyword"&gt;typename&lt;/span&gt; ResultType&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;struct&lt;/span&gt; &lt;span class="title class_"&gt;Task&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 声明 promise_type 为 TaskPromise 类型&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;using&lt;/span&gt; promise_type = TaskPromise&amp;lt;Task, ResultType&amp;gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;ResultType &lt;span class="title"&gt;get_result&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;from Task&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; handle.&lt;span class="built_in"&gt;promise&lt;/span&gt;().&lt;span class="built_in"&gt;get_result&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;Task &amp;amp;&lt;span class="title"&gt;then&lt;/span&gt;&lt;span class="params"&gt;(std::function&amp;lt;&lt;span class="type"&gt;void&lt;/span&gt;(ResultType)&amp;gt; &amp;amp;&amp;amp;func)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;task id = &amp;quot;&lt;/span&gt; + std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(task_id));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; handle.&lt;span class="built_in"&gt;address&lt;/span&gt;() &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; handle.&lt;span class="built_in"&gt;promise&lt;/span&gt;().&lt;span class="built_in"&gt;on_completed&lt;/span&gt;([func](&lt;span class="keyword"&gt;auto&lt;/span&gt; result) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;func&lt;/span&gt;(result.&lt;span class="built_in"&gt;get_or_throw&lt;/span&gt;());&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125; &lt;span class="built_in"&gt;catch&lt;/span&gt; (std::exception &amp;amp;e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 忽略异常&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; *&lt;span class="keyword"&gt;this&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;Task &amp;amp;&lt;span class="title"&gt;catching&lt;/span&gt;&lt;span class="params"&gt;(std::function&amp;lt;&lt;span class="type"&gt;void&lt;/span&gt;(std::exception &amp;amp;)&amp;gt; &amp;amp;&amp;amp;func)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;task id = &amp;quot;&lt;/span&gt; + std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(task_id));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; handle.&lt;span class="built_in"&gt;promise&lt;/span&gt;().&lt;span class="built_in"&gt;on_completed&lt;/span&gt;([func](&lt;span class="keyword"&gt;auto&lt;/span&gt; result) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 忽略返回值&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; result.&lt;span class="built_in"&gt;get_or_throw&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125; &lt;span class="built_in"&gt;catch&lt;/span&gt; (std::exception &amp;amp;e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;func&lt;/span&gt;(e);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; *&lt;span class="keyword"&gt;this&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;Task &amp;amp;&lt;span class="title"&gt;finally&lt;/span&gt;&lt;span class="params"&gt;(std::function&amp;lt;&lt;span class="type"&gt;void&lt;/span&gt;()&amp;gt; &amp;amp;&amp;amp;func)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;task id = &amp;quot;&lt;/span&gt; + std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(task_id));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; handle.&lt;span class="built_in"&gt;address&lt;/span&gt;() &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; handle.&lt;span class="built_in"&gt;promise&lt;/span&gt;().&lt;span class="built_in"&gt;on_completed&lt;/span&gt;([func](&lt;span class="keyword"&gt;auto&lt;/span&gt; result) &amp;#123; &lt;span class="built_in"&gt;func&lt;/span&gt;(); &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; *&lt;span class="keyword"&gt;this&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="function"&gt;&lt;span class="keyword"&gt;explicit&lt;/span&gt; &lt;span class="title"&gt;Task&lt;/span&gt;&lt;span class="params"&gt;(std::coroutine_handle&amp;lt;promise_type&amp;gt; handle)&lt;/span&gt; &lt;span class="keyword"&gt;noexcept&lt;/span&gt;: handle(handle), task_id(cnt)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ++cnt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;Construct from handle, task id = &amp;quot;&lt;/span&gt; + std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(task_id));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; handle.&lt;span class="built_in"&gt;address&lt;/span&gt;() &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;Task&lt;/span&gt;(Task &amp;amp;&amp;amp;task) &lt;span class="keyword"&gt;noexcept&lt;/span&gt;: &lt;span class="built_in"&gt;handle&lt;/span&gt;(std::&lt;span class="built_in"&gt;exchange&lt;/span&gt;(task.handle, &amp;#123;&amp;#125;)), &lt;span class="built_in"&gt;task_id&lt;/span&gt;(cnt)&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ++cnt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;Move Construct from task, task id = &amp;quot;&lt;/span&gt; + std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(task.task_id) &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;-&amp;gt;&amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(task_id));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; handle.&lt;span class="built_in"&gt;address&lt;/span&gt;() &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;Task&lt;/span&gt;(Task &amp;amp;) = &lt;span class="keyword"&gt;delete&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Task &amp;amp;&lt;span class="keyword"&gt;operator&lt;/span&gt;=(Task &amp;amp;) = &lt;span class="keyword"&gt;delete&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ~&lt;span class="built_in"&gt;Task&lt;/span&gt;() &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;if&lt;/span&gt; (handle) handle.&lt;span class="built_in"&gt;destroy&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;private&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::coroutine_handle&amp;lt;promise_type&amp;gt; handle;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;static&lt;/span&gt; std::atomic&amp;lt;&lt;span class="type"&gt;int&lt;/span&gt;&amp;gt; cnt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;const&lt;/span&gt; &lt;span class="type"&gt;int&lt;/span&gt; task_id;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;template&lt;/span&gt; &amp;lt;&lt;span class="keyword"&gt;typename&lt;/span&gt; T&amp;gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;std::atomic&amp;lt;&lt;span class="type"&gt;int&lt;/span&gt;&amp;gt; Task&amp;lt;T&amp;gt;::cnt = &lt;span class="number"&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;Task&amp;lt;&lt;span class="type"&gt;int&lt;/span&gt;&amp;gt; &lt;span class="title"&gt;simple_task2&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;task 2 start ...&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;using&lt;/span&gt; &lt;span class="keyword"&gt;namespace&lt;/span&gt; std::chrono_literals;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::this_thread::&lt;span class="built_in"&gt;sleep_for&lt;/span&gt;(&lt;span class="number"&gt;1&lt;/span&gt;s);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;task 2 returns after 1s.&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;co_return&lt;/span&gt; &lt;span class="number"&gt;2&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;Task&amp;lt;&lt;span class="type"&gt;int&lt;/span&gt;&amp;gt; &lt;span class="title"&gt;simple_task3&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;in task 3 start ...&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;using&lt;/span&gt; &lt;span class="keyword"&gt;namespace&lt;/span&gt; std::chrono_literals;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::this_thread::&lt;span class="built_in"&gt;sleep_for&lt;/span&gt;(&lt;span class="number"&gt;2&lt;/span&gt;s);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;task 3 returns after 2s.&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;co_return&lt;/span&gt; &lt;span class="number"&gt;3&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;Task&amp;lt;&lt;span class="type"&gt;int&lt;/span&gt;&amp;gt; &lt;span class="title"&gt;simple_task&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;task start ...&amp;quot;&lt;/span&gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; result2 = &lt;span class="keyword"&gt;co_await&lt;/span&gt; &lt;span class="built_in"&gt;simple_task2&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;returns from task2: &amp;quot;&lt;/span&gt; + std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(result2));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; result3 = &lt;span class="keyword"&gt;co_await&lt;/span&gt; &lt;span class="built_in"&gt;simple_task3&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;returns from task3: &amp;quot;&lt;/span&gt; + std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(result3));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;co_return&lt;/span&gt; &lt;span class="number"&gt;1&lt;/span&gt; + result2 + result3;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;&lt;span class="type"&gt;int&lt;/span&gt; &lt;span class="title"&gt;main&lt;/span&gt;&lt;span class="params"&gt;()&lt;/span&gt; &lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; simpleTask = &lt;span class="built_in"&gt;simple_task&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;======================&amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; simpleTask.&lt;span class="built_in"&gt;then&lt;/span&gt;([](&lt;span class="type"&gt;int&lt;/span&gt; i) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;simple task end: &amp;quot;&lt;/span&gt; + std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(i));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;).&lt;span class="built_in"&gt;catching&lt;/span&gt;([](std::exception &amp;amp;e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;error occurred&amp;quot;&lt;/span&gt; + std::string&amp;#123;e.&lt;span class="built_in"&gt;what&lt;/span&gt;()&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;======================&amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;try&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; i = simpleTask.&lt;span class="built_in"&gt;get_result&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;simple task end from get: &amp;quot;&lt;/span&gt; + std::&lt;span class="built_in"&gt;to_string&lt;/span&gt;(i));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125; &lt;span class="built_in"&gt;catch&lt;/span&gt; (std::exception &amp;amp;e) &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;debug&lt;/span&gt;(&lt;span class="string"&gt;&amp;quot;error: &amp;quot;&lt;/span&gt; + std::string&amp;#123;e.&lt;span class="built_in"&gt;what&lt;/span&gt;()&amp;#125;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;return&lt;/span&gt; &lt;span class="number"&gt;0&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;h2 id="运行结果分析"&gt;&lt;a href="#运行结果分析" class="headerlink" title="运行结果分析"&gt;&lt;/a&gt;运行结果分析&lt;/h2&gt;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;42&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;43&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;44&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;45&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;46&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;47&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;48&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;49&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;50&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;51&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;52&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;53&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;54&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;55&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;56&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;57&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;58&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;59&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;60&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;61&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;62&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;63&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;64&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;65&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;66&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;67&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;68&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;69&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;70&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;71&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;72&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;73&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;74&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;75&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;76&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;77&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;78&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;79&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;80&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;81&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;82&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;83&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;84&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;85&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;86&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;87&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;88&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;89&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;90&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;91&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;92&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;93&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;94&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;95&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;96&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;97&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;98&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;99&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;100&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;101&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;102&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;103&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;104&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;105&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;106&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;107&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;108&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;109&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;110&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;ASM generation compiler returned: &lt;span class="number"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;Execution build compiler returned: &lt;span class="number"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;Program returned: &lt;span class="number"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里通过 auto simpleTask = simple_task(); 构建第一个 Task1 0x5562ea17feb0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;250&lt;/span&gt; Task: Construct from handle, task id = &lt;span class="number"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea17feb0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 构建协程类第一步 initial_suspend&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;100&lt;/span&gt; initial_suspend: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里因为 suspend_never 所以继往下走&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;295&lt;/span&gt; simple_task: task start ...&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 通过 auto result2 = co_await simple_task2(); 构建第二个 Task2 0x5562ea180fe0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;250&lt;/span&gt; Task: Construct from handle, task id = &lt;span class="number"&gt;1&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea180fe0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;100&lt;/span&gt; initial_suspend: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;279&lt;/span&gt; simple_task2: task &lt;span class="number"&gt;2&lt;/span&gt; start ...&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;282&lt;/span&gt; simple_task2: task &lt;span class="number"&gt;2&lt;/span&gt; returns after &lt;span class="number"&gt;1&lt;/span&gt;s.&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 一直走到 co_return 2; 进入 TaskPromise::return_value&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;126&lt;/span&gt; return_value: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 通过 result = Result&amp;lt;ResultType&amp;gt;(std::move(value)); 用 value(2) 构建了一个 Result&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;26&lt;/span&gt; Result: Construct from value&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 中间调了 completion.notify_all(); 但是这里没有 wait&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 继续在 TaskPromise::return_value 中 notify_callbacks();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;181&lt;/span&gt; notify_callbacks: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// co_return 之后这个协程 0x5562ea180fe0 就结束了 (还没释放，因为 finial 是 suspend_always)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;105&lt;/span&gt; final_suspend: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里把 co_return 的结果给到 Task1 的 TaskPromise&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;170&lt;/span&gt; await_transform: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// await_transform 返回一个 TaskAwaiter, 把刚刚的 Task2 存起来了（移动拷贝）&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;257&lt;/span&gt; Task: Move Construct from task, task id = &lt;span class="number"&gt;1&lt;/span&gt;-&amp;gt;&lt;span class="number"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea180fe0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;55&lt;/span&gt; TaskAwaiter: Construct from task&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里先判断 ready, 直接返回 false&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;70&lt;/span&gt; await_ready: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// false 的情况下需要 suspend (注意这里传入的参数 handle 是 Task1 的 handle)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;76&lt;/span&gt; await_suspend: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea17feb0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里 task.finally([handle]() &amp;#123; 就是刚刚存起来的 Task2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 传了一个回调去 handle.resume();&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;241&lt;/span&gt; finally: task id = &lt;span class="number"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea180fe0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// handle.promise().on_completed([func](auto result) &amp;#123; func(); &amp;#125;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;149&lt;/span&gt; on_completed: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里判断了一下 result 有没有值, 那么这里因为是 Task2 的 Promise &lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 所以 result 在刚刚 return_value 填充上了&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;153&lt;/span&gt; on_completed: already has value&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里是通过刚刚的回调调到的 Task1 Promise 的 handle.resume()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;87&lt;/span&gt; await_resume: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里 return task.get_result(); 这里的 task 是刚刚传到 TaskAwaiter 的 Task2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;207&lt;/span&gt; get_result: from Task&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;136&lt;/span&gt; get_result: from TaskPromise&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里还是一样的 result 是 value(2)&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;144&lt;/span&gt; get_result: already has value now&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里因为 TaskAwaiter 在 finally 中 resume 了 Task1 的 handle 所以继续进行&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;297&lt;/span&gt; simple_task: returns from task2: &lt;span class="number"&gt;2&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 通过 auto result3 = co_await simple_task3(); 构建 Task3 0x5562ea180fe0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;250&lt;/span&gt; Task: Construct from handle, task id = &lt;span class="number"&gt;3&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea180fe0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 后面 Task3 执行和返回的过程大部分都一样的&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;100&lt;/span&gt; initial_suspend: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;287&lt;/span&gt; simple_task3: in task &lt;span class="number"&gt;3&lt;/span&gt; start ...&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;290&lt;/span&gt; simple_task3: task &lt;span class="number"&gt;3&lt;/span&gt; returns after &lt;span class="number"&gt;2&lt;/span&gt;s.&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;126&lt;/span&gt; return_value: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;26&lt;/span&gt; Result: Construct from value&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;181&lt;/span&gt; notify_callbacks: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;105&lt;/span&gt; final_suspend: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;170&lt;/span&gt; await_transform: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;257&lt;/span&gt; Task: Move Construct from task, task id = &lt;span class="number"&gt;3&lt;/span&gt;-&amp;gt;&lt;span class="number"&gt;4&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea180fe0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;55&lt;/span&gt; TaskAwaiter: Construct from task&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;70&lt;/span&gt; await_ready: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;76&lt;/span&gt; await_suspend: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea17feb0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;241&lt;/span&gt; finally: task id = &lt;span class="number"&gt;4&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea180fe0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;149&lt;/span&gt; on_completed: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;153&lt;/span&gt; on_completed: already has value&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;87&lt;/span&gt; await_resume: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;207&lt;/span&gt; get_result: from Task&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;136&lt;/span&gt; get_result: from TaskPromise&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;144&lt;/span&gt; get_result: already has value now&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 一直到这里同样 Task1 通过 Task3 的回调被 resume 继续执行&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;299&lt;/span&gt; simple_task: returns from task3: &lt;span class="number"&gt;3&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里 co_return 1 + result2 + result3; 调到 return_value&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;126&lt;/span&gt; return_value: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里和前面一样同样构建了 Result&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;26&lt;/span&gt; Result: Construct from value&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;181&lt;/span&gt; notify_callbacks: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里 Task 也走完了, suspend&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;105&lt;/span&gt; final_suspend: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;======================&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 刚刚所有的过程只在声明了 simpleTask 之后就执行完了&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 接着调用 simpleTask.then()&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;213&lt;/span&gt; then: task id = &lt;span class="number"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;0x5562ea17feb0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 那这里同样之前已经有值了&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;149&lt;/span&gt; on_completed: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;153&lt;/span&gt; on_completed: already has value&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里通过 on_completed 调回到 lambda 回调 debug(&amp;quot;simple task end: &amp;quot; + std::to_string(i));&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;306&lt;/span&gt; &lt;span class="built_in"&gt;operator&lt;/span&gt;(): simple task end: &lt;span class="number"&gt;6&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// 这里 catch 了一下异常，但是因为没有异常所以没有触发 debug(&amp;quot;error: &amp;quot; + std::string&amp;#123;e.what()&amp;#125;);&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;227&lt;/span&gt; catching: task id = &lt;span class="number"&gt;0&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;149&lt;/span&gt; on_completed: &lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;153&lt;/span&gt; on_completed: already has value&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;======================&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// try-catch 里的逻辑也类似&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;207&lt;/span&gt; get_result: from Task&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;136&lt;/span&gt; get_result: from TaskPromise&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;144&lt;/span&gt; get_result: already has value now&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="number"&gt;313&lt;/span&gt; main: simple task end from get: &lt;span class="number"&gt;6&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="References"&gt;&lt;a href="#References" class="headerlink" title="References"&gt;&lt;/a&gt;References&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="url:" title="https://www.bennyhuo.com/2022/03/19/cpp-coroutines-04-task"&gt;渡劫 C++ 协程（4）：通用异步任务 Task&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;</description></item><item><title>Ceph Client 中的 LRU 设计</title><link>https://merisky.top/posts/3763995770/</link><pubDate>Wed, 08 Mar 2023 13:38:20 +0000</pubDate><guid>https://merisky.top/posts/3763995770/</guid><description>&lt;p&gt;在 Ceph 中 Client 和 MDS 部分的 lru 实现和淘汰策略稍有不同，但作用都是用来管理 dentry 等结构，方便及时将不使用的文件和目录清出内存，这里主要关注 lru 的实现而不关注 dentry 的淘汰时机。&lt;/p&gt;&#10;&lt;p&gt;首先看下 LRU 的结构：&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// lru.h&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;class&lt;/span&gt; &lt;span class="title class_"&gt;LRU&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="comment"&gt;// ...&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;private&lt;/span&gt;:&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;using&lt;/span&gt; LRUList = xlist&amp;lt;LRUObject*&amp;gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; LRUList top, bottom, pintail;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;这里和大部分 LRU 的实现类似， ceph 也使用一个双向链表作为 lru 的基础结构（也就是这里的 xlist，基本可以当作一个双向链表看待）。只不过 ceph 同时使用了三个链表 top、bottom 和 pintail 来细化 lru 的不同位置。&lt;/p&gt;</description></item><item><title>C++20 Asio With Boost 获取 B 站徽章（直播间牌子）</title><link>https://merisky.top/posts/173569173/</link><pubDate>Sat, 25 Feb 2023 14:17:24 +0000</pubDate><guid>https://merisky.top/posts/173569173/</guid><description>&lt;p&gt;本文介绍了如何使用 C++20 with Asio (Boost 版本) 完成一个简单的客户端用于获取 B 站徽章。&lt;/p&gt;&#10;&lt;p&gt;强烈推荐昨天发现的一个视频 《Why C++20 is the Awesomest Language for Network Programming》，可以去油管上搜一下，总时长一个小时，比较长但是讲的很好，听的巨舒服，上次有这种感觉还是听那个 c10k 问题的视频。&lt;/p&gt;&#10;&lt;p&gt;总之先看一下程序执行的效果：&lt;/p&gt;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;➜ bili-medal git:(master) ./build/main &amp;#123;my_cookie&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;当前登录账号的 mid 为 29248492&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;当前登陆账号所佩戴的直播间徽章为 &lt;span class="string"&gt;&amp;quot;ASAKI&amp;quot;&lt;/span&gt;, 等级为 15 级&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;贴一下主要逻辑：&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;14&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;15&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;16&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;17&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;18&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;19&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;20&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;21&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;22&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;23&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;24&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;25&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;26&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;27&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;28&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;29&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;30&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;31&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;32&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;33&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;34&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;35&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;36&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;37&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;38&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;39&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;40&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;41&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;42&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;awaitable&amp;lt;&lt;span class="type"&gt;void&lt;/span&gt;&amp;gt; &lt;span class="title"&gt;start&lt;/span&gt;&lt;span class="params"&gt;(asio::io_context &amp;amp;ctx, &lt;span class="type"&gt;const&lt;/span&gt; std::string &amp;amp;cookie)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;span class="function"&gt;&lt;/span&gt;&amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;const&lt;/span&gt; std::string host = &lt;span class="string"&gt;&amp;quot;api.bilibili.com&amp;quot;&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 解析域名&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; [e1, endpoint] = &lt;span class="keyword"&gt;co_await&lt;/span&gt; tcp::&lt;span class="built_in"&gt;resolver&lt;/span&gt;(ctx).&lt;span class="built_in"&gt;async_resolve&lt;/span&gt;(host, &lt;span class="string"&gt;&amp;quot;https&amp;quot;&lt;/span&gt;, use_nothrow_awaitable);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// ssl_ctx 用于建立 https 连接&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ssl::context ssl_ctx &amp;#123;ssl::context::sslv23&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ssl_socket socket &amp;#123;ctx, ssl_ctx&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="built_in"&gt;static_assert&lt;/span&gt;(std::is_same_v&amp;lt;std::&lt;span class="type"&gt;decay_t&lt;/span&gt;&amp;lt;&lt;span class="keyword"&gt;decltype&lt;/span&gt;(socket.&lt;span class="built_in"&gt;next_layer&lt;/span&gt;())&amp;gt;, tcp::socket&amp;gt;);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 建立 tcp 连接&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ssl_ctx.&lt;span class="built_in"&gt;set_default_verify_paths&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; [e2, _] = &lt;span class="keyword"&gt;co_await&lt;/span&gt; asio::&lt;span class="built_in"&gt;async_connect&lt;/span&gt;(socket.&lt;span class="built_in"&gt;lowest_layer&lt;/span&gt;(), endpoint, use_nothrow_awaitable);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// https 握手，建立 https 连接&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; [e3] = &lt;span class="keyword"&gt;co_await&lt;/span&gt; socket.&lt;span class="built_in"&gt;async_handshake&lt;/span&gt;(ssl::stream_base::client, use_nothrow_awaitable);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::string mid;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 创建并发送一个 api 请求到 B 站服务器，获得返回结果并解析 mid 字段&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; request = &lt;span class="built_in"&gt;make_request&lt;/span&gt;(http::verb::get, host, &lt;span class="string"&gt;&amp;quot;/x/web-interface/nav&amp;quot;&lt;/span&gt;, cookie);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; response = &lt;span class="keyword"&gt;co_await&lt;/span&gt; &lt;span class="built_in"&gt;async_send_request&lt;/span&gt;(socket, request);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; data = json::&lt;span class="built_in"&gt;parse&lt;/span&gt;(beast::&lt;span class="built_in"&gt;buffers_to_string&lt;/span&gt;(response.&lt;span class="built_in"&gt;body&lt;/span&gt;().&lt;span class="built_in"&gt;data&lt;/span&gt;()));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; mid = data[&lt;span class="string"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;mid&amp;quot;&lt;/span&gt;].&lt;span class="built_in"&gt;dump&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;当前登录账号的 mid 为 &amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; mid &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::string medal_id, medal_name, medal_level;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#123;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="comment"&gt;// 创建并发送一个 api 请求到 B 站服务器，获得返回结果并解析直播间徽章&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; request = &lt;span class="built_in"&gt;make_request&lt;/span&gt;(http::verb::get, host, &lt;span class="string"&gt;&amp;quot;/x/space/acc/info?mid=&amp;quot;&lt;/span&gt;+mid, cookie);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; response = &lt;span class="keyword"&gt;co_await&lt;/span&gt; &lt;span class="built_in"&gt;async_send_request&lt;/span&gt;(socket, request);&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="keyword"&gt;auto&lt;/span&gt; data = json::&lt;span class="built_in"&gt;parse&lt;/span&gt;(beast::&lt;span class="built_in"&gt;buffers_to_string&lt;/span&gt;(response.&lt;span class="built_in"&gt;body&lt;/span&gt;().&lt;span class="built_in"&gt;data&lt;/span&gt;()));&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; medal_id = data[&lt;span class="string"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;fans_medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal_id&amp;quot;&lt;/span&gt;].&lt;span class="built_in"&gt;dump&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; medal_level = data[&lt;span class="string"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;fans_medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;level&amp;quot;&lt;/span&gt;].&lt;span class="built_in"&gt;dump&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; medal_name = data[&lt;span class="string"&gt;&amp;quot;data&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;fans_medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal&amp;quot;&lt;/span&gt;][&lt;span class="string"&gt;&amp;quot;medal_name&amp;quot;&lt;/span&gt;].&lt;span class="built_in"&gt;dump&lt;/span&gt;();&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &amp;#125;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; std::cout &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;当前登陆账号所佩戴的直播间徽章为 &amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; medal_name &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot;, 等级为 &amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; medal_level &amp;lt;&amp;lt; &lt;span class="string"&gt;&amp;quot; 级&amp;quot;&lt;/span&gt; &amp;lt;&amp;lt; std::endl;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;整体流程我觉得是比较清晰的，基本上对 https 请求过程有所了解的话应该能容易能理解这里做了什么。&lt;/p&gt;</description></item><item><title>ChatGPT 注册教程</title><link>https://merisky.top/posts/3773623110/</link><pubDate>Sat, 18 Feb 2023 12:55:03 +0000</pubDate><guid>https://merisky.top/posts/3773623110/</guid><description>&lt;p&gt;ChatGPT 推出已经有一段时间了，但是由于地区政策国内很多小伙伴都还没有能有机会区体验 AI 的魅力，这里就给大家介绍一下如何注册和使用 ChatGPT&lt;/p&gt;&#10;&lt;h2 id="前期准备"&gt;&lt;a href="#前期准备" class="headerlink" title="前期准备"&gt;&lt;/a&gt;前期准备&lt;/h2&gt;&lt;p&gt;要使用 ChatGPT, 你需要如下几样东西：&lt;/p&gt;&#10;&lt;figure class="highlight bash"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;- 一个科学且靠谱的上网办法&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;- 一个邮箱用于接收验证码&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;- 一个 Google 账号用于快速注册登录 (可选)&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;接着切换你的代理节点，尝试打开 ChatGPT 的&lt;a target="_blank" rel="noopener" href="https://chat.openai.com/auth/login"&gt;登陆页面&lt;/a&gt;&lt;/p&gt;&#10;&lt;p&gt;如果你看到如下画面：&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://merisky.top/images/ChatGPT%20%E6%B3%A8%E5%86%8C%E5%8F%8A%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%20794dfe8bcd30494794be9a24bd703999/Untitled.png" loading="lazy" decoding="async" alt="Untitled"&gt;&lt;/p&gt;&#10;&lt;p&gt;恭喜你此节点可以用于访问 ChatGPT，但如果 ChatGPT 提示 &lt;code&gt;Access Deined&lt;/code&gt;,那就说明此节点被列入黑名单，需要换一个节点再试。&lt;/p&gt;&#10;&lt;p&gt;另外需要注意的一点是，尽量优先尝试使用非香港节点登录 ChatGPT&lt;/p&gt;&#10;&lt;p&gt;确认节点是否可用的原因是等下会用到一个接码平台用于注册 ChatGPT，要花几块钱，这里如果没有可用节点的话，等下充值甚至注册了没法用就很难受。&lt;/p&gt;&#10;&lt;h2 id="开始注册"&gt;&lt;a href="#开始注册" class="headerlink" title="开始注册"&gt;&lt;/a&gt;开始注册&lt;/h2&gt;&lt;p&gt;前期准备完成以后点击 &lt;code&gt;Sign up&lt;/code&gt; 进入 ChatGPT 的&lt;a target="_blank" rel="noopener" href="https://auth0.openai.com/u/signup/identifier?state=hKFo2SBoVzczLUZ4dm54X2NpUTV6YS1sdlNNVkZIc1ctWEZMT6Fur3VuaXZlcnNhbC1sb2dpbqN0aWTZIEVmVmdMd3NnSzh2aU4yZnQwOFpvTUliSTEwSWM4Wndoo2NpZNkgVGRKSWNiZTE2V29USHROOTVueXl3aDVFNHlPbzZJdEc"&gt;注册页面&lt;/a&gt;，显示如下：&lt;/p&gt;</description></item><item><title>如何向 STL 算法中传入重载函数</title><link>https://merisky.top/posts/781272769/</link><pubDate>Fri, 01 Jul 2022 04:02:45 +0000</pubDate><guid>https://merisky.top/posts/781272769/</guid><description>&lt;p&gt;本文分享如何向 STL 算法中传入重载函数，来自 Jonathan 2017 年在 Fluent C++ 上发起的一个挑战&lt;/p&gt;&#10;&lt;figure class="highlight cpp"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;The STL is a fantastic tool to make your code more expressive &lt;span class="keyword"&gt;and&lt;/span&gt; more robust. If you’re a C++ developer &lt;span class="keyword"&gt;and&lt;/span&gt; want to become proficient, it is essential that you learn the STL.&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;But there is one &lt;span class="keyword"&gt;case&lt;/span&gt; where we can’t apply STL algorithms right out of the box: when the function passed has overloads.&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;大概意思是说 STL 本身非常好用，但是如果传递一个具有重载的函数就会使得 STL 失效。&lt;/p&gt;</description></item><item><title>RAC 集群访问过程分析</title><link>https://merisky.top/posts/4037772901/</link><pubDate>Fri, 15 Apr 2022 09:09:54 +0000</pubDate><guid>https://merisky.top/posts/4037772901/</guid><description>&lt;p&gt;本文介绍了通过 WireShark 分析的在 RAC11gR2 集群访问中连接建立的过程&lt;/p&gt;&#10;&lt;h3 id="环境"&gt;&lt;a href="#环境" class="headerlink" title="环境"&gt;&lt;/a&gt;环境&lt;/h3&gt;&lt;blockquote&gt;&#10;&lt;p&gt;环境：&lt;/p&gt;&#10;&lt;p&gt;主机：192.168.40.37&lt;/p&gt;&#10;&lt;p&gt;SCAN节点：192.168.40.234&lt;/p&gt;&#10;&lt;p&gt;节点1：192.168.40.230 vip：192.168.40.231&lt;/p&gt;&#10;&lt;p&gt;节点2：192.168.40.232 vip：192.168.40.233&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;h3 id="连接过程"&gt;&lt;a href="#连接过程" class="headerlink" title="连接过程"&gt;&lt;/a&gt;连接过程&lt;/h3&gt;&lt;ol&gt;&#10;&lt;li&gt;主机与scan节点进行三次握手，建立tcp连接（目的端口1521）&lt;/li&gt;&#10;&lt;li&gt;主机向scan节点发送Oracle-TNS（Transparent Network Substrate Protocol）Request，主要携带信息如下：&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;figure class="highlight plaintext"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;6&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;7&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;8&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;9&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;10&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;11&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;12&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;13&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;14&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;Packet Type: Connect (1)&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;Connect&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Version: 314&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Service Options: 0x0041&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Session Data Unit Size: 8192&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Maximum Transmission Data Unit Size: 32767&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; NT Protocol Characteristics:0xc60e, Hangon to listener connect, Confirmed release, Callback IO supported, ASync IO Supported, Generate SIGURG signal, Urgent IO supported, Full duplex IO supported&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Value of 1 in Hardware: 0100&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Connect Flags 0:0x41, NA services wanted&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Connect Flags 1:0x41, NA services wanted&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Trace Cross Facility Item 1: 0x00000000&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Trace Cross Facility Item 2: 0x00000000&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Trace Unique Connection ID: 0x0000000000000000&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; Connect Data: (DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=MER)(CID=(PROGRAM=E:\Navicat?for?Oracle\navicat.exe)(HOST=DESKTOP-PSOJPQF)(USER=MER)))(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.40.234)(PORT=1521)))&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;ol start="3"&gt;&#10;&lt;li&gt;&lt;p&gt;scan返回TNS Response, Redirect消息&lt;/p&gt;</description></item><item><title>Windows7 下安装 Oracle RAC11gR2</title><link>https://merisky.top/posts/324638463/</link><pubDate>Tue, 12 Apr 2022 03:24:51 +0000</pubDate><guid>https://merisky.top/posts/324638463/</guid><description>&lt;p&gt;本文介绍了如何在 Window7 环境下安装 Oracle RAC11gR2 的方法以及安装过程中遇到的一些问题的解决方法&lt;/p&gt;&#10;&lt;h2 id="资源"&gt;&lt;a href="#资源" class="headerlink" title="资源"&gt;&lt;/a&gt;资源&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;win64_11gR2_grid&lt;/li&gt;&#10;&lt;li&gt;win64_11gR2_database_1of2&lt;/li&gt;&#10;&lt;li&gt;win64_11gR2_database_2of2&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;以上资源可在官网下载&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;安装文档以 &lt;a href="#page0"&gt;Rac11gR2OnWindows.pdf&lt;/a&gt; 为准，由于文档上面有很多步骤比较省略，且更接近真实环境下的配置方案，因此找了 一篇安装步骤非常接近官方文档的博客&lt;a href="#page1"&gt;&lt;sup&gt;1&lt;/sup&gt;&lt;/a&gt;作为参考，同时结合其他博客进行安装&lt;/p&gt;&#10;&lt;p&gt;首先给出网络拓扑结构如下，后面遇到问题可以返回来作为参照&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://merisky.top/images/Windows7%E4%B8%8B%E5%AE%89%E8%A3%85Oracle-RAC11gR2/image-20200717110829833.png" loading="lazy" decoding="async" alt="总体网络拓扑"&gt;&lt;/p&gt;&#10;&lt;p&gt;正式开始配置&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;h2 id="配置步骤"&gt;&lt;a href="#配置步骤" class="headerlink" title="配置步骤"&gt;&lt;/a&gt;配置步骤&lt;/h2&gt;&lt;h3 id="虚拟机准备"&gt;&lt;a href="#虚拟机准备" class="headerlink" title="虚拟机准备"&gt;&lt;/a&gt;虚拟机准备&lt;/h3&gt;&lt;ol&gt;&#10;&lt;li&gt;创建一个 Windows7 虚拟机（使用 VMware® Workstation 15 pro）&lt;/li&gt;&#10;&lt;li&gt;安装 jdk （版本应在 1.5 及以上）&lt;/li&gt;&#10;&lt;li&gt;为虚拟机添加一块网卡&lt;/li&gt;&#10;&lt;li&gt;克隆虚拟机&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h3 id="✴︎节点名称配置"&gt;&lt;a href="#✴︎节点名称配置" class="headerlink" title="✴︎节点名称配置"&gt;&lt;/a&gt;✴︎节点名称配置&lt;/h3&gt;&lt;p&gt;修改主机名、统一用户名密码，分别更改两台服务器的主机名为 cluster1 和 cluster2，需要重启后生效，两台服务器统一使用 administrator 用户，并且保持密码一致。&lt;/p&gt;</description></item><item><title>Nginx 多阶段 Http 请求处理流程</title><link>https://merisky.top/posts/649054200/</link><pubDate>Mon, 11 Apr 2022 09:42:32 +0000</pubDate><guid>https://merisky.top/posts/649054200/</guid><description>&lt;p&gt;Nginx 通过将各个阶段的所有模块按序的组织成一条执行链，以流水线的形式依次进行处理。&lt;/p&gt;&#10;&lt;h2 id="执行链及流程"&gt;&lt;a href="#执行链及流程" class="headerlink" title="执行链及流程"&gt;&lt;/a&gt;执行链及流程&lt;/h2&gt;&lt;p&gt;nginx执行链的定义如下：&lt;/p&gt;&#10;&lt;figure class="highlight c"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="keyword"&gt;typedef&lt;/span&gt; &lt;span class="class"&gt;&lt;span class="keyword"&gt;struct&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;ngx_http_phase_handler_t&lt;/span&gt; *handlers; &lt;span class="comment"&gt;/*执行链*/&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;ngx_uint_t&lt;/span&gt; server_rewrite_index;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;ngx_uint_t&lt;/span&gt; location_rewrite_index;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125; &lt;span class="type"&gt;ngx_http_phase_engine_t&lt;/span&gt;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;执行链节点的数据结构定义如下：&lt;/p&gt;&#10;&lt;figure class="highlight c"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;4&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;5&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;&lt;span class="class"&gt;&lt;span class="keyword"&gt;struct&lt;/span&gt; &lt;span class="title"&gt;ngx_http_phase_handler_s&lt;/span&gt; &amp;#123;&lt;/span&gt;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ngx_http_phase_handler_pt checker;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; ngx_http_handler_pt handler;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt; &lt;span class="type"&gt;ngx_uint_t&lt;/span&gt; next;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;&amp;#125;;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;对于执行链节点，相同阶段具有相同的checker函数，handler中则保存的是挂载至该阶段的模块处理函数，一般在checker函数中会执行当前节点的handler函数&lt;/p&gt;&#10;&lt;p&gt;执行链的执行流程是按照执行链顺序向前执行，但某个阶段需要回跳或跳过之后的某些执行阶段，next字段保存的就是跳跃的目的索引。&lt;/p&gt;&#10;&lt;p&gt;各个阶段如下：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;NGX_HTTP_POST_READ_PHASE&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;接受完请求头之后的第一个阶段，位于uri重写之前，默认情况下该阶段被跳过;&lt;/p&gt;&#10;&lt;p&gt;nginx源码中仅有realip模块在POST_READ阶段对客户端ip进行了替换，且该模块并未被默认编译进nginx&lt;/p&gt;&#10;&lt;p&gt;POST_HEAD阶段的checker函数仅调用对应的handler函数，随后对返回值进行处理，当handler返回NGX_OK时进入下一阶段&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;NGX_HTTP_SERVER_REWRITE_PHASE&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;server级别的uri重写阶段，执行于server块内，location块外的重写指令。&lt;/p&gt;&#10;&lt;p&gt;nginx的rewrite模块在此阶段提供url重写指令rewrite和变量指令set，以及逻辑控制指令if、break和return以供用户完成一些简单的需求而不必注册handler&lt;/p&gt;&#10;&lt;p&gt;该阶段的请求未被匹配至具体的location中&lt;/p&gt;&#10;&lt;p&gt;SERVER_REWRITE阶段的checker函数同样调用handler函数，但处理方式稍有不同，当handler返回NGX_DECLINED时进入下一阶段&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;NGX_HTTP_FIND_CONFIG_PHASE&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;这一阶段根据重写过的uri查找对应的location，可能被执行多次&lt;/p&gt;&#10;&lt;p&gt;通过ngx_http_core_find_location函数完成对r-&amp;gt;loc_conf的设置，然后调用ngx_http_update_location_config更新请求相关配置&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;NGX_HTTP_REWRITE_PHASE&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;location级别的重写阶段，该阶段执行location基本的重写指令，可能被执行多次。&lt;/p&gt;&#10;&lt;p&gt;与SERVER_REWRITE的逻辑基本相同，使用相同的handler函数，只是执行的时机不同。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;NGX_HTTP_POST_REWRITE_PHASE&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;该阶段不能挂载handler，仅用于检查REWRITE阶段是否进行uri重写，若发生重写则返回再次执行REWRITE阶段，默认限制的重写次数为10次&lt;/p&gt;</description></item><item><title>Manacher's Algorithm（求最长回文子串）</title><link>https://merisky.top/posts/4278205636/</link><pubDate>Sat, 25 Jul 2020 09:47:41 +0000</pubDate><guid>https://merisky.top/posts/4278205636/</guid><description>&lt;p&gt;本文讲述 Manacher 算法（马拉车算法）的原理以及如何利用 Manacher 算法获取最长回文子串。&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;给定一个字符串s，找到这个字符串的最长回文子串&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Example 1:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;figure class="highlight plaintext"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;3&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;Input: &amp;quot;babad&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;Output: &amp;quot;bab&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;Note: &amp;quot;aba&amp;quot; is also a valid answer.&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;p&gt;&lt;strong&gt;Example 2:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;figure class="highlight plaintext"&gt;&lt;table&gt;&lt;tr&gt;&lt;td class="gutter"&gt;&lt;pre&gt;&lt;span class="line"&gt;1&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;2&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;td class="code"&gt;&lt;pre&gt;&lt;span class="line"&gt;Input: &amp;quot;cbbd&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;span class="line"&gt;Output: &amp;quot;bb&amp;quot;&lt;/span&gt;&lt;br&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;/figure&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;要找到一个字符串中的最长回文子串有以下几种方法：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;暴力搜索，算法复杂度O(N&lt;sup&gt;3&lt;/sup&gt;)&lt;/li&gt;&#10;&lt;li&gt;动态规划，算法复杂度O(N&lt;sup&gt;2&lt;/sup&gt;)&lt;/li&gt;&#10;&lt;li&gt;Manacher算法，算法复杂度O(N)&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;而本文的主角就是Manacher’s algorithm，一起来看看它的实现&lt;/p&gt;&#10;&lt;h2 id="核心思想"&gt;&lt;a href="#核心思想" class="headerlink" title="核心思想"&gt;&lt;/a&gt;核心思想&lt;/h2&gt;&lt;p&gt;Manacher算法的核心思想在于搜索以每个字符为中心的回文子串长度，并利用先前搜索的结果加快后续回文子串长度的计算&lt;/p&gt;&#10;&lt;p&gt;其中从中心向两边搜索是Manacher和动态规划方法相比暴力搜索降低复杂度的关键，而利用先前的搜索结果加快后续回文子串计算则是Manacher算法能够以不可思议的O(N)复杂度完成回文串查找的关键&lt;/p&gt;&#10;&lt;h2 id="过程"&gt;&lt;a href="#过程" class="headerlink" title="过程"&gt;&lt;/a&gt;过程&lt;/h2&gt;&lt;p&gt;以Manacher算法的一个中间过程为例&lt;/p&gt;&#10;&lt;p&gt;&lt;img src="https://merisky.top/images/Manacher-s-Algorithm/%E6%88%AA%E5%B1%8F2020-07-24%2023.54.30.png" loading="lazy" decoding="async" alt="截屏2020-07-24 23.54.30"&gt;&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;s：&lt;/strong&gt;给定的字符串&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;P：&lt;/strong&gt;手动维护，&lt;code&gt;P[i]&lt;/code&gt;保存以i位置为中心的最长子串长度&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Center：&lt;/strong&gt;当前最近的回文子串中心&lt;/p&gt;</description></item><item><title>关于</title><link>https://merisky.top/about/</link><pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate><guid>https://merisky.top/about/</guid><description>&lt;p&gt;这里是 Mer 的个人博客，记录技术学习、源码阅读和问题分析。&lt;/p&gt;&#10;&lt;p&gt;文章主要涉及 Ceph、C++、算法与系统开发。希望这些笔记既能帮助自己回顾，也能给遇到相似问题的人一些参考。&lt;/p&gt;&#10;&lt;h2 id="联系与订阅"&gt;联系与订阅&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;a href="https://github.com/Mer1997"&gt;GitHub：Mer1997&lt;/a&gt;&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://merisky.top/index.xml"&gt;通过 RSS 订阅文章&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="阅读说明"&gt;阅读说明&lt;/h2&gt;&#10;&lt;p&gt;技术文章有其适用版本和时间背景。较早的文章保留了当时的分析，请结合文章日期和实际软件版本阅读。&lt;/p&gt;&#10;&lt;p&gt;旧文的“存档更新”来自历史站点文件的提交记录，不一定表示正文在当天修改过。&lt;/p&gt;</description></item></channel></rss>