If you block (for any reason, including acquiring a mutex) in a callback and are using a SingleThreadedExecutor, it will prevent other callbacks from being called.
You can either, avoid blocking and defer the work (asynchronous programming) or you can use a multi threaded executor, which can still get exhausted, but at least will allow other callbacks to be called at the same time so long as there are more thread available.
If you just need to ensure two callbacks do not get called at the same time (you would otherwise use a scoped lock with a shared mutex in the root of each callback) then you can instead put them in the same "MutuallyExclusiveCallbackGroup" (this is the default behavior if you just call create_{timer, subscription, service, client}
) and the then the executor will ensure they are not called concurrently in a multi threaded executor. This prevents you from having N callbacks, all waiting on the same mutex, from consuming and blocking in all N threads of your multi threaded executor.
See:
We've got some documentation about executors and callback groups in the works but it's not public yet.