はじめに
Raspberry Pi Pico + FreeRTOS SMP で stdio を usb にすると std::cout でたまにハングする。
どこかの設定で回避できるのかも知れないが、見つけられなかった。
正しい処置が不明だが、調べた事と 考えた対策を記述する。
発生事象
- 開発環境
- Raspberry Pi Pico C/C++ SDK 1.5.1
- FreeRTOS branch main #982
- Windows11 + VSCode
- task 1本でもハングする
-
task を 1本だけ起こして そこから出力しただけでもハングする
-
std::cout << std::endl
2行は 発生頻度を上げるため -
std::cout << std::endl
を増やしたり、delay を小さくすると 更に頻度アップvoid test_func(void*) { int n = 0 ; while(true) { vTaskDelay( pdMS_TO_TICKS(50) ) ; std::cout << n++ << std::endl ; std::cout << std::endl ; std::cout << std::endl ; } } int main(void) { stdio_init_all() ; xTaskCreate(test_func, "test_func", configMINIMAL_STACK_SIZE, NULL, tskIDLE_PRIORITY + 1, NULL) ; vTaskStartScheduler(); }
pico_enable_stdio_usb (${project_name} 1) pico_enable_stdio_uart(${project_name} 0)
-
確認内容
-
stack size
stack は 確認できた時点で 108 x 4 byte 残っており、stack 不足では無さそう。
stack size を増やしても効果なしIDLE1 R 0 232 4 IDLE0 R 0 226 3 test_func B 1 108 2 Tmr Svc R 31 972 5
-
configUSE_PREEMPTION
変更しても変化なし -
configRUN_MULTIPLE_PRIORITIES
頻度は下がるがハングする -
core を 0 に固定
vTaskCoreAffinitySet()
で core を 0 に固定すると 発生しない!! -
core を 1 に固定
vTaskCoreAffinitySet()
で core を 1 に固定すると 頻度爆上がり
と言うことで、std::cout
実行時に core 1 が割り当てられると 不都合があるようだ。
対策
-
std::cout
の実行時は core 0 に固定する -
std::cout
呼び出し時に 一々vTaskCoreAffinitySet()
するのは面倒なので、自分の stream を定義する - ここで core 0 に固定してから
std::cout
に渡し、終わったら元に戻す - このコードで ハングしなくなった
class CNkStringBuf : public std::stringbuf { virtual int sync() override { UBaseType_t pre = vTaskCoreAffinityGet(NULL) ; vTaskCoreAffinitySet(NULL, 1) ; std::cout << str() << std::flush ; str(""); vTaskCoreAffinitySet(NULL, pre) ; return 0; } }; namespace nk { CNkStringBuf cout_buf ; std::ostream cout(&cout_buf) ; } void test_func(void*) { int n = 0 ; while(true) { vTaskDelay( pdMS_TO_TICKS(50) ) ; nk::cout << n++ << std::endl ; nk::cout << std::endl ; nk::cout << std::endl ; } }
まとめ
-
std::cout
を core 0 で実行するとハングしない - 本来の対策 ( 設定? ) は 未だ不明
- 対策として、自前の stream で core 0 に固定してから
std::cout
を呼ぶ
おまけ
- Core の lock guard を作ってみた
class CCoreLock
{
protected:
UBaseType_t pre ;
public:
CCoreLock() = delete ;
CCoreLock(uint core_id)
{
#if configNUMBER_OF_CORES>1 && configUSE_CORE_AFFINITY==1
pre = vTaskCoreAffinityGet(NULL) ;
vTaskCoreAffinitySet(NULL, 1<<core_id) ;
#endif
}
~CCoreLock()
{
#if configNUMBER_OF_CORES>1 && configUSE_CORE_AFFINITY==1
vTaskCoreAffinitySet(NULL, pre) ;
#endif
}
} ;
class CNkStringBuf : public std::stringbuf
{
virtual int sync() override
{
CCoreLock core(0) ;
std::cout << str() << std::flush ;
str("");
return 0;
}
};