Blocking vs Non-blocking Code

A very significant portion of programming in embedded systems is recognizing and avoiding blocking code.
This also extends into operating systems and programming languages development, where we try to keep the same ideals.

First of all, lets go over what is blocking code.

Blocking code is code that prevents any other code from running.

The most common types of blocking code are:

Understanding Blocking

Consider the following code, no need to learn how to compile or run this, just understand what happens.

// basic-blocking.c
#include <stdio.h>

int main() {
  int outer = 0;

  while (outer < 2) {
    printf("outer = %d\n", outer);
    outer++;

    int inner = 0;
    while (inner < 3) {
      printf("inner = %d, blocked\n", inner);
      inner++;
    }
    printf("outer = %d, unblocked\n", outer);
  }
  return 0;
}

Then we compile:

> clang basic-blocking.c
> ./a.out
outer = 0
inner = 0, blocked
inner = 1, blocked
inner = 2, blocked
outer = 1, unblocked
outer = 1,
inner = 0, blocked
inner = 1, blocked
inner = 2, blocked
outer = 2, unblocked

As we can see the outer loop cannot increment, until the inner loop is done!

The outer-loop is parallel to functions like sleep and HAL_refresh in our code, which can be affected if you use while loops!

Example: Non-Interrupt Based

Generally, the main-loop design of our code involve the following pseudo-logic:

int main(void) {
  InitNonBlockingLED();            // init non-blocking check

  while(1) {                       // same as while(true)
    PollNonBlockingLED();          // toggle LED if button-pressed
    HAL_refresh(&hal)              // refresh peripherals from HAL
                                    // ERR : Two lines above will NOT run if you stay in
                                    //       Application_loop!
    Application_loop(&app, &hal);  // FSM
                                    // WARN: what do you think happens here if we block /
                                    //       stuck here?
  }
}

Please read through the comments, and then read the comments in bold.

If we are to have blocking code in our FSM function (for example, a while loop w/ condition inside the Application_loop), just like the earlier example, HAL_refresh cannot be run until the FSM is done, leading to delays or completely frozen in refreshing our peripherals.

Because PollingNonBlockingLED also resides on the same level as HAL_refresh the button to check for blocking will also be non-responsive.

Example: Interrupt-Based

Generally, the main-loop design of our interrupt-based code involve the following pseudo-logic:

int main() {
  construct();    // initialize


while(1) {
    main_loop();  // FSM
    sleep();       // ERR : If we block in the FSM above, will this run?
  }
}

Please read through the comments, and then read the comments in bold.

If we are to have blocking code in our FSM function (for example, a while loop w/ condition inside the main_loop), just like the earlier example, sleep cannot be run until the FSM is done, which prevents the processor from sleeping / going into low-power mode, defeating the purpose of interrupt-driven code.

Avoiding Blocking Code

Ideally, avoid while and for loops you don't understand.
Obviously, ideally, you should avoid any code you don't understand.

If you must use them, or don't understand where your code is blocking, use the following steps to debug: