I'm following the tutorial (already in lesson 16), and I'm missing some advanced background information on how threads are scheduled in Bela, how to write thread safe code, how to write IO code (networking for example) that interact with the audio main thread, etc.
Where I can find more information on this subject?
Threads in Bela
there's some detail here https://forum.bela.io/d/853-cannot-read-data-sent-over-serial-port/5
and I hoped we had much more somewhere, but an extensive search didn't produce the results I expected.
uzi_hs how threads are scheduled in Bela,
the audio thread is a Xenomai thread scheduled with priority 95. That should be the highest priority thread on the board. You can schedule auxiliary threads the "usual" way (using e.g.: std::thread
or pthread_create()
), or using Bela's AuxiliaryTask
API: http://docs.bela.io/group__auxtask.html. The advantages of AuxiliaryTask
are:
- these are Xenomai threads, so they can perform with real-time priority ( above Linux priority, but below the audio thread), so they can be used e.g.: for DSP co-routines (see e.g.:
Audio/FFT-phase-vocoder
). They also have access to Xenomai functions, so they can lock a Xenomai mutex or write to the RT side of a Xenomai pipe (seePipe
below). - the API to create/destroy is simpler than
pthread
and it cleans up after itself automatically so there is no need to manually call the equivalent ofstd::thread::join
orpthread_join()
. - they can be scheduled safely and simply from the audio thread (via
Bela_scheduleAuxiliaryTask()
), without need to create a mutex or condition variable
In many cases, we use them for disk or other peripheral I/O, see e.g.:
Multichannel/multichannel-player
terminal-only/filter-FIR
terminal-only/samples
terminal-only/filter-IIR
Audio/FFT-phase-vocoder
Audio/sample-streamer
Audio/sample-streamer-multi
Communication/SPI
Communication/Serial
or for logging to console large chunks of text without bothering the audio thread, e.g.:
Capelets/multiplexer-spectrum
Extras/measure-noisefloor
Extras/cpu-monitoring
Writing thread safe code depends on the application. In our examples, we often communicate via a 32-bit shared variable between the two threads. This is simple but often effective, though its timing is not guaranteed and in principle you could encounter some edge cases where the behaviour is not deterministic (e.g.: thread 1 sets some data and then a flag variable, but thread 2 sees the updated flag variable before the data set by thread 1 has been properly updated in memory). In many cases this is not a major issue, but in some it may be crucial. When we are reading data from Trill sensors we simply update a global array containing position and size and then the number of touches. If the number of touches is updated before the global array is updated, you may get some spurious readings when adding or removing one touch, but - again - in many cases these go unnoticed if they at all happen: it all depends on how the code that uses the data reacts to it. For large data structures, you can also share a global pointer and update it from one thread when you want to notify the other thread, although this has the usual issues of not containing a memory barrier, so it's not 100% thread safe, but it may be good enough if a structure in an inconsistent state for a brief moment won't cause noticeable glitches in your program.
For proper thread-safe operation, we often recommend using the Pipe class, which allows bidirectional communication of large (or small!) chunks of data between two threads (one of which must be a Xenomai thread, i.e.: either the audio thread or a thread created with Bela_{create,run}AuxiliaryTask()
), The Pipe
can also be blocking, which is useful to avoid spurious wakeups when an auxiliary thread only needs to run when new data is available. Examples using it are:
Communication/Serial
Communication/OSC-Pipe
Gui/frequency-response
Trill/general-settings
Other alternatives for proper thread-safe signalling: lightweight C++11 atomics, or more heavy-handed Xenomai mutex (see https://forum.bela.io/d/2622-what-mutex-to-use-on-auxiliarytask/2 ) .
Thanks giuliomoro