As of May 2024, there are about 145,000 crates; of which, approximately 127,000 contain significant code. Of those 127,000 crates, 24,362 make use of the unsafe keyword, which is 19.11% of all crates. And 34.35% make a direct function call into another crate that uses the unsafe keyword. 6 Nearly 20% of all crates have at least one instance of the unsafe keyword, a non-trivial number.
Most of these Unsafe Rust uses are calls into existing third-party non-Rust language code or libraries, such as C or C++. In fact, the crate with the most uses of the unsafe keyword is the Windows crate 7, which allows Rust developers to call into various Windows APIs.
Примерно 81% крейтов не содержат ни одной строчки unsafe. А среди остальных большинство - это FFI.
Теперь ваша очередь доказывать про пропаганду. Надеюсь получить аргументированный ответ.
Посмотрел. Похоже, что его транслировали из C в Rust (например этим), и потом добились того чтобы компилировался.
Я делаю такой вывод из того, что внутри написана реализация memset, memchr и юникода (лол). Так что ваш пример - это антипример.
P.S. Вот пример +- большого проекта (https://github.com/picodata/picodata), изначально написанного на Rust. На 112kloc Rust кода 238 строк unsafe (0.2%). Из них около 80% это плагин к tarantool, т.к. обращения к ffi.
Но restrict по умолчанию только в safe Rust. А в unsafe мы оказываемся ближе к С (но не на его уровне), и можно писать код на указателях, главное при переходе снова к ссылкам соблюдать ограничение restrict.
На практике за этим надо следить разработчикам библиотек, которые что-то оптимизируют через unsafe и указатели, а в прикладном коде unsafe практически не встречается.
А вы точно прочитали статью? В конце раздела На сцене появляется C++11 ровно один указатель, и можно было бы заменить на std::string без изменения сути примера.
А суть в том, что на повторный std::move компилятор не выдает ошибку (и даже варнинг), и нужно много приседать, чтобы компилятор что-то заметил. Ито это не гарантируется.
"давайте рассыпем по коду сырых указателей да наворотим дикой дичи в стиле pure C".
Целью проекта Rust Coreutils является создание кроссплатформенной альтернативной реализации Coreutils, способной работать в том числе на платформах Windows, Redox и Fuchsia.
На сколько я понимаю, GNU Coreutils не кроссплатформенный.
Возможно вы не знаете, но стандартная библиотека Rust тоже покрыта десятками тысяч тестов.
Если я подлючу случаный крейт с unsafe-фрагментом кода, где гарантия, что он был написан столь же ответственно, и что его ежедневные фиксы не сломают мое приложение?
А если я подключу случайную библиотеку в Java, где гарантия, что она не вызовет внутри гонку данных? Или правильно вызовет С библиотеку, не приводя к UB.
Я думаю что требования для небезопасного кода одинаковые для всех языков: покрывать тестами, проверять санитайзерами, проводить фаззинг и т.д. Если библиотеки этого не делают, это не может быть претензией к самому языку.
Просто в Rust такие блоки кода намеренно выделены в блоки unsafe, чтобы быть заметнее. А в каком-нибудь Python вызов C-API легко затеряется среди тысяч строк кода.
Похоже вы отрицаете саму идею построения безопасного API поверх потенциально небезопасного кода.
Но если вы загляните в исходники стандартной библиотеки любого языка, то там будет и арифметика указателей, и ассемблерные вставки, и системные вызовы, что априори небезопасно. И тем не менее вызвать UB в C# например довольно трудно.
Вероятно вы путаете неявное приведение и автоматический вывод типов. Или вы скажете, что auto a = ... в С++ тоже неявное приведение?
Вот это неявное приведение: uint8_t a = -100. А вот это автоматический вывод типов: let a: u8 = (-100).into().
При этом макросы куда хуже сишных, это отдельный какой-то регекс язык
<sarcasm on> Да да, препроцессор гораздо лучше разбора AST. Это просто сказка! <sarcasm off>
Муторный синтаксис, язык настроен так, что при любом самом мелком изменении приходится менять много кода.
Утиная типизация в С++ тоже имеет свои плюсы и минусы. Лично я думаю, что минусы перевешивают. Лучше написать чуть больше кода, но иметь больше контроля.
потому что язык слаб в шаблонах (джнериках)
Единственное с чем соглашусь. Но это пока, посмотрим через пару лет.
Теперь буду знать, что unsafe - это не для компилятора, а для программиста. Указание на то, что надо посмотреть документацию или реализацию.
unsafe - это контракт между программистом и компилятором. Если все его придерживаются, то он полностью избавляет от многих классов UB. На практике все ошибаются, и шанс не равен нулю, но очень мал.
Вообще я удивляюсь, что все так прицепились к unsafe блокам, как будто видят их впервые. В C# например они были еще с версии 1.0, задолго до Rust. И никого не смущало, что нужно оборачивать опасные места в коде в блоки unsafe.
Использовать у себя код, которому нет доверия? Вы серьезно?
В С например в документации к memcpy описано несколько вариантов, когда может случиться UB. В контексте этого обсуждения, это и есть тот самый недоверенный код. Но ведь все используют memcpy! Просто нельзя нарушать её инварианты.
В Rust такие функции помечают как unsafe, и в документации также как и в С описано, как её вызывать правильно, т.к. компилятор не может это проверить сам (а в случае asm! он вообще ничего проверить не может). А компилятор не даст просто так вызвать unsafe функцию.
Через unsafe блок программист говорит компилятору: я добавил в код все необходимые проверки (которые описаны в документации), и гарантирую что функция вызвана правильно. Ровно то же самое нужно делать на С с опасными функциями, но реализуется это не компилятором, а статическими анализаторами, санитайзерами и т.д.
Если же в unsafe блоке в коде ошибка, то может произойти UB с теми же последствиями, что и в С. Но тут программист хотя бы знает где начать искать ошибку.
Я наоборот вам показал, что связь не обязательно реализуется через WiFi и BLE. И в этом случае вовсе не обязательно использовать ESP32, подойдет любой МК.
Кроме того связь только одно из многих направлений, и для всех остальных подходит опять же любой МК, либо специализированный (не в нем вряд ли будет WiFi).
Всё это было к обсуждению, что новые ESP32 очень узкий случай. Для остального можно взять например старые ESP32, для которых не нужен Nightly компилятор.
Я вам ничего не приписывал, а пытался показать, что ваш случай очень узкий, а вы его обобщаете на все МК.
То есть, Вы придумали тезис, а теперь его опровергаете? Оригинально. Или Вы можете процитировать, где я писал обратное?
Какой именно я опровергаю? Вы сами придумали, и мне приписываете. Процитируйте.
При этом массово доступны ESP32-C3/C6 и т.п., RP2040, TLSR82696 и многие другие, появившиеся за последние пять лет.
Я писал про сравнение STM32F4 против STM32G4 как пример что G4 новые, а F4 старые. Причем тут другие МК?
Вот исходная моя цитата:
STM32G4xx тоже выпускается с 2021 года. Но все пишут на STM32F4, т.к. их проще купить (еще недавно G4 было вообще не купить) и их возможностей достаточно. Переход начался только сейчас, когда их цена почти сравнялась.
Демагогией развлекаетесь, да? Хватит выдирать из контекста!
В RTIC не совсем вытесняющая мультизадачность. Там общий стек, что по идеологии ближе к кооперативной мультизадачности.
Вытесняющая/кооперативная мультизадачность и общий/раздельный стек вообще разные вещи, и могут встречаться в любых сочетаниях друг с другом.
Например в RTIC вытесняющая с общим стеком, в FreeRTOS вытесняющая или кооперативная с раздельным, в Embassy вытесняющая или кооперативная с общим.
Во-первых, это не аппаратный планировщик, а лишь контроллер прерываний. Во-вторых, он есть только в CortexM.
И его можно использовать как планировщик. Но FreeRTOS не использует, из-за чего проигрывает в 6 раз по производительности на Cortex-M.
Как Вы собрались его применять в RP2040
Там есть ядра Cortex-M0+, в них есть NVIC.
)))
Ок, я был не прав. Как давно это добавили? Это обычный FreeRTOS, или специально допиленный для ESP32?
Как и любая OS, RTOS обязан поддерживать задачи на любых языках программирования.
Вы выступаете за интеграцию всего в RTOS, а я за подход "каждая библиотека делает только одну вещь". Это уже начало напоминать спор "Торвальдс против Танненбаума". Тут мы принципиально не сойдемся, нет смысла спорить.
Но замечу, что можно было бы выработать универсальное API для RTOS, как сделали в Embedded HAL для периферии. И тогда вопрос отпал бы сам собой, можно было бы применять любой подход.
Я боюсь, что скорее небезопасность Вы обнаружите в unsafe блоках, которыми изобилует Rust на МК или к Вам прилетит use-after-free от DMA, который не остановили по целому ряду причин. Уж поверьте коммитеру esp-rs, что таких приколов в Rust для embeded еще много не вычищено.
За это отвечают библиотеки HAL. А RTOS отвечает за безопасность переключений контекста. И код из разных языков ничего не знает о других языках. В С вылазят проблемы со статическими переменными и выделением/освобождением памяти, а в С++ (подключенном через С API) с RTTI, исключениями, выбрасыванием кода ошибки из деструкторов и т.д.
По-этому я за нормальные обертки на Rust, и категорически против прямого вызова С и С++ кода из RTOS.
Откажитесь от своего "золотого молотка".
Тезис про золотой молоток вы придумали сами, и как обычно мне приписываете. Берите нормальную обертку, которая учитывает времена жизни и проброс ошибок, и используйте код на других языках.
Уж поверьте коммитеру esp-rs
Это какое-то давление авторитетом? Я вот тоже например коммитил в Embassy, stm32f4xx-hal и т.д. и что?
А еще я писал свою RTOS с нуля на С, а потом переписал на Rust. А вы писали свою RTOS? Предлагаю не играть в авторитеты, а обсуждать по существу.
Я наоборот вам показал, что связь не обязательно реализуется через WiFi и BLE. И в этом случае вовсе не обязательно использовать ESP32, подойдет любой МК.
Кроме того связь только одно из многих направлений, и для всех остальных подходит опять же любой МК, либо специализированный (не в нем вряд ли будет WiFi).
Докажите.
Уже доказал. Зайдите на элитан, и посмотрите наличие. Любой новый МК будет пара штук в России, доставка от 30 дней, и не факт что вообще привезут. Могу еще привести Errata файлы, которые в момент выпуска МК неизвестны, и только через пару лет производитель их публикует.
Всё новое содержит баги, и это просто здравый смысл выждать время. Все мои знакомые так и поступают. Исключение - если нужен какой-то уникальный функционал, как у вас.
Сами хоть читали на что ссылаетесь? Там сравнивают вытесняющую мультизадачность с кооперативной. Если хотели сравнивать вытесняющую мультизадачность, то почему не порождали в RTIC отдельные задачи со своим стеком? А если хотели сравнивать кооперативную, то почему не сравнивали с короутинсами в FreeRTOS? Ну или хотя бы с родными async/await?
Это доказывает, что вы не читаете не только ссылки, но и комментарии. Я выше уже писал, что в RTIC у задач нет отдельного стека, и это гигантская экономия для процессора. Не нужно сохранять/подгружать регистры на стек, и еще делать кучу действий.
Там сравнение идет вытесняющей мультизадачности в RTIC с кооперативной в Embassy (на тот момент там еще не было вытесняющей), и с вытесняющей во FreeRTOS.
Но если всё переписать на вытесняющую, то результат не изменится, т.к. в Embassy и RTIC аппаратный планировщик NVIC против программного во FreeRTOS.
Что не умеет? Короутинсы поддерживает. Родные async/await тоже. О чем речь?
Запустите на FreeRTOS несколько кооперативных планировщиков с разным приоритетом. Если получится, я скажу что был не прав. Но не получится. И перестаньте читать по диагонали!
А вот ни Embassy, ни RTIC не поддерживают С ABI, что не позволяет обращаться к ним из других языков, кроме Rust. А это уже огромный минус, очень сильно сужающий их область применения.
А причем тут RTOS вообще? Это во первых не его задача, а во вторых небезопасно, легко вызвать race condition, и получить невоспроизводимый баг. Напишите / возьмите готовую обертку для нужной библиотеки, и используйте, в чем проблема?
Возможно вам больше подойдет TockOS и аналоги, чтобы запускать безопасно С код.
STM32G4xx тоже выпускается с 2021 года. Но все пишут на STM32F4, т.к. их проще купить (еще недавно G4 было вообще не купить) и их возможностей достаточно. Переход начался только сейчас, когда их цена почти сравнялась.
А вот с Вашей выборкой мне очень хочется ознакомиться. И думаю, далеко не только мне.
У меня тоже только личная выборка по общению с ~10 другими компаниями моего города.
Вы правда не видите, что первое Ваше предложение противоречит второму? )))
А в чем противоречие, если не секрет?
А зачем, если эта задача уже решена в FreeRTOS? Я удивлен, что Вам оплатили это велосипедостроение.
3 раза хаха. Во FreeRTOS одно из самых медленных (вероятно даже самое медленное) переключений контекста среди всех RTOS на С и С++. И это пример жесткого real-time? xD
Когда мне нужно действительно жесткий real-time, я беру RTIC. Это RTOS, полностью работающая на прерываниях и без отдельных стеков для потоков. Нет входа в PendSV и SVCall, нет планировщика, всё планирование на аппаратном NVIC.
RTIC в среднем в 3 раза быстрее переключает контекст, чем Embassy, который в среднем в 2 раза быстрее FreeRTOS (пруф).
Изучите FreeRTOS, прежде чем глупости писать. Наоборот, короутины (кооперативная мультизадачность) там появились не так давно, а уж приоритизация задач была от рождения.
Глупость пишите вы, т.к. не понимаете, что несколько планировщиков с разным приоритетом, внутри которых кооперативная многозадачность, будут работать значительно быстрее обычной вытесняющей многозадачности. Разделение ресурсов внутри кооперативной многозадачности не требует атомарных инструкций или отключения прерываний.
FreeRTOS так не умеет, но некоторые RTOS на С умеют. А в RTIC и Embassy всё на этом построено.
Легко. Цитирую:
Примерно 81% крейтов не содержат ни одной строчки unsafe. А среди остальных большинство - это FFI.
Теперь ваша очередь доказывать про пропаганду. Надеюсь получить аргументированный ответ.
Как и ожидалось, фразу про пропаганду вам подкрепить нечем.
Неужели так сложно сказать "я погорячился / я ошибся"?
Я вас прошу доказать вот эти ваши слова:
Потому что вы широко заявляете про пропаганду, но как пример приводите маленький проект, и обобщаете его на все проекты на Rust.
А я вам привел большой контрпример на 112kloc. Теперь, если вы стоите на своем, докажите на нескольких (больших) проектах.
Посмотрел. Похоже, что его транслировали из C в Rust (например этим), и потом добились того чтобы компилировался.
Я делаю такой вывод из того, что внутри написана реализация memset, memchr и юникода (лол). Так что ваш пример - это антипример.
P.S. Вот пример +- большого проекта (https://github.com/picodata/picodata), изначально написанного на Rust. На 112kloc Rust кода 238 строк unsafe (0.2%). Из них около 80% это плагин к tarantool, т.к. обращения к ffi.
Но restrict по умолчанию только в safe Rust. А в unsafe мы оказываемся ближе к С (но не на его уровне), и можно писать код на указателях, главное при переходе снова к ссылкам соблюдать ограничение restrict.
На практике за этим надо следить разработчикам библиотек, которые что-то оптимизируют через unsafe и указатели, а в прикладном коде unsafe практически не встречается.
Я считаю что это ошибка дизайна стандарта, и автор статьи тоже пытается это показать на примерах.
Причем не могу придумать ситуацию, где бы это было полезно. А значит можно было бы и поменять в новых версиях стандарта.
А вы точно прочитали статью? В конце раздела На сцене появляется C++11 ровно один указатель, и можно было бы заменить на std::string без изменения сути примера.
А суть в том, что на повторный std::move компилятор не выдает ошибку (и даже варнинг), и нужно много приседать, чтобы компилятор что-то заметил. Ито это не гарантируется.
Статью не читали, но осуждаете да?
На сколько я понимаю, GNU Coreutils не кроссплатформенный.
Возможно вы не знаете, но стандартная библиотека Rust тоже покрыта десятками тысяч тестов.
А если я подключу случайную библиотеку в Java, где гарантия, что она не вызовет внутри гонку данных? Или правильно вызовет С библиотеку, не приводя к UB.
Я думаю что требования для небезопасного кода одинаковые для всех языков: покрывать тестами, проверять санитайзерами, проводить фаззинг и т.д. Если библиотеки этого не делают, это не может быть претензией к самому языку.
Просто в Rust такие блоки кода намеренно выделены в блоки unsafe, чтобы быть заметнее. А в каком-нибудь Python вызов C-API легко затеряется среди тысяч строк кода.
Похоже вы отрицаете саму идею построения безопасного API поверх потенциально небезопасного кода.
Но если вы загляните в исходники стандартной библиотеки любого языка, то там будет и арифметика указателей, и ассемблерные вставки, и системные вызовы, что априори небезопасно. И тем не менее вызвать UB в C# например довольно трудно.
И? Там нет слова "неявное", что логично, т.к.
.into()
надо явно написать.Давайте приведу другой пример, тут уж должно быть абсолютно очевидно. Неявное приведение:
void func(uint8_t a) { ... }
func(-100)
Явное приведение с автоматическим выводом типа:
fn func(a: u8) { ... }
func((-100).into())
Если убрать
.into()
, то код не скомпилируется, т.к. в Rust сильная типизация, и неявных преобразований нет.Напишу на что не ответили другие.
Вероятно вы путаете неявное приведение и автоматический вывод типов. Или вы скажете, что
auto a = ... в С++
тоже неявное приведение?Вот это неявное приведение:
uint8_t a = -100
. А вот это автоматический вывод типов:let a: u8 = (-100).into()
.<sarcasm on> Да да, препроцессор гораздо лучше разбора AST. Это просто сказка! <sarcasm off>
Утиная типизация в С++ тоже имеет свои плюсы и минусы. Лично я думаю, что минусы перевешивают. Лучше написать чуть больше кода, но иметь больше контроля.
Единственное с чем соглашусь. Но это пока, посмотрим через пару лет.
unsafe - это контракт между программистом и компилятором. Если все его придерживаются, то он полностью избавляет от многих классов UB. На практике все ошибаются, и шанс не равен нулю, но очень мал.
Вообще я удивляюсь, что все так прицепились к unsafe блокам, как будто видят их впервые. В C# например они были еще с версии 1.0, задолго до Rust. И никого не смущало, что нужно оборачивать опасные места в коде в блоки unsafe.
В С например в документации к memcpy описано несколько вариантов, когда может случиться UB. В контексте этого обсуждения, это и есть тот самый недоверенный код. Но ведь все используют memcpy! Просто нельзя нарушать её инварианты.
В Rust такие функции помечают как unsafe, и в документации также как и в С описано, как её вызывать правильно, т.к. компилятор не может это проверить сам (а в случае asm! он вообще ничего проверить не может). А компилятор не даст просто так вызвать unsafe функцию.
Через unsafe блок программист говорит компилятору: я добавил в код все необходимые проверки (которые описаны в документации), и гарантирую что функция вызвана правильно. Ровно то же самое нужно делать на С с опасными функциями, но реализуется это не компилятором, а статическими анализаторами, санитайзерами и т.д.
Если же в unsafe блоке в коде ошибка, то может произойти UB с теми же последствиями, что и в С. Но тут программист хотя бы знает где начать искать ошибку.
Цитируйте, так уж полностью:
Всё это было к обсуждению, что новые ESP32 очень узкий случай. Для остального можно взять например старые ESP32, для которых не нужен Nightly компилятор.
Я вам ничего не приписывал, а пытался показать, что ваш случай очень узкий, а вы его обобщаете на все МК.
Но почему же вы ничего не отвечаете про RTOS? Это очень удобно, заострить внимание на чем то одном, и уйти от остальных вопросов. https://ru.wikipedia.org/wiki/Демагогия#Концентрация_на_частностях
Но тут описано всё правильно, и слог не похож на нейросеть. Статья полезная.
Лучше позвать модератора в те статьи, где все плохо.
Но в данной статье поднята важная проблема, и пути решения описаны правильно, за исключением одной маленькой опечатки.
Какой именно я опровергаю? Вы сами придумали, и мне приписываете. Процитируйте.
Я писал про сравнение STM32F4 против STM32G4 как пример что G4 новые, а F4 старые. Причем тут другие МК?
Вот исходная моя цитата:
Демагогией развлекаетесь, да? Хватит выдирать из контекста!
Вытесняющая/кооперативная мультизадачность и общий/раздельный стек вообще разные вещи, и могут встречаться в любых сочетаниях друг с другом.
Например в RTIC вытесняющая с общим стеком, в FreeRTOS вытесняющая или кооперативная с раздельным, в Embassy вытесняющая или кооперативная с общим.
И его можно использовать как планировщик. Но FreeRTOS не использует, из-за чего проигрывает в 6 раз по производительности на Cortex-M.
Там есть ядра Cortex-M0+, в них есть NVIC.
Ок, я был не прав. Как давно это добавили? Это обычный FreeRTOS, или специально допиленный для ESP32?
Вы выступаете за интеграцию всего в RTOS, а я за подход "каждая библиотека делает только одну вещь". Это уже начало напоминать спор "Торвальдс против Танненбаума". Тут мы принципиально не сойдемся, нет смысла спорить.
Но замечу, что можно было бы выработать универсальное API для RTOS, как сделали в Embedded HAL для периферии. И тогда вопрос отпал бы сам собой, можно было бы применять любой подход.
За это отвечают библиотеки HAL. А RTOS отвечает за безопасность переключений контекста. И код из разных языков ничего не знает о других языках. В С вылазят проблемы со статическими переменными и выделением/освобождением памяти, а в С++ (подключенном через С API) с RTTI, исключениями, выбрасыванием кода ошибки из деструкторов и т.д.
По-этому я за нормальные обертки на Rust, и категорически против прямого вызова С и С++ кода из RTOS.
Тезис про золотой молоток вы придумали сами, и как обычно мне приписываете. Берите нормальную обертку, которая учитывает времена жизни и проброс ошибок, и используйте код на других языках.
Это какое-то давление авторитетом? Я вот тоже например коммитил в Embassy, stm32f4xx-hal и т.д. и что?
А еще я писал свою RTOS с нуля на С, а потом переписал на Rust. А вы писали свою RTOS? Предлагаю не играть в авторитеты, а обсуждать по существу.
Я наоборот вам показал, что связь не обязательно реализуется через WiFi и BLE. И в этом случае вовсе не обязательно использовать ESP32, подойдет любой МК.
Кроме того связь только одно из многих направлений, и для всех остальных подходит опять же любой МК, либо специализированный (не в нем вряд ли будет WiFi).
Уже доказал. Зайдите на элитан, и посмотрите наличие. Любой новый МК будет пара штук в России, доставка от 30 дней, и не факт что вообще привезут.
Могу еще привести Errata файлы, которые в момент выпуска МК неизвестны, и только через пару лет производитель их публикует.
Всё новое содержит баги, и это просто здравый смысл выждать время. Все мои знакомые так и поступают.
Исключение - если нужен какой-то уникальный функционал, как у вас.
Это доказывает, что вы не читаете не только ссылки, но и комментарии. Я выше уже писал, что в RTIC у задач нет отдельного стека, и это гигантская экономия для процессора. Не нужно сохранять/подгружать регистры на стек, и еще делать кучу действий.
Там сравнение идет вытесняющей мультизадачности в RTIC с кооперативной в Embassy (на тот момент там еще не было вытесняющей), и с вытесняющей во FreeRTOS.
Но если всё переписать на вытесняющую, то результат не изменится, т.к. в Embassy и RTIC аппаратный планировщик NVIC против программного во FreeRTOS.
Запустите на FreeRTOS несколько кооперативных планировщиков с разным приоритетом. Если получится, я скажу что был не прав. Но не получится.
И перестаньте читать по диагонали!
А причем тут RTOS вообще? Это во первых не его задача, а во вторых небезопасно, легко вызвать race condition, и получить невоспроизводимый баг. Напишите / возьмите готовую обертку для нужной библиотеки, и используйте, в чем проблема?
Возможно вам больше подойдет TockOS и аналоги, чтобы запускать безопасно С код.
STM32G4xx тоже выпускается с 2021 года. Но все пишут на STM32F4, т.к. их проще купить (еще недавно G4 было вообще не купить) и их возможностей достаточно. Переход начался только сейчас, когда их цена почти сравнялась.
У меня тоже только личная выборка по общению с ~10 другими компаниями моего города.
А в чем противоречие, если не секрет?
3 раза хаха. Во FreeRTOS одно из самых медленных (вероятно даже самое медленное) переключений контекста среди всех RTOS на С и С++. И это пример жесткого real-time? xD
Когда мне нужно действительно жесткий real-time, я беру RTIC. Это RTOS, полностью работающая на прерываниях и без отдельных стеков для потоков. Нет входа в PendSV и SVCall, нет планировщика, всё планирование на аппаратном NVIC.
RTIC в среднем в 3 раза быстрее переключает контекст, чем Embassy, который в среднем в 2 раза быстрее FreeRTOS (пруф).
Глупость пишите вы, т.к. не понимаете, что несколько планировщиков с разным приоритетом, внутри которых кооперативная многозадачность, будут работать значительно быстрее обычной вытесняющей многозадачности. Разделение ресурсов внутри кооперативной многозадачности не требует атомарных инструкций или отключения прерываний.
FreeRTOS так не умеет, но некоторые RTOS на С умеют. А в RTIC и Embassy всё на этом построено.