I am sharing a variable between a software timer and a function called from the loop. I am running into issues where the software timer is modifying the variable in the middle of the loop function and it is causing issues in that function. Any recommendations on how to avoid this problem?
What would the correct behaviour of your firmware be?
Do you want it to immediately start using the new value of the variable?
Another option would be to finish the function and then update the variable.
One possible solution is to use the software timer to set a flag instead of changing the variable.
The firmware can then check the flag at the appropriate time, and update the variable when you decide it should be updated.
Checking the flag could be in the main loop or in the function; it depends on the behaviour that you want to achieve.
This really all about thinking through how your program works. Key in any piece of software is planning, what you will find if you are not careful (particularly embedded I seem to find) is that if you are not careful you can end up with very high number of global variables. Whenever you create a variable think about what it is for, what can access it and why as well as the consequences of that access.
Wouldn’t it be a similar scenario since the loop and the timer share the flag?
As @Viscacha says, it is important to think through how your firmware works.
If you use a flag per my earlier suggestion, the flag would only be set by the software timer. Then the flag would be reset at the appropriate time. It is not about letting any part of your firmware set or reset a flag; but carefully controlling how it is used.
I think what Viscacha might be suggesting is to look carefully at the design of your firmware. Think carefully about what information is needed and how it is used; that will affect how you use variables. Also look at the order of steps in your firmware; getting the steps in just the right order might simplify things. There might be a solution more elegant than using a flag.
In this situation, I think a flag is not going to work. I have a software timer that reads the ADC, performs some math, and stores it in a variable. The main loop needs to publish the latest value of that variable upon a certain event independent of the status of software timer. Would using SINGLE_THREADED_BLOCK be helpful in this scenario where they need to share that variable?
@felixgalindo, since Software Timers run in a separate thread, then yes, using SINGLE_THREAD_BLOCK in loop as a way to prevent contention should work. The trick is to keep the blocking code short and fast. Also make sure to declare your shared variable
volatile so the compiler doesn’t do any optimization weirdness with it.
Another alternative is to copy your timer set variable into another, local, variable at the start of the loop function, so you don’t need to reference it again inside the function. Doing that single assignment in a single-threaded block would be safe and fast. In this way, the value stays stable for purposes of your function and is only updated when you expect it to be.
@apt, this is a good suggestion but will depend on the “timeliness” of the change in variable value. As pointed out earlier, a good design looks at the relationship between the Timer and loop() to establish how connected they need to be. This might mean a “loose” coupling via a queue or a tight coupling via a mutex. Using a simple flag that the Timer reads but loop() sets could be used as a simple mutex for example. The mutex would be set by loop() to indicate that data was being used and the Timer would skip modifying that data if the mutex was in “busy” mode. There are plenty of ways to cook this dish!
I’ve considered using a flag in other scenarios as sort of a mutex as well but would reading/writing a flag be considered an atomic operation. Was worried that I could write to the flag while in the other thread it was being read. Do you see this as a potential problem or would using the SINGLE_THREAD_BLOCK be the safest way to go? @peekay123
@felixgalindo, the chances of getting a preemptive thread interruption in the middle of an instruction (eg. flag = true) is minute. If you are concerned, specify it as a SINGLE_THREAD_BLOCK() or an ATOMIC_BLOCK().
There was a thread on creating FreeRTOS mutexes where a member successfully created a mutex for the SPI resource:
@peekay123 Would the SINGLE_THREAD_BLOCK only be required in the loop or also in the timer callback?
In loop() only since the Timer thread is higher priority and will preempt loop() unless blocked.