I read that SMP feature:
"
Symmetric memory (with some small exceptions).
If multiple cores access the same memory address, their access will be serialized at the memory bus level.
True atomic access to the same memory address is achieved via an atomic compare-and-swap instruction provided by the ISA.
"
So does that mean that I can create global variable and not worry about multi thread access?
Why do I need thread safety if Symmetric Multiprocessing protects memory
-
- Posts: 9766
- Joined: Thu Nov 26, 2015 4:08 am
Re: Why do I need thread safety if Symmetric Multiprocessing protects memory
Assuming that the variable is 8, 16 or 32-bit and only accessed as such (if it's not, the answer is always 'no'): Depends on your definition of 'access'. If you have one thread writing it and another thread reading it, there's no issue. However, if you have an operation that requires multiple accesses, you still need thread safety. E.g. if a variable is used as a semaphore: thread 1 sets the variable to 1, thread 2 reads the variable and if 1, it clears it to 0 and does something. In that case, the read and write are two different operations, and thread 1 could 'sneak in' another 'set to 1' in there, which would get lost.
Re: Why do I need thread safety if Symmetric Multiprocessing protects memory
Thank you! It is clear now. And I have only 32-bit words with one task writing and the other reading.
-
- Posts: 9766
- Joined: Thu Nov 26, 2015 4:08 am
Re: Why do I need thread safety if Symmetric Multiprocessing protects memory
Do note that you probably want to document the ever-loving crap out of that variable if you're not using atomic instructions to read/write it. It's easy to forget that that thing only works because the architecture happens to do 32-bit reads/writes atomically by itself, change it into e.g. a 64-bit variable or struct, and introduce very subtle bugs in your program.
Re: Why do I need thread safety if Symmetric Multiprocessing protects memory
Are you psychic? I already forgot today and made it a struct then I came here to read your previous answer, just to find the new
I made a new single task that reads messages from MessageBuffer and write the new incoming value to one member of the global struct, then in the same task read all the values. So now that there is only one task that deals with this global struct and I document that this global struct must not be accessed from another task, would I be safe?
I made a new single task that reads messages from MessageBuffer and write the new incoming value to one member of the global struct, then in the same task read all the values. So now that there is only one task that deals with this global struct and I document that this global struct must not be accessed from another task, would I be safe?
-
- Posts: 9766
- Joined: Thu Nov 26, 2015 4:08 am
Re: Why do I need thread safety if Symmetric Multiprocessing protects memory
Nah, I simply have lots of experience... you could say that by now I'm an expert at creating bugs in code at some point you kinda get an intuition for what constructions are 'dangerous', as in they work but are easy to misunderstand by the next person (who might be yourself)
It's hard to say. The issue with reading/writing a struct like this is that it can end up being written as it is read, meaning at the reader side you get a mix of both old and new data in the struct. If that's OK (e.g. the struct is a sort of ring buffer) it can work, in this particular case, in theory. But do know you're relying on deep hardware properties there like the memory consistency model in use; I'm decently sure Xtensa is TSO so it can't reorder writes, but something like the RVMO as used by RiscV might be able to. In that case, your code will work on an ESP32, but breaks if you port it to a multicore RiscV processor. (With reordering writes, what I mean is that core 0 might do a write to A and then to B, but because of optimizations to make everything go faster, core 1 might see them as a write to B and then to A.)I made a new single task that reads messages from MessageBuffer and write the new incoming value to one member of the global struct, then in the same task read all the values. So now that there is only one task that deals with this global struct and I document that this global struct must not be accessed from another task, would I be safe?
Generally, I'd advise to simply use spinlocks (taskENTER_CRITICAL/taskEXIT_CRITICAL in ESP-IDF) around accesses to a struct like this. It makes it immediately clear what you're trying to do (so no guesswork or possibilities for bugs if you rework it) and given you use the spinlocks properly, it's always safe regardless of architecture. The alternative is a deep dive into memory consistency models, and believe me, you don't want to do that every time you try to fix a simple bug.
Re: Why do I need thread safety if Symmetric Multiprocessing protects memory
ok regardless of the implications. Just to improve my understanding. In the case I mentioned, where a single task only deals with the global variable, it is impossible for the same task to be writing and reading at the same time, regardless of the number of cores. Is my understanding correct? A task can be paused and resumed for some amount of ticks, but it is always doing one thing at a time. right?
So if my understanding is correct. and task code lines cannot be run in parallel, then why your answer was "It's hard to say."
Code: Select all
struct global{
int32_t m1=0;
int32_t m2=0;
} g;
void vTaskReadThenWrite(void* pvParameters ){
g.m1=1;
g.m2=m1+1; // This line will always run after the first line and never before it no matter how many cores or schedulers
}
-
- Posts: 9766
- Joined: Thu Nov 26, 2015 4:08 am
Re: Why do I need thread safety if Symmetric Multiprocessing protects memory
That will always work. All memory consistency models I know of always respect program order, that is: as long as you're doing the reads and writes within the same task/thread, the result as seen from that tasks point of view will always be what you expect it to be. Memory consistency models mainly concern themselves with what the result of execution of a code in a task/thread looks like from the point of view from *another* task/thread.
Suggest reading up on memory consistency models if you want to know more. It's a deep rabbit hole and you end up doubting anything you read from and write to memory for a while but it's interesting for sure. This seems a good primer, and if you want to go more into detail, you can look up the RiscV RVMO description. I don't think the Xtensa memory consistency model is publicly available, but you can safely assume it's TSO.
Suggest reading up on memory consistency models if you want to know more. It's a deep rabbit hole and you end up doubting anything you read from and write to memory for a while but it's interesting for sure. This seems a good primer, and if you want to go more into detail, you can look up the RiscV RVMO description. I don't think the Xtensa memory consistency model is publicly available, but you can safely assume it's TSO.
Who is online
Users browsing this forum: No registered users and 68 guests