That does sound like a PIA.
That does sound like a PIA.
In many cases when you have smaller systems, you actually don't want your OS to do dynamic memory allocation, since it opens up for all kinds of bugs related to eg double-free, or use-after-free, or running out of heap, or too defragmented heap.
It's also easier to calculate memory usage (and thus be fairly certain it will be enough) if you allocate as much of the memory as possible as static memory. Hence why some coding standards such as MISRA-C disallows use of dynamic memory allocation.
Exactly what caused the lock-up here isn't delved deeper into, but it might be that there was not enough heap, or not a working malloc-implementation linked into the compiled binary (could be a stub), or could be that a non-re-entrant printf was linked in while they tried printf from several threads.
The USB CDC version is much faster but may not always be appropriate for your project.
I implemented one that uses a buffer and an interrupt to feed the UART in the background, and then I disabled the plugin the Pico SDK provided. I'm not using an RTOS.
The SDK example [uart_advanced](https://github.com/raspberrypi/pico-examples/blob/master/uar...) [github.com/raspberrypi] shows how to set up UART interrupts using the Pico SDK
Printf isn't re-entrant, and they are calling it from multiple threads.
There are solutions with trade offs: https://interrupt.memfault.com/blog/printf-on-embedded
Everything in embedded is a tradeoff of space, performance, or cost.
If you come at it as web development with IO you are going to have a very bad time.
This! Simple schedulers generally only allow system calls (such as printf) from the main thread. If you really want to 'print' from a child thread then send a message to the main thread, asking that it prints the message contents on on behalf of the child thread.