System thread

Synchronizing access to shared system resources

With system threading enabled, the system thread and the application thread run in parallel. When both attempt to use the same resource, such as writing a message to Serial, there is no guaranteed order - the message printed by the system and the message printed by the application are arbitrarily interleaved as the RTOS rapidly switches between running a small part of the system code and then the application code. This results in both messages being intermixed.

This can be avoided by acquiring exclusive access to a resource. To get exclusive access to a resource, we can use locks. A lock ensures that only the thread owning the lock can access the resource. Any other thread that tries to use the resource via the lock will not be granted access until the first thread eventually unlocks the resource when it is done.

There are a number of shared resources that can be used by the system. These include:

  • Serial when using listening mode, or when using SerialLogHandler.
  • SPI on some devices that use Ethernet.
  • Wire (I2C) on some devices that use a PMIC and fuel gauge on primary I2C.
  • PMIC on devices that use the system power manager and the bq24195 PMIC.

This example shows how to use locking to share the UART serial port Serial between two threads.

void print_status()
{
    WITH_LOCK(Serial) {
        Serial.print("Current status is:");
            Serial.println(status);
    }
}

The primary difference compared to using Serial without a lock is the WITH_LOCK declaration. This does several things:

  • attempts to acquire the lock for Serial. If the lock isn't available, the thread blocks indefinitely until it is available.

  • once Serial has been locked, the code in the following block is executed.

  • when the block has finished executing, the lock is released, allowing other threads to use the resource.

It's also possible to attempt to lock a resource, but not block when the resource isn't available.

TRY_LOCK(Serial) {
    // this code is only run when no other thread is using Serial
}

The TRY_LOCK() statement functions similarly to WITH_LOCK() but it does not block the current thread if the lock isn't available. Instead, the entire block is skipped over.