Practical/common approaches to 'using' threadsafe functions for simple shared variables?
(By virtue of being at best rusty, I'm basically an Arduino/C++ beginner)
An evolving scenario:
- I start of with a simple problem: I have different functions that will want to access the same variable, so lets just assume a global variable.
- Now lets say 'some' these functions will actually be run on a loop inside a thread/task (in my case ESP32 dual core & FreeRTOS, but I'm I'd be interested in both platform specific comments and more general answers).
- But not 'all' of these functions will be tasks, some might just reside in setup() for a while.
- What this means is that I can't just use a global variable any more. The easiest thing to do is probably just to accept that I need to use some kind of threadsafe, specifically implemented tool like a semaphore(mutex?) or queue for the purpose, or some such. Most of these are platform/implementation specific API offerings, but the concepts they offer are fairly generic.
Now I've spent days looking into this, googling it, etc... and the more I look the further away I get from an understanding of a 'practical' approach. Perhaps literally no two examples agree, or build up a coherent pattern, as to how one would actually code with with these tools, in a way that wasn't likely to go horribly wrong because of one mistake (a mistake that would mean your code wasn't threadsafe, slips straight past the compiler, and only ever crops up in run time in the most 'impossible' of circumstances to even begin to debug the cause of.)
Lets assume that this would-be-global is very basic, but not super basic, so lets assume its a C++ string, or any abstracted platform specific data structure (so we can't be too sure of the under-the-hood details, we don't want to have to be and that's the whole point.) So as far as I know there is no point in relying on the 'atomic::' class as an option.
'Realtime' performance is 'not' 'critical', nor is space really, but lets at least follow enough good practice that we don't utterly murder the scheduling.
Lets assume we were to use a semaphore to gatekeep that our variable is only ever accessed appropriately.
Now, looked at naively we could implement use of that semaphore in every task that wants to apply to work with our would-be-global variable, but one misstep, and we are done for, we might as well not use it at all.
So it seems like there ought to be a way, or a tool, or a pattern, that uses in a semaphore in one place, or some other tool, and applies to accesses our shared variable through in place, so that we can check we've got our coding right once, and then move on, reasonably confident that we are good.
E.g:
- a class with a private property (variable)
- only methods provide public access to get and put to that private property
- internally the semaphores are used one time in one collection of code, to ensure safe access to out variable.
- perhaps our class should be an event oriented task itself?
- Is there someway to use queues here instead?
- What should I be returning where when and how to ensure that I don't just move my thread safety problem from one piece of code to another.
Now, I could keep looking at this (and I will) but it feels like I'm not getting any closer.
It seems like there ought to be a clear pattern for
- abstract problem, abstract code
- re-usably implementing thread safety in one place
- its for a simple problem:- like 'some' global data structure/object, but its not a complex resource like protocolling with a shared hardware interface or some such.
Any help with this would be appreciated.
I know that thread and RT concerns aren't the most beginner stuff, but it seems like this is the most basic problem to solve when you start working with tasks/threads, and no 'final complete' examples ever seem to pull in the same direction.
Sorry this is so rambling. But that's the issue, my progress has just got ever more rambling not less.
Thanks.