From 88ca5bca2b3d351d1ebf87ef5a1c1ad4d8c8eed8 Mon Sep 17 00:00:00 2001
From: Koschei <nitianzero@gmail.com>
Date: Wed, 26 Jun 2024 12:29:47 +0800
Subject: [PATCH] fix: resolve memory leaks

---
 src/index/ix_index_handle.cpp           | 67 +++++++++++++------------
 src/index/ix_index_handle.h             | 36 +++++++------
 src/index/ix_manager.h                  | 13 +++--
 src/index/ix_scan.cpp                   |  2 +-
 src/rmdb.cpp                            | 21 ++++++--
 src/transaction/transaction_manager.cpp |  2 +
 src/transaction/transaction_manager.h   | 12 ++++-
 7 files changed, 95 insertions(+), 58 deletions(-)

diff --git a/src/index/ix_index_handle.cpp b/src/index/ix_index_handle.cpp
index b7a2821..7f336d4 100644
--- a/src/index/ix_index_handle.cpp
+++ b/src/index/ix_index_handle.cpp
@@ -240,6 +240,7 @@ IxIndexHandle::IxIndexHandle(DiskManager *disk_manager, BufferPoolManager *buffe
     disk_manager_->read_page(fd, IX_FILE_HDR_PAGE, buf, PAGE_SIZE);
     file_hdr_ = new IxFileHdr();
     file_hdr_->deserialize(buf);
+    delete []buf;
 
     // disk_manager管理的fd对应的文件中,设置从file_hdr_->num_pages开始分配page_no
     // int now_page_no = disk_manager_->get_fd2pageno(fd);
@@ -255,8 +256,9 @@ IxIndexHandle::IxIndexHandle(DiskManager *disk_manager, BufferPoolManager *buffe
  * @note need to Unlatch and unpin the leaf node outside!
  * 注意:用了FindLeafPage之后一定要unlatch叶结点,否则下次latch该结点会堵塞!
  */
-std::pair<IxNodeHandle *, bool> IxIndexHandle::find_leaf_page(const char *key, Operation operation,
-                                                              Transaction *transaction, bool find_first) {
+std::pair<std::shared_ptr<IxNodeHandle>, bool> IxIndexHandle::find_leaf_page(const char *key, Operation operation,
+                                                                             Transaction *transaction,
+                                                                             bool find_first) {
     // Todo:
     // 1. 获取根节点
     // 2. 从根节点开始不断向下查找目标key
@@ -338,14 +340,14 @@ bool IxIndexHandle::get_value(const char *key, std::vector<Rid> *result, Transac
  * @note need to unpin the new node outside
  * 注意:本函数执行完毕后,原node和new node都需要在函数外面进行unpin
  */
-IxNodeHandle *IxIndexHandle::split(IxNodeHandle *node) {
+std::shared_ptr<IxNodeHandle> IxIndexHandle::split(std::shared_ptr<IxNodeHandle> &node) {
     // Todo:
     // 1. 将原结点的键值对平均分配,右半部分分裂为新的右兄弟结点
     //    需要初始化新节点的page_hdr内容
     // 2. 如果新的右兄弟结点是叶子结点,更新新旧节点的prev_leaf和next_leaf指针
     //    为新节点分配键值对,更新旧节点的键值对数记录
     // 3. 如果新的右兄弟结点不是叶子结点,更新该结点的所有孩子结点的父节点信息(使用IxIndexHandle::maintain_child())
-    auto &&new_sibling_node = create_node();
+    auto new_sibling_node = create_node();
     new_sibling_node->page->WLatch();
     auto &&split_point = node->get_min_size();
     if (node->is_leaf_page()) {
@@ -393,8 +395,8 @@ IxNodeHandle *IxIndexHandle::split(IxNodeHandle *node) {
  * 右半部分的键值对分裂为新的右兄弟节点,在参数中称为new_node(参考Split函数来理解old_node和new_node)
  * @note 本函数执行完毕后,new node和old node都需要在函数外面进行unpin
  */
-void IxIndexHandle::insert_into_parent(IxNodeHandle *old_node, const char *key, IxNodeHandle *new_node,
-                                       Transaction *transaction) {
+void IxIndexHandle::insert_into_parent(std::shared_ptr<IxNodeHandle> &old_node, const char *key,
+                                       std::shared_ptr<IxNodeHandle> &new_node, Transaction *transaction) {
     // Todo:
     // 1. 分裂前的结点(原结点, old_node)是否为根结点,如果为根结点需要分配新的root
     // 2. 获取原结点(old_node)的父亲结点
@@ -569,8 +571,8 @@ bool IxIndexHandle::delete_entry(const char *key, Transaction *transaction) {
     }
     if (transaction != nullptr) {
         // for (auto &page: *transaction->get_index_deleted_page_set()) {
-            // assert(page->get_pin_count() == 0);
-            // assert(buffer_pool_manager_->delete_page(page->get_page_id()));
+        // assert(page->get_pin_count() == 0);
+        // assert(buffer_pool_manager_->delete_page(page->get_page_id()));
         // }
         transaction->get_index_deleted_page_set()->clear();
     }
@@ -583,7 +585,7 @@ bool IxIndexHandle::delete_entry(const char *key, Transaction *transaction) {
  * @return bool 根结点是否需要被删除
  * @note size of root page can be less than min size and this method is only called within coalesce_or_redistribute()
  */
-bool IxIndexHandle::adjust_root(IxNodeHandle *old_root_node) {
+bool IxIndexHandle::adjust_root(std::shared_ptr<IxNodeHandle> &old_root_node) {
     // Todo:
     // 1. 如果old_root_node是内部结点,并且大小为1,则直接把它的孩子更新成新的根结点
     // 2. 如果old_root_node是叶结点,且大小为0,则直接更新root page
@@ -626,7 +628,8 @@ bool IxIndexHandle::adjust_root(IxNodeHandle *old_root_node) {
  * index>0,则neighbor是node前驱结点,表示:neighbor(left)  node(right)
  * 注意更新parent结点的相关kv对
  */
-void IxIndexHandle::redistribute(IxNodeHandle *neighbor_node, IxNodeHandle *node, IxNodeHandle *parent, int index) {
+void IxIndexHandle::redistribute(std::shared_ptr<IxNodeHandle> &neighbor_node, std::shared_ptr<IxNodeHandle> &node,
+                                 std::shared_ptr<IxNodeHandle> &parent, int index) {
     // Todo:
     // 1. 通过index判断neighbor_node是否为node的前驱结点
     // 2. 从neighbor_node中移动一个键值对到node结点中
@@ -668,7 +671,8 @@ void IxIndexHandle::redistribute(IxNodeHandle *neighbor_node, IxNodeHandle *node
  * @return true means parent node should be deleted, false means no deletion happend
  * @note Assume that *neighbor_node is the left sibling of *node (neighbor -> node)
  */
-bool IxIndexHandle::coalesce(IxNodeHandle **neighbor_node, IxNodeHandle **node, IxNodeHandle **parent, int index,
+bool IxIndexHandle::coalesce(std::shared_ptr<IxNodeHandle> *neighbor_node, std::shared_ptr<IxNodeHandle> *node,
+                             std::shared_ptr<IxNodeHandle> *parent, int index,
                              Transaction *transaction, bool *root_is_latched) {
     // Todo:
     // 1. 用index判断neighbor_node是否为node的前驱结点,若不是则交换两个结点,让neighbor_node作为左结点,node作为右结点
@@ -720,7 +724,8 @@ bool IxIndexHandle::coalesce(IxNodeHandle **neighbor_node, IxNodeHandle **node,
  * If sibling's size + input page's size >= 2 * page's minsize, then redistribute.
  * Otherwise, merge(Coalesce).
  */
-bool IxIndexHandle::coalesce_or_redistribute(IxNodeHandle *node, Transaction *transaction, bool *root_is_latched) {
+bool IxIndexHandle::coalesce_or_redistribute(std::shared_ptr<IxNodeHandle> &node, Transaction *transaction,
+                                             bool *root_is_latched) {
     // Todo:
     // 1. 判断node结点是否为根节点
     //    1.1 如果是根节点,需要调用AdjustRoot() 函数来进行处理,返回根节点是否需要被删除
@@ -793,7 +798,7 @@ bool IxIndexHandle::coalesce_or_redistribute(IxNodeHandle *node, Transaction *tr
  * @note iid和rid存的不是一个东西,rid是上层传过来的记录位置,iid是索引内部生成的索引槽位置
  */
 Rid IxIndexHandle::get_rid(const Iid &iid) const {
-    IxNodeHandle *node = fetch_node(iid.page_no);
+    auto node = fetch_node(iid.page_no);
     if (iid.slot_no >= node->get_size()) {
         throw IndexEntryNotFoundError();
     }
@@ -866,7 +871,7 @@ Iid IxIndexHandle::upper_bound(const char *key) {
  * @return Iid
  */
 Iid IxIndexHandle::leaf_end() const {
-    IxNodeHandle *node = fetch_node(file_hdr_->last_leaf_);
+    auto node = fetch_node(file_hdr_->last_leaf_);
     Iid iid = {.page_no = file_hdr_->last_leaf_, .slot_no = node->get_size()};
     buffer_pool_manager_->unpin_page(node->get_page_id(), false); // unpin it!
     return iid;
@@ -890,10 +895,9 @@ Iid IxIndexHandle::leaf_begin() const {
  * @return IxNodeHandle*
  * @note pin the page, remember to unpin it outside!
  */
-IxNodeHandle *IxIndexHandle::fetch_node(int page_no) const {
-    Page *page = buffer_pool_manager_->fetch_page({fd_, page_no});
-    IxNodeHandle *node = new IxNodeHandle(file_hdr_, page);
-    return node;
+std::shared_ptr<IxNodeHandle> IxIndexHandle::fetch_node(int page_no) const {
+    auto *page = buffer_pool_manager_->fetch_page({fd_, page_no});
+    return std::make_shared<IxNodeHandle>(file_hdr_, page);
 }
 
 /**
@@ -906,15 +910,12 @@ IxNodeHandle *IxIndexHandle::fetch_node(int page_no) const {
  * 在最开始插入时,一直是create node,那么first_page_no一直没变,一直是IX_NO_PAGE
  * 与Record的处理不同,Record将未插入满的记录页认为是free_page
  */
-IxNodeHandle *IxIndexHandle::create_node() {
-    IxNodeHandle *node;
-    file_hdr_->num_pages_++;
-
+std::shared_ptr<IxNodeHandle> IxIndexHandle::create_node() {
+    ++file_hdr_->num_pages_;
     PageId new_page_id = {.fd = fd_, .page_no = INVALID_PAGE_ID};
     // 从3开始分配page_no,第一次分配之后,new_page_id.page_no=3,file_hdr_.num_pages=4
-    Page *page = buffer_pool_manager_->new_page(&new_page_id);
-    node = new IxNodeHandle(file_hdr_, page);
-    return node;
+    auto *page = buffer_pool_manager_->new_page(&new_page_id);
+    return std::make_shared<IxNodeHandle>(file_hdr_, page);
 }
 
 /**
@@ -922,11 +923,11 @@ IxNodeHandle *IxIndexHandle::create_node() {
  *
  * @param node
  */
-void IxIndexHandle::maintain_parent(IxNodeHandle *node) {
-    IxNodeHandle *curr = node;
+void IxIndexHandle::maintain_parent(std::shared_ptr<IxNodeHandle> &node) {
+    auto curr = node;
     while (curr->get_parent_page_no() != IX_NO_PAGE) {
         // Load its parent
-        IxNodeHandle *parent = fetch_node(curr->get_parent_page_no());
+        auto parent = fetch_node(curr->get_parent_page_no());
         int rank = parent->find_child(curr);
         char *parent_key = parent->get_key(rank);
         char *child_first_key = curr->get_key(0);
@@ -945,14 +946,14 @@ void IxIndexHandle::maintain_parent(IxNodeHandle *node) {
  *
  * @param leaf 要删除的leaf
  */
-void IxIndexHandle::erase_leaf(IxNodeHandle *leaf) {
+void IxIndexHandle::erase_leaf(std::shared_ptr<IxNodeHandle> &leaf) {
     assert(leaf->is_leaf_page());
 
-    IxNodeHandle *prev = fetch_node(leaf->get_prev_leaf());
+    auto prev = fetch_node(leaf->get_prev_leaf());
     prev->set_next_leaf(leaf->get_next_leaf());
     buffer_pool_manager_->unpin_page(prev->get_page_id(), true);
 
-    IxNodeHandle *next = fetch_node(leaf->get_next_leaf());
+    auto next = fetch_node(leaf->get_next_leaf());
     next->set_prev_leaf(leaf->get_prev_leaf()); // 注意此处是SetPrevLeaf()
     buffer_pool_manager_->unpin_page(next->get_page_id(), true);
 }
@@ -969,11 +970,11 @@ void IxIndexHandle::release_node_handle(IxNodeHandle &node) {
 /**
  * @brief 将node的第child_idx个孩子结点的父节点置为node
  */
-void IxIndexHandle::maintain_child(IxNodeHandle *node, int child_idx) {
+void IxIndexHandle::maintain_child(std::shared_ptr<IxNodeHandle> &node, int child_idx) {
     if (!node->is_leaf_page()) {
         //  Current node is inner node, load its child and set its parent to current node
         int child_page_no = node->value_at(child_idx);
-        IxNodeHandle *child = fetch_node(child_page_no);
+        auto child = fetch_node(child_page_no);
         child->set_parent_page_no(node->get_page_no());
         buffer_pool_manager_->unpin_page(child->get_page_id(), true);
     }
diff --git a/src/index/ix_index_handle.h b/src/index/ix_index_handle.h
index c249c8c..e5632ef 100644
--- a/src/index/ix_index_handle.h
+++ b/src/index/ix_index_handle.h
@@ -161,7 +161,7 @@ public:
      * @param child
      * @return int
      */
-    int find_child(IxNodeHandle *child) {
+    int find_child(std::shared_ptr<IxNodeHandle> child) {
         // TODO:优化为什么不二分
         int rid_idx;
         for (rid_idx = 0; rid_idx < page_hdr->num_key; rid_idx++) {
@@ -219,11 +219,16 @@ private:
 public:
     IxIndexHandle(DiskManager *disk_manager, BufferPoolManager *buffer_pool_manager, int fd);
 
+    ~IxIndexHandle() {
+        delete file_hdr_;
+    }
+
     // for search
     bool get_value(const char *key, std::vector<Rid> *result, Transaction *transaction);
 
-    std::pair<IxNodeHandle *, bool> find_leaf_page(const char *key, Operation operation, Transaction *transaction,
-                                                   bool find_first = false);
+    std::pair<std::shared_ptr<IxNodeHandle>, bool> find_leaf_page(const char *key, Operation operation,
+                                                                  Transaction *transaction,
+                                                                  bool find_first = false);
 
     // check unique
     bool is_unique(const char *key, Rid &value, Transaction *transaction);
@@ -231,21 +236,24 @@ public:
     // for insert
     page_id_t insert_entry(const char *key, const Rid &value, Transaction *transaction);
 
-    IxNodeHandle *split(IxNodeHandle *node);
+    std::shared_ptr<IxNodeHandle> split(std::shared_ptr<IxNodeHandle> &node);
 
-    void insert_into_parent(IxNodeHandle *old_node, const char *key, IxNodeHandle *new_node, Transaction *transaction);
+    void insert_into_parent(std::shared_ptr<IxNodeHandle> &old_node, const char *key,
+                            std::shared_ptr<IxNodeHandle> &new_node, Transaction *transaction);
 
     // for delete
     bool delete_entry(const char *key, Transaction *transaction);
 
-    bool coalesce_or_redistribute(IxNodeHandle *node, Transaction *transaction = nullptr,
+    bool coalesce_or_redistribute(std::shared_ptr<IxNodeHandle> &node, Transaction *transaction = nullptr,
                                   bool *root_is_latched = nullptr);
 
-    bool adjust_root(IxNodeHandle *old_root_node);
+    bool adjust_root(std::shared_ptr<IxNodeHandle> &old_root_node);
 
-    void redistribute(IxNodeHandle *neighbor_node, IxNodeHandle *node, IxNodeHandle *parent, int index);
+    void redistribute(std::shared_ptr<IxNodeHandle> &neighbor_node, std::shared_ptr<IxNodeHandle> &node,
+                      std::shared_ptr<IxNodeHandle> &parent, int index);
 
-    bool coalesce(IxNodeHandle **neighbor_node, IxNodeHandle **node, IxNodeHandle **parent, int index,
+    bool coalesce(std::shared_ptr<IxNodeHandle> *neighbor_node, std::shared_ptr<IxNodeHandle> *node,
+                  std::shared_ptr<IxNodeHandle> *parent, int index,
                   Transaction *transaction, bool *root_is_latched);
 
     Iid lower_bound(const char *key);
@@ -263,18 +271,18 @@ private:
     bool is_empty() const { return file_hdr_->root_page_ == IX_NO_PAGE; }
 
     // for get/create node
-    IxNodeHandle *fetch_node(int page_no) const;
+    std::shared_ptr<IxNodeHandle> fetch_node(int page_no) const;
 
-    IxNodeHandle *create_node();
+    std::shared_ptr<IxNodeHandle> create_node();
 
     // for maintain data structure
-    void maintain_parent(IxNodeHandle *node);
+    void maintain_parent(std::shared_ptr<IxNodeHandle> &node);
 
-    void erase_leaf(IxNodeHandle *leaf);
+    void erase_leaf(std::shared_ptr<IxNodeHandle> &leaf);
 
     void release_node_handle(IxNodeHandle &node);
 
-    void maintain_child(IxNodeHandle *node, int child_idx);
+    void maintain_child(std::shared_ptr<IxNodeHandle> &node, int child_idx);
 
     // for index test
     Rid get_rid(const Iid &iid) const;
diff --git a/src/index/ix_manager.h b/src/index/ix_manager.h
index 4b315d7..6b4dd38 100644
--- a/src/index/ix_manager.h
+++ b/src/index/ix_manager.h
@@ -30,7 +30,7 @@ public:
     std::string get_index_name(const std::string &filename, const std::vector<std::string> &index_cols) {
         std::ostringstream oss;
         oss << filename;
-        for (const auto &col : index_cols) {
+        for (const auto &col: index_cols) {
             oss << "_" << col;
         }
         oss << ".idx";
@@ -40,7 +40,7 @@ public:
     std::string get_index_name(const std::string &filename, const std::vector<ColMeta> &index_cols) {
         std::ostringstream oss;
         oss << filename;
-        for (const auto &col : index_cols) {
+        for (const auto &col: index_cols) {
             oss << "_" << col.name;
         }
         oss << ".idx";
@@ -61,7 +61,7 @@ public:
         return disk_manager_->is_file(index_name);
     }
 
-    void create_index(const std::string &ix_name, const std::vector<ColMeta>& index_cols) {
+    void create_index(const std::string &ix_name, const std::vector<ColMeta> &index_cols) {
         // Create index file
         disk_manager_->create_file(ix_name);
         // Open index file
@@ -89,8 +89,8 @@ public:
                                         col_num, col_tot_len, btree_order, (btree_order + 1) * col_tot_len,
                                         IX_INIT_ROOT_PAGE, IX_INIT_ROOT_PAGE);
         for (int i = 0; i < col_num; ++i) {
-            fhdr->col_types_.push_back(index_cols[i].type);
-            fhdr->col_lens_.push_back(index_cols[i].len);
+            fhdr->col_types_.emplace_back(index_cols[i].type);
+            fhdr->col_lens_.emplace_back(index_cols[i].len);
         }
         fhdr->update_tot_len();
 
@@ -99,6 +99,9 @@ public:
 
         disk_manager_->write_page(fd, IX_FILE_HDR_PAGE, data, fhdr->tot_len_);
 
+        delete fhdr;
+        delete []data;
+
         char page_buf[PAGE_SIZE]; // 在内存中初始化page_buf中的内容,然后将其写入磁盘
         memset(page_buf, 0, PAGE_SIZE);
         // 注意leaf header页号为1,也标记为叶子结点,其前一个/后一个叶子均指向root node
diff --git a/src/index/ix_scan.cpp b/src/index/ix_scan.cpp
index f737276..a65dc76 100644
--- a/src/index/ix_scan.cpp
+++ b/src/index/ix_scan.cpp
@@ -16,7 +16,7 @@ See the Mulan PSL v2 for more details. */
  */
 void IxScan::next() {
     assert(!is_end());
-    IxNodeHandle *node = ih_->fetch_node(iid_.page_no);
+    auto &&node = ih_->fetch_node(iid_.page_no);
     assert(node->is_leaf_page());
     assert(iid_.slot_no < node->get_size());
     // increment slot no
diff --git a/src/rmdb.cpp b/src/rmdb.cpp
index fa42f6f..4b54a0e 100644
--- a/src/rmdb.cpp
+++ b/src/rmdb.cpp
@@ -71,6 +71,7 @@ void SetTransaction(txn_id_t *txn_id, Context *context) {
 
 void *client_handler(void *sock_fd) {
     int fd = *((int *) sock_fd);
+    free(sock_fd);
     pthread_mutex_unlock(sockfd_mutex);
 
     int i_recvBytes;
@@ -112,6 +113,9 @@ void *client_handler(void *sock_fd) {
         if (strcmp(data_recv, "crash") == 0) {
             std::cout << "Server crash" << std::endl;
             delete []data_send;
+            for (auto &[_, txn]: txn_manager->txn_map) {
+                delete txn;
+            }
             exit(1);
         }
 
@@ -250,25 +254,31 @@ void start_server() {
 
     while (!should_exit) {
         std::cout << "Waiting for new connection..." << std::endl;
+        // 解决局部变量析构问题
+        int *sockfd = (int *) malloc(sizeof(int));
+
         pthread_t thread_id;
         struct sockaddr_in s_addr_client{};
         int client_length = sizeof(s_addr_client);
 
         if (setjmp(jmpbuf)) {
+            free(sockfd);
             std::cout << "Break from Server Listen Loop\n";
             break;
         }
 
         // Block here. Until server accepts a new connection.
         pthread_mutex_lock(sockfd_mutex);
-        int sockfd = accept(sockfd_server, (struct sockaddr *) (&s_addr_client), (socklen_t *) (&client_length));
-        if (sockfd == -1) {
+        *sockfd = accept(sockfd_server, (struct sockaddr *) (&s_addr_client), (socklen_t *) (&client_length));
+        if (*sockfd == -1) {
+            free(sockfd);
             std::cout << "Accept error!" << std::endl;
             continue; // ignore current socket ,continue while loop.
         }
 
         // 和客户端建立连接,并开启一个线程负责处理客户端请求
-        if (pthread_create(&thread_id, nullptr, &client_handler, (void *) (&sockfd)) != 0) {
+        if (pthread_create(&thread_id, nullptr, &client_handler, (void *) (sockfd)) != 0) {
+            free(sockfd);
             std::cout << "Create thread fail!" << std::endl;
             break; // break while loop
         }
@@ -280,6 +290,11 @@ void start_server() {
     if (ret == -1) { printf("%s\n", strerror(errno)); }
     //    assert(ret != -1);
     sm_manager->close_db();
+
+    for (auto &[_, txn]: txn_manager->txn_map) {
+        delete txn;
+    }
+
     std::cout << " DB has been closed.\n";
     std::cout << "Server shuts down." << std::endl;
 }
diff --git a/src/transaction/transaction_manager.cpp b/src/transaction/transaction_manager.cpp
index 63f2361..9e5df52 100644
--- a/src/transaction/transaction_manager.cpp
+++ b/src/transaction/transaction_manager.cpp
@@ -182,7 +182,9 @@ void TransactionManager::abort(Transaction *txn, LogManager *log_manager) {
         }
         delete write_record;
     }
+    delete context;
     write_set->clear();
+
     // 释放所有锁
     auto &&lock_set = txn->get_lock_set();
     for (auto &it: *lock_set) {
diff --git a/src/transaction/transaction_manager.h b/src/transaction/transaction_manager.h
index 7f9bc5c..2e58af2 100644
--- a/src/transaction/transaction_manager.h
+++ b/src/transaction/transaction_manager.h
@@ -50,14 +50,22 @@ public:
      * @param {txn_id_t} txn_id 事务ID
      */
     Transaction *get_transaction(txn_id_t txn_id) {
-        if (txn_id == INVALID_TXN_ID) return nullptr;
+        if (txn_id == INVALID_TXN_ID) {
+            return nullptr;
+        }
 
-        std::unique_lock<std::mutex> lock(latch_);
+        std::unique_lock lock(latch_);
         if (txn_map.find(txn_id) == txn_map.end()) {
             return nullptr;
         }
 
         auto *res = txn_map[txn_id];
+        if (res->get_state() == TransactionState::COMMITTED || res->get_state() == TransactionState::ABORTED) {
+            delete res;
+            txn_map.erase(txn_id);
+            return nullptr;
+        }
+
         lock.unlock();
         assert(res != nullptr);
         assert(res->get_thread_id() == std::this_thread::get_id());
-- 
GitLab