Critical sections
CANopen_301 » Driver

CANopenNode is designed to run in different threads, as described in README.md. Threads are implemented differently in different systems. In microcontrollers threads are interrupts with different priorities, for example. It is necessary to protect sections, where different threads access to the same resource. In simple systems interrupts or scheduler may be temporary disabled between access to the shared resource. Otherwise mutexes or semaphores can be used. More...

## Macros

#define | CO_LOCK_CAN_SEND()
| Lock critical section in CO_CANsend()
#define | CO_UNLOCK_CAN_SEND()
| Unlock critical section in CO_CANsend()
#define | CO_LOCK_EMCY()
| Lock critical section in CO_errorReport() or CO_errorReset()
#define | CO_UNLOCK_EMCY()
| Unlock critical section in CO_errorReport() or CO_errorReset()
#define | CO_LOCK_OD()
| Lock critical section when accessing Object Dictionary.
#define | CO_UNLOCK_OD()
| Unock critical section when accessing Object Dictionary.
#define | CO_FLAG_READ(rxNew) ((rxNew) != NULL)
| Check if new message has arrived.
#define | CO_FLAG_SET(rxNew) { __sync_synchronize(); rxNew = (void *)1L; }
| Set new message flag.
#define | CO_FLAG_CLEAR(rxNew) { __sync_synchronize(); rxNew = NULL; }
| Clear new message flag.

## Detailed Description

CANopenNode is designed to run in different threads, as described in README.md. Threads are implemented differently in different systems. In microcontrollers threads are interrupts with different priorities, for example. It is necessary to protect sections, where different threads access to the same resource. In simple systems interrupts or scheduler may be temporary disabled between access to the shared resource. Otherwise mutexes or semaphores can be used.

#### Reentrant functions

Functions CO_CANsend() from C_driver.h, CO_errorReport() from CO_Emergency.h and CO_errorReset() from CO_Emergency.h may be called from different threads. Critical sections must be protected. Either by disabling scheduler or interrupts or by mutexes or semaphores.

#### Object Dictionary variables

In general, there are two threads, which accesses OD variables: mainline and timer. CANopenNode initialization and SDO server runs in mainline. PDOs runs in faster timer thread. Processing of PDOs must not be interrupted by mainline. Mainline thread must protect sections, which accesses the same OD variables as timer thread. This care must also take the application. Note that not all variables are allowed to be mapped to PDOs, so they may not need to be protected. SDO server protects sections with access to OD variables.

#### Synchronization functions for CAN receive

After CAN message is received, it is pre-processed in CANrx_callback(), which copies some data into appropriate object and at the end sets new_message flag. This flag is then pooled in another thread, which further processes the message. The problem is, that compiler optimization may shuffle memory operations, so it is necessary to ensure, that new_message flag is surely set at the end. It is necessary to use Memory barrier.

If receive function runs inside IRQ, no further synchronization is needed. Otherwise, some kind of synchronization has to be included. The following example uses GCC builtin memory barrier `__sync_synchronize()`. More information can be found here.