System thread

SINGLE_THREADED_BLOCK()

SINGLE_THREADED_BLOCK() declares that the next code block is executed in single threaded mode. Task switching is disabled until the end of the block and automatically re-enabled when the block exits. Interrupts remain enabled, so the thread may be interrupted for small periods of time, such as by interrupts from peripherals.

// SYNTAX
SINGLE_THREADED_BLOCK() {
   // code here is executed without thread swapping
}

Here's an example:

void so_timing_sensitive()
{
    if (ready_to_send) {
           SINGLE_THREADED_BLOCK() { 
        // single threaded execution starts now

        // timing critical GPIO
            digitalWrite(D0, LOW);        
            delayMicroseconds(250);
            digitalWrite(D0, HIGH);
          }
      // thread swapping can occur again now
    }  
}

You must avoid within a SINGLE_THREADED_BLOCK:

  • Lengthy operations
  • Calls to delay() (delayMicroseconds() is OK)
  • Any call that can block (Particle.publish, Cellular.RSSI, and others)
  • Any function that uses a mutex to guard a resource (Log.info, SPI transactions, etc.)
  • Nesting. You cannot have a SINGLE_THREADED_BLOCK within another SINGLE_THREADED_BLOCK.

The problem with mutex guarded resources is a bit tricky. For example: Log.info uses a mutex to prevent multiple threads from trying to log at the same time, causing the messages to be mixed together. However the code runs with interrupts and thread swapping enabled. Say the system thread is logging and your user thread code swaps in. The system thread still holds the logging mutex. Your code enters a SINGLE_THREADED_BLOCK, then does Log.info. The system will deadlock at this point. Your Log.info in the user thread blocks on the logging mutex. However it will never become available because thread swapping has been disabled, so the system thread can never release it. All threads will stop running at this point.

Because it's hard to know exactly what resources will be guarded by a mutex its best to minimize the use of SINGLE_THREADED_BLOCK.