test_pthread_cxx.cpp
1 #include <iostream> 2 #include <sstream> 3 #include <thread> 4 #include <mutex> 5 #include "freertos/FreeRTOS.h" 6 #include "freertos/task.h" 7 #include "unity.h" 8 9 #if __GTHREADS && __GTHREADS_CXX0X 10 11 #define LOG_LOCAL_LEVEL CONFIG_LOG_DEFAULT_LEVEL 12 #include "esp_log.h" 13 const static char *TAG = "pthread_test"; 14 15 static std::mutex mtx; 16 static std::shared_ptr<int> global_sp_mtx; // protected by mux 17 18 static std::recursive_mutex recur_mtx; 19 static std::shared_ptr<int> global_sp_recur_mtx; // protected by recursive mux 20 21 static void thread_do_nothing() {} 22 23 static void thread_main() 24 { 25 std::cout << "thread_main CXX " << std::hex << std::this_thread::get_id() << std::endl; 26 std::chrono::milliseconds dur = std::chrono::milliseconds(10); 27 28 for (int i = 0; i < 10; i++) { 29 for (int j = 0; j < 10; j++) { 30 int old_val, new_val; 31 32 // mux test 33 mtx.lock(); 34 old_val = *global_sp_mtx; 35 std::this_thread::yield(); 36 (*global_sp_mtx)++; 37 std::this_thread::yield(); 38 new_val = *global_sp_mtx; 39 mtx.unlock(); 40 std::cout << "thread " << std::hex << std::this_thread::get_id() << ": nrec " << i << " val= " << *global_sp_mtx << std::endl; 41 TEST_ASSERT_EQUAL(old_val + 1, new_val); 42 43 // sleep_for test 44 std::this_thread::sleep_for(dur); 45 46 // recursive mux test 47 recur_mtx.lock(); 48 recur_mtx.lock(); 49 old_val = *global_sp_recur_mtx; 50 std::this_thread::yield(); 51 (*global_sp_recur_mtx)++; 52 std::this_thread::yield(); 53 new_val = *global_sp_recur_mtx; 54 recur_mtx.unlock(); 55 recur_mtx.unlock(); 56 std::cout << "thread " << std::hex << std::this_thread::get_id() << ": rec " << i << " val= " << *global_sp_recur_mtx << std::endl; 57 TEST_ASSERT_EQUAL(old_val + 1, new_val); 58 } 59 60 // sleep_until test 61 using std::chrono::system_clock; 62 std::time_t tt = system_clock::to_time_t(system_clock::now()); 63 struct std::tm *ptm = std::localtime(&tt); 64 ptm->tm_sec++; 65 std::this_thread::sleep_until(system_clock::from_time_t(mktime(ptm))); 66 } 67 } 68 69 TEST_CASE("pthread C++", "[pthread]") 70 { 71 global_sp_mtx.reset(new int(1)); 72 global_sp_recur_mtx.reset(new int(-1000)); 73 74 std::thread t1(thread_do_nothing); 75 t1.join(); 76 77 std::thread t2(thread_main); 78 std::cout << "Detach thread " << std::hex << t2.get_id() << std::endl; 79 t2.detach(); 80 TEST_ASSERT_FALSE(t2.joinable()); 81 82 std::thread t3(thread_main); 83 std::thread t4(thread_main); 84 if (t3.joinable()) { 85 std::cout << "Join thread " << std::hex << t3.get_id() << std::endl; 86 t3.join(); 87 } 88 if (t4.joinable()) { 89 std::cout << "Join thread " << std::hex << t4.get_id() << std::endl; 90 t4.join(); 91 } 92 93 global_sp_mtx.reset(); // avoid reported leak 94 global_sp_recur_mtx.reset(); 95 } 96 97 static void task_test_sandbox() 98 { 99 std::stringstream ss; 100 101 ESP_LOGI(TAG, "About to create a string stream"); 102 ESP_LOGI(TAG, "About to write to string stream"); 103 ss << "Hello World!"; 104 ESP_LOGI(TAG, "About to extract from stringstream"); 105 ESP_LOGI(TAG, "Text: %s", ss.str().c_str()); 106 } 107 108 static void task_test_sandbox_c(void *arg) 109 { 110 bool *running = (bool *)arg; 111 112 // wrap thread func to ensure that all C++ stack objects are cleaned up by their destructors 113 task_test_sandbox(); 114 115 ESP_LOGI(TAG, "Task stk_wm = %d", uxTaskGetStackHighWaterMark(NULL)); 116 if (running) { 117 *running = false; 118 vTaskDelete(NULL); 119 } 120 } 121 122 TEST_CASE("pthread mix C/C++", "[pthread]") 123 { 124 bool c_running = true; 125 126 std::thread t1(task_test_sandbox); 127 xTaskCreatePinnedToCore((TaskFunction_t)&task_test_sandbox_c, "task_test_sandbox", 3072, &c_running, 5, NULL, 0); 128 while (c_running) { 129 vTaskDelay(1); 130 } 131 if (t1.joinable()) { 132 std::cout << "Join thread " << std::hex << t1.get_id() << std::endl; 133 t1.join(); 134 } 135 } 136 137 #endif