stepinto opened a new issue, #2278:
URL: https://github.com/apache/brpc/issues/2278

   **Describe the bug (描述bug)**
   C++ 异常机制倚赖 TLS 实现。若在 catch 代码块中让 bthread 被换出,则有小概率导致 exception 对象被提前析构。
   
   例如在下面的代码中
   
   ```
   try {
       ... 
   } catch (exception& e) {
      Foo();  // 该函数是一个阻塞的函数,导致当前 bthread 被换出。
      std::cout << e.msg << std::endl;  // 小概率出错
   }
   ```
   
   详细的解释请看下面复现的测试用例。
   
   **To Reproduce (复现方法)**
   
   下面这个用例可以复现此问题。
   
   ```
   #include <stdio.h>
   #include <bthread/bthread.h>
   #include <bthread/task_control.h>
   #include <bthread/task_group.h>
   #include <gtest/gtest.h>
   
   namespace brpc_test
   {
   
   struct MyException
   {
       bthread_t bthread_id;
   
       MyException()
       {
           printf("[%lu] %s %p\n", bthread_self(), __FUNCTION__, this);
           bthread_id = bthread_self();
       }
   
       ~MyException()
       {
           printf("[%lu] %s %p\n", bthread_self(), __FUNCTION__, this);
           bthread_id = 0;
       }
   };
   
   static void* MyBthread(void *unused)
   {
       printf("[%lu] Enter %s\n", bthread_self(), __FUNCTION__);
       try
       {
           printf("[%lu] Throw\n", bthread_self());
           throw MyException();
       }
       catch (const MyException& e)
       {
           printf("[%lu] Catch %p\n", bthread_self(), &e);
           EXPECT_EQ(bthread_self(), e.bthread_id);
           printf("[%lu] Sleep\n", bthread_self());
           bthread_usleep(1);  // 让当前协程切换出去
           printf("[%lu] Resume\n", bthread_self());
           EXPECT_EQ(bthread_self(), e.bthread_id);  // ERROR
       }
       printf("[%lu] Leave %s\n", bthread_self(), __FUNCTION__);
       return NULL;
   }
   
   TEST(BugDemo, Brpc)
   {
       // 直接使用 bthread_start_background() 是多线程的,不容易复现 bug。
       // 我们定义一个单线程的线程池,
       bthread::TaskControl tc;
       EXPECT_EQ(0, tc.init(1));
   
       bthread::TaskGroup *tg = tc.choose_one_group();
       bthread_t thread1;
       tg->start_background<true>(&thread1, NULL, MyBthread, NULL);
       bthread_t thread2;
       tg->start_background<true>(&thread2, NULL, MyBthread, NULL);
   
       bthread_join(thread1, NULL);
       bthread_join(thread2, NULL);
       tc.stop_and_join();
   }
   
   }  // namespace brpc_test
   ```
   
   这个程序的输出为如下(我加了点注释)。
   
   ```
   Note: Google Test filter = BugDemo.Brpc
   [==========] Running 1 test from 1 test case.
   [----------] Global test environment set-up.
   [----------] 1 test from BugDemo
   [ RUN      ] BugDemo.Brpc
   [4294967552] Enter MyBthread   # bthread1 开始执行
   [4294967552] Throw
   [4294967552] MyException 0x7f72dc01fa30  # bthread1 分配 MyException 对象 e1
   [4294967552] Catch 0x7f72dc01fa30
   [4294967552] Sleep             # bhread1 换出
   [4294967553] Enter MyBthread   # bthread2 开始执行
   [4294967553] Throw
   [4294967553] MyException 0x7f72dc0263c0  # bthread2 分配 MyException 对象 e2
   [4294967553] Catch 0x7f72dc0263c0
   [4294967553] Sleep             # bthread2 换出
   [4294967552] Resume            # bthread1 换入
   [4294967552] ~MyException 0x7f72dc0263c0  # bthread1 析构 e2,注意这里开始出问题了!!
   [4294967552] Leave MyBthread
   [4294967553] Resume            # bthread2 换入
   /mag/workspace/bug-demo/brpc_test.cpp:42: Failure  # bthread2 访问到已析构的对象 e2
   Expected equality of these values:
     bthread_self()
       Which is: 4294967553
     e.bthread_id
       Which is: 0
   [4294967553] ~MyException 0x7f72dc01fa30  # bthread2 析构 e1
   [4294967553] Leave MyBthread
   [  FAILED  ] BugDemo.Brpc (3 ms)
   [----------] 1 test from BugDemo (3 ms total)
   
   [----------] Global test environment tear-down
   [==========] 1 test from 1 test case ran. (3 ms total)
   [  PASSED  ] 0 tests.
   [  FAILED  ] 1 test, listed below:
   [  FAILED  ] BugDemo.Brpc
   ```
   
   **Expected behavior (期望行为)**
   
   
   **Versions (各种版本)**
   OS: Linux 3.10
   Compiler: gcc 9.2.1
   brpc: 1.5.0
   protobuf: n/a
   
   **Additional context/screenshots (更多上下文/截图)**
   
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to