is a read/write from/to an uint8_t atomic?

Greetings,

the title says it all; an interrupt function decodes an IR remote signal and delivers a uint8_t value; the main control flow periodically reads this value and decides what to do; I don't mind reading an older value as long as the value isn't 'corrupted' by my interrupt handler; it can never be corrupt if the reading/writing of this variable is atomic. Is this so?

thanks in advance and kind regards,

Jos

A change to a 8 bit variable is atomic. Changes to larger values are not. However, interrupts are disabled during an interrupt service routine, so corruption of a multibyte variable will not occur in the ISR.

The value in a multi-byte variable may change in the middle of a read/write operation outside of the ISR. That needs to protected by disabling interrupts during the read/write and then re-enabling them.

PaulS:
A change to a 8 bit variable is atomic. Changes to larger values are not. However, interrupts are disabled during an interrupt service routine, so corruption of a multibyte variable will not occur in the ISR.

Thanks; that's what I wanted to know; I do enable the interrupts in my function though (reading from an IR takes several milli seconds and I want timers etc. to go on). But as long as that single byte is changed atomically, all is fine.

Thanks again for your reply and kind regards,

Jos

JosAH:
I do enable the interrupts in my function though

You are enabling interrupts within an interrupt handler? That is a big red flag.

PeterH:

JosAH:
I do enable the interrupts in my function though

You are enabling interrupts within an interrupt handler? That is a big red flag.

No it isn't, I know what I'm doing (<--- famous last words :wink: My interrupt function is triggered by a falling edge and then it starts decoding an IR signal from a digital pin; that can takes several milli seconds (I simply poll that pin in the interrupt function) and I want the millis() etc. interrupts to go on. At the start of my function I've detached it from the particular interrupt because I don't want it to be called recursively by another falling edge on that pin. b.t.w. I only enable interrupts after I've detached my function from that falling edge interrupt.

kind regards,

Jos

JosAH:
I know what I'm doing

You "know what you're doing" but don't know that eight-bit writes are atomic? This is not very reassuring.

PeterH:

JosAH:
I do enable the interrupts in my function though

You are enabling interrupts within an interrupt handler? That is a big red flag.

Not really a 'red flag', just not recommended by those that don't understand the effect of using nested interrupts. But one certainly can re-enable interrupts within a ISR if required and understand all the effects of doing that.

Lefty

PeterH:

JosAH:
I know what I'm doing

You "know what you're doing" but don't know that eight-bit writes are atomic? This is not very reassuring.

I dunno, if the OP has experience on other platforms, he/she may have known what was the atomic types there, but was asking about the AVR in particular. On the 20-30 different machines I have worked on, it has varied from byte to 16-bit, to 32-bit to 64-bit types that are suitably aligned depending on the underlying memory architecture of the machine. If you are dealing with systems that have multiple processors accessing the same memory pool, it can get even more complex and you need to line things up on a cache line and use special instructions to get a lock. Given the AVR is an 8-bit machine, and generates multiple instructions to handle 16 and 32-bit types, 8-bit chars are the right answer, while on the ARM, int is listed as sig_atomic_t.

Arithmetic is probably not atomic (eg. foo++);

Also be cautious of stuff like this:

if (foo > 100)
  foo = 0;

However a simple read would be OK.

I want the millis() etc. interrupts to go on.

If the whole things takes multiple milliseconds, can't you decode outside the ISR? Or do it another way? The IRremote library, for example, uses a timer interrupt to check (at a fixed interval) whether the incoming pulse is high or low.

JosAH:

PeterH:
You are enabling interrupts within an interrupt handler? That is a big red flag.

No it isn't, I know what I'm doing (<--- famous last words :wink: My interrupt function is triggered by a falling edge and then it starts decoding an IR signal from a digital pin; that can takes several milli seconds (I simply poll that pin in the interrupt function) and I want the millis() etc. interrupts to go on. At the start of my function I've detached it from the particular interrupt because I don't want it to be called recursively by another falling edge on that pin. b.t.w. I only enable interrupts after I've detached my function from that falling edge interrupt.

Instead of polling the IR receiver in the ISR, why don't you leave the interrupt attached, and at each interrupt:

  • call micros() to get the current time
  • calculate the time since the previous edge, so that you can decode the next bit
  • store the time you got from micros(), ready for the next interrupt

PaulS:
A change to a 8 bit variable is atomic.

A change to an eight bit variable is not necessarily atomic depending on what "change" means.
It will not be atomic if "change" means to modify part of the 8 bit variable
because of the AVR being RISC

For example, on some processors, operations like:

foo |= mask;
foo &= ~mask;

can be atomic because they compile down to a non interruptible instruction.
On the AVR these operations are not atomic because they compile down to multiple
instructions which can be interrupted.

When using an 8 bit variable on the AVR, a read is atomic and a write is atomic
an update/change to an 8 bit variable that involves reading/modifying/writing back the variable
to preserve a portion of it's previous contents is not atomic.

However, there are some specific AVR bit set/clear instructions that can be used on certain hardware
registers that can provide atomicity of setting or clearing an individual bit (not multiple bits).

--- bill

I agree with Bill. Setting/clearing one bit (where the bit is known at compile time) the compiler usually generates a single atomic instruction. However doing multiple bits, or arithmetic, is unlikely to be atomic.

Also, even if it is a single bit and the bit is known at compile time, SBI/CBI instructions can only be done
on an i/o registers vs a ram based 8 bit variable.

PeterH:

JosAH:
I know what I'm doing

You "know what you're doing" but don't know that eight-bit writes are atomic? This is not very reassuring.

I don't know much about the Atmega internal memory model, that's why I asked here; if it had an internal four bit bus so that nibbles were written and read the read/writes could've been non-atomic; but they are so I'm happy again :wink:

kind regards,

Jos

Fair enough, but enabling interrupts inside an ISR is a last resort, as far as I am concerned.

dc42:

JosAH:

PeterH:
You are enabling interrupts within an interrupt handler? That is a big red flag.

No it isn't, I know what I'm doing (<--- famous last words :wink: My interrupt function is triggered by a falling edge and then it starts decoding an IR signal from a digital pin; that can takes several milli seconds (I simply poll that pin in the interrupt function) and I want the millis() etc. interrupts to go on. At the start of my function I've detached it from the particular interrupt because I don't want it to be called recursively by another falling edge on that pin. b.t.w. I only enable interrupts after I've detached my function from that falling edge interrupt.

Instead of polling the IR receiver in the ISR, why don't you leave the interrupt attached, and at each interrupt:

  • call micros() to get the current time
  • calculate the time since the previous edge, so that you can decode the next bit
  • store the time you got from micros(), ready for the next interrupt

That was my first approach; the code ended up a bit too messy: it was a state machine and it had to keep track of a bit too many state variables; I rewrote it and the code looks a bit cleaner now. It isn't cast in stone that this will be my final implementation maybe I'll go back to your suggestion if I can make a cleaner implemenation.

Thanks for replying and kind regards,

Jos

Why is everybody so itchy when it comes to (re)enabling interrupts in an interrupt handler? I'm not longjmp( ... )ing around, I just want the micros() and millis() things to keep working (I have to keep track of a wall clock time (*)). Neither does my interrupt handler need to be reentrant ...

kind regards,

Jos

(*) yes, the device syncs its notion of time with an NTP server each day.

Let me turn the question around.

What is your objection to doing what the IRremote library does? It doesn't re-enable interrupts inside an ISR.

I've seen that library; it records 'ticks' between state transitions and it uses 100 two byte ints for it (200 bytes!) and it decodes afterwards. I decode 'on the fly' for one particular protocol (NEC); reading 32 bits in that protocol takes +- 108 millis and I don't want to lose the millis() ticks. The 'header' pulse takes 560us and I might lose a millis() tick in there. Maybe if I use edge change interrupts (my first approach) I don't need to (re)enable interrupts inside my handler ...

kind regards,

Jos

You won't lose a millis() tick in 560uS - the interrupt is queued.