1

I need to have data synchronization in my code. Currently I am accessing a global value inside interrupt and also in a local function which may corrupt the data if interrupt call is frequent. I need to avoid this scenario. I am not using operating system in my code, so I cannot use a semaphore. Using a similar locking method as semaphore might solve my problem.

Any help would be appreciated

Navin
  • 11
  • 3
  • 1
    Post your code. – Andrew Henle May 04 '17 at 10:18
  • 1
    Tell us something about your hardware environment. If you have a single CPU core with one thread of execution, the easy way is to disable interrupts while you are doing the update. In a multiprocessor environment, the processor instruction set will normally have an atomic test and set instruction or equivalent. – JeremyP May 04 '17 at 10:48

3 Answers3

4

Interrupts work differently than threads or processes - if a thread waits for a semaphore, it is simply not scheduled until either the semaphore gets available or, if given, the wait timeout elapses. In the mean while, other threads can be scheduled, one of those potentially giving back the semaphore.

This is not the case with interrupt service routines - these won't be interrupted by any thread scheduling (if at all, then only by other interrupts), but are executed until they return. So if a ISR gets to wait for a semaphore (or a similar mechanism, as you asked for), we are in a deadlock as the thread holding it can't be scheduled any more either to give the semaphore back...

So you need a totally different mechanism!

The usual way to do this is to disable the interrupt as long as your function needs to access the common data, and re-enable it afterwards (and you potentially need to do this within the ISR itself, too).

How? Well, OS/hardware specific - as long as you do not provide further details, I'm out here...

Just some hints yet: Keep the period of disabled interrupts as short as possible and make sure that the commonly accessed data is declared volatile!

Aconcagua
  • 24,880
  • 4
  • 34
  • 59
  • 1
    Lock-free data structures can help, too! – unwind May 04 '17 at 11:01
  • You don't have to wait in a forever-loop to grab a semaphore, you could as well just check if it is available. If not, the ISR will have to either discard the value or save it in a buffer. – Lundin May 04 '17 at 13:42
1

It's probably as simple as this in your main code:

disable_interrupts();
value += 1;
enable_interrupts();

So you make sure the interrupt cannot fire while you're using the value in the main code.

Brian Sidebotham
  • 1,706
  • 11
  • 15
  • 1
    If these functions change the global interrupt mask, then this is not a good idea. Ideally you should only disable the affected interrupt. – Lundin May 04 '17 at 13:43
  • @Lundin There's not enough information in the question to provide the ideal answer. – Brian Sidebotham May 04 '17 at 13:55
  • Either this code disables the global interrupt mask or it doesn't - it isn't clear and that has nothing to do with the question. On several systems I have worked with, a macro with the exact name `enable_interrupts()` results in setting/clearing the global interrupt mask. Which is usually not a good idea unless you have perfect control over all interrupts in the MCU. – Lundin May 04 '17 at 14:03
1

What you need is atomic access to the data. If it is a single variable and you can guarantee that access is atomic, then that is enough. However, this involves disassembling the C code and see what you ended up with. And even if the machine code ended up as atomic (single instruction), it won't be portable.

If you have a modern compiler with C11 support, you can declare the shared variable as _Atomic and that will solve the issue.

Another alternative is to simply shut off the particular interrupt during variable access in the caller. This will however disrupt real-time performance and you might miss out interrupts.

The universally "best" solution might be to invent a semaphore by yourself. Example:

// volatile to prevent dangerous compiler optimizations; does not solve re-entrancy
volatile uint32_t data;
volatile bool guard;

void ISR (void)
{
  if(!guard)
  {
    data = SOME_REGISTER;
  }
}

void main (void)
{
  ...
  guard = true;
  uint32_t local = data;
  guard = false;
}

In the above example no atomic access is guaranteed at all, not even to the guard variable. However, it is no longer necessary, because at the point where main() is about to read the data, the guard is guaranteed to be set. If the interrupt would kick in during the read, it wouldn't corrupt the data.

The only downside of this solution is that you will be missing out updating data when the guard is set. If this is an issue, you will have to implement some manner of temporary storage.

(Note that this code does not result in "memory barriers", so on complex multi-core processors, this method might not work and volatile will not necessarily result in a memory barrier. On ordinary microcontrollers it will work just fine though.)

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • The above solution is fine. My actual scenario is that; I am holding the value of a ADC interrupt in a global variable and updating it to a local variable inside another sub function. So the function and the interrupt will be called frequently ; So we need to set and reset the guard variable in the interrupt too and provide a check in the function before the local variable tries to access the global variable. The issue arises when the local variable tries to access the global variable and interrupt happens the same time and value of global variable getting updated resulting in data corruption. – Navin May 05 '17 at 02:56
  • @Navin The interrupt cannot get interrupted by the caller, so it is not necessary to set the guard there. (Unless you have a multicore solution with a second core executing the interrupt.) – Lundin May 05 '17 at 06:27
  • @Lundin very thorough answer covering off all things to think about – Brian Sidebotham May 05 '17 at 08:38
  • This solution has a chicken-and-egg problem: In the generic case, you cannot assume access to `guard` is atomic without disabling interrupts (in most cases, it will be, though). Back to square 1. – tofro Aug 28 '17 at 07:21
  • @tofro No, because _it doesn't matter_ if the access to `guard` is not atomic and interrupted - `guard` is not written to by any other code but main. In case it is interrupted, then the interrupt will execute and the main code gets back to setting that variable as if nothing happened. The only thing that matters is that at the point where main() is about to use the `data` variable, `guard` is guaranteed to be set, because the write to guard is sequenced before the read from `data`. This holds true in the generic case. – Lundin Aug 28 '17 at 07:29