Critical sections

Post your cool example code here.
Post Reply
edogaldo
Posts: 252
Joined: Fri Jun 03, 2016 8:19 am

Critical sections

Post by edogaldo » Fri Jul 22, 2016 3:27 pm

Dear all, I'd like to share a proposal for defining critical sections, that is: pieces of code that are guaranteed not to be interrupted by interrupts.
atomic.h
(2.41 KiB) Downloaded 83 times
Critical sections are useful to safely and consistently access data which is modified by interrupts.

Sample usage:

Code: Select all

// We'll use timer 2
#include "atomic.h"

#define TIMER_PERIOD 10000    // in microseconds; should give 100Hz toggles

HardwareTimer timer(2);

uint64 myVar=0;
uint64 myVarCopy0=0;
uint64 myVarCopy1=0;
uint64 myVarCopy2=0;

void setup() {
    Serial.begin(57600);

    // Pause the timer while we're configuring it
    timer.pause();

    // Set up period
    timer.setPeriod(TIMER_PERIOD); // in microseconds

    // Set up an interrupt on channel 1
    timer.setChannel1Mode(TIMER_OUTPUT_COMPARE);
    timer.setCompare(TIMER_CH1, 1);  // Interrupt 1 count after each update
    timer.attachCompare1Interrupt(timer_handler);

    // Refresh the timer's count, prescale, and overflow
    timer.refresh();

    // Start the timer counting
    timer.resume();
}

void loop() {
    myVarCopy0 = myVar; // unsafe access to myVar

    // sample critical section 1
    __start_critical();
    myVarCopy1 = myVar; // safe access to myVar
    __stop_critical();
    
    // sample critical section 2
    ATOMIC_BLOCK() {
      myVarCopy2 = myVar; // safe access to myVar
    }
    
    Serial.println(myVarCopy0);
    Serial.println(myVarCopy1);
    Serial.println(myVarCopy2);
    
    delay(1000);
}

void timer_handler(void) {
    myVar++;
}
Edit: as always, best practice is to keep critical sections as small as possible to minimize the risk to lose interrupt events.
Last edited by edogaldo on Sat Jul 23, 2016 5:47 pm, edited 2 times in total.

User avatar
Pito
Posts: 1385
Joined: Sat Mar 26, 2016 3:26 pm
Location: Rapa Nui

Re: Critical sections

Post by Pito » Sat Jul 23, 2016 2:18 pm

I had to change the evaluation (newest core version with TXbuffering, MMini):

Code: Select all

..
    // sample critical section 2
    ATOMIC_BLOCK() {
      myVarCopy2 = myVar; // safe access to myVar
    }

    if ((myVarCopy0 != myVarCopy1) || (myVarCopy0 != myVarCopy2) || (myVarCopy1 != myVarCopy2)) {
    Serial1.println(myVarCopy0);
    Serial1.println(myVarCopy1);
    Serial1.println(myVarCopy2);
    Serial1.println("===");
    myVar=0;
    myVarCopy0=0;
    myVarCopy1=0;
    myVarCopy2=0;
    }
    ..
as with the original I got correct readings even with TIMER PERIOD = 100.

For example with above code and TIMER PERIOD = 1000:

Code: Select all

===
438
439
439
===
0
0
1
===
4
4
5
===
34
34
35
===
4
4
5
===
23
23
24
===
3
3
4
===
783
784
784
===
238
239
239
===
60
61
61
===
6
6
7
===
356
357
357
===
62
63
63
===
1
1
2
===
Interestingly the both atomic readings differ, sometimes.. :)
What went wrong?
Pukao Hats Cleaning Services Ltd.

edogaldo
Posts: 252
Joined: Fri Jun 03, 2016 8:19 am

Re: Critical sections

Post by edogaldo » Sat Jul 23, 2016 4:53 pm

Pito wrote: Interestingly the both atomic readings differ, sometimes.. :)
What went wrong?
Nothing went wrong, copy1 and copy 2 are not in the same critical section so nothing prevents an interrupt to fire between the two critical sections.
If you want the same reading you have to put them in the same critical section.
Anyway the purpose of my sample was to just show the 2 ways to define the critical sections.
Last edited by edogaldo on Sat Jul 23, 2016 5:26 pm, edited 1 time in total.

edogaldo
Posts: 252
Joined: Fri Jun 03, 2016 8:19 am

Re: Critical sections

Post by edogaldo » Sat Jul 23, 2016 5:08 pm

Maybe this can better show the race condition problem addressed by the library:

Code: Select all

// We'll use timer 2
#include "atomic.h"

#define TIMER_PERIOD 1000    // in microseconds; should give 1000Hz toggles

HardwareTimer timer(2);

uint64 myVar=0;
uint64 myVarCopy0=0;
uint64 myVarCopy1=0;
uint64 myVarCopy2=0;

void setup() {
    Serial.begin(57600);

    // Pause the timer while we're configuring it
    timer.pause();

    // Set up period
    timer.setPeriod(TIMER_PERIOD); // in microseconds

    // Set up an interrupt on channel 1
    timer.setChannel1Mode(TIMER_OUTPUT_COMPARE);
    timer.setCompare(TIMER_CH1, 1);  // Interrupt 1 count after each update
    timer.attachCompare1Interrupt(timer_handler);

    // Refresh the timer's count, prescale, and overflow
    timer.refresh();

    // Start the timer counting
    timer.resume();
}

void loop() {
    myVarCopy0 = myVar; // unsafe access to myVar

    // sample critical section 1
    __start_critical();
    myVarCopy1 = myVar; // safe access to myVar
    __stop_critical();
    
    // sample critical section 2
    ATOMIC_BLOCK() {
      myVarCopy2 = myVar; // safe access to myVar
    }
    if(myVarCopy0 != myVarCopy1 && myVarCopy0 != ~myVarCopy1) {
      Serial.println(myVarCopy0, HEX);
      Serial.println(myVarCopy1, HEX);
      Serial.println(myVarCopy2, HEX);
    }
    //delay(1000);
}

void timer_handler(void) {
    myVar = ~myVar;
}

Post Reply

Who is online

Users browsing this forum: No registered users and 1 guest