Using:
- GCC 4.8
- pure-gcc Setup
- Sdk 5.2.0
- Softdevice 6.0
I execute some code within interrupt context (softdevice handler) and also in main thread. I need to increment / decrement an integer variable concurrent in both thread context (e.g. for some refcounted objects within my inter-thread communication).
Since we unfortunately have no __sync_add_and_fetch nor std::atomic on the M0 core, I have to synchronize the concurrent access in a different way.
1st approach using CRITICAL_REGION_ENTER/EXIT()
Currently, I synchronize the access using CRITICAL_REGION_ENTER/EXIT(), but it seems a bit brutally.
2nd approach using sd_mutex_…()
Might that be a better solution? What the costs (ram & runtime) for SD mutexes?
3nd approach disabling interrupts
In some places I'd read that it was a bad idea to disable interrupts during ble communication. Also a bad idea for the small time of just a int val = ++(*refcount);
?
Edit 2014-08-28: I'd now made further investigations into the 3 approaches.
CRITICAL_REGION_ENTER/EXIT()
CRITICAL_REGION_ENTER();
(*refcount)++;
CRITICAL_REGION_EXIT();
The duration of these instructions is about 25 µs.
sd_mutex
errCode = sd_mutex_acquire(&mutexHandle);
APP_ERROR_CHECK(errCode);
(*refcount)++;
errCode = sd_mutex_release(&mutexHandle);
APP_ERROR_CHECK(errCode);
The duration of these instructions is about 20 µs. But this approach won't work for my problem. sd_mutex_acquire() is not that kind of mutex lock we know from threading concurrency. The call is more like a »try lock«. If the mutex already is locked, then sd_mutex_acquire() returns with NRF_ERROR_SOC_MUTEX_ALREADY_TAKEN instead of passing control to the code which holds the lock (see here).
disabling interrupts
__disable_irq();
(*refcount)++;
__enable_irq();
The duration of these instructions is about 800 ns.
Finally, I decide to use CRITICAL_REGION_ENTER/EXIT() (even it's much slower than disabling irq) because I don't want to disturb the radio traffic. Since I like to have it in C++, I'd wrote a small RAII class based on the sdk macro code:
class CriticalSection {
public:
explicit CriticalSection()
: intPrio(current_int_priority_get())
, nested(0)
{
if (intPrio != APP_IRQ_PRIORITY_HIGH) {
uint32_t errCode = sd_nvic_critical_region_enter(&nested);
if (errCode == NRF_ERROR_SOFTDEVICE_NOT_ENABLED)
__disable_irq();
else
APP_ERROR_CHECK(errCode);
}
}
~CriticalSection() {
if (intPrio != APP_IRQ_PRIORITY_HIGH) {
__enable_irq();
uint32_t errCode = sd_nvic_critical_region_exit(nested);
if (errCode != NRF_ERROR_SOFTDEVICE_NOT_ENABLED)
APP_ERROR_CHECK(errCode);
}
}
int getIntPrio() const { return intPrio; }
private:
uint8_t intPrio;
uint8_t nested;
};
And my code now looks like this:
{
CriticalSection cs;
(*refcount)++;
}
Edit: Please note that this don't work for nested critical sections when softdevice is disabled (see also this post).
Cheers, Joe