Why do I need thread safety if Symmetric Multiprocessing protects memory

kokonuts
Posts: 15
Joined: Tue Jul 11, 2017 6:39 pm

Why do I need thread safety if Symmetric Multiprocessing protects memory

Postby kokonuts » Sun Feb 05, 2023 3:56 pm

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?

ESP_Sprite
Posts: 9709
Joined: Thu Nov 26, 2015 4:08 am

Re: Why do I need thread safety if Symmetric Multiprocessing protects memory

Postby ESP_Sprite » Mon Feb 06, 2023 2:21 am

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.

kokonuts
Posts: 15
Joined: Tue Jul 11, 2017 6:39 pm

Re: Why do I need thread safety if Symmetric Multiprocessing protects memory

Postby kokonuts » Mon Feb 06, 2023 11:59 am

Thank you! It is clear now. And I have only 32-bit words with one task writing and the other reading.

ESP_Sprite
Posts: 9709
Joined: Thu Nov 26, 2015 4:08 am

Re: Why do I need thread safety if Symmetric Multiprocessing protects memory

Postby ESP_Sprite » Tue Feb 07, 2023 12:37 am

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.

kokonuts
Posts: 15
Joined: Tue Jul 11, 2017 6:39 pm

Re: Why do I need thread safety if Symmetric Multiprocessing protects memory

Postby kokonuts » Tue Feb 07, 2023 4:40 pm

Are you psychic? I already forgot today and made it a struct :oops: 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?

ESP_Sprite
Posts: 9709
Joined: Thu Nov 26, 2015 4:08 am

Re: Why do I need thread safety if Symmetric Multiprocessing protects memory

Postby ESP_Sprite » Wed Feb 08, 2023 12:29 am

kokonuts wrote:
Tue Feb 07, 2023 4:40 pm
Are you psychic? I already forgot today and made it a struct :oops: then I came here to read your previous answer, just to find the new
Nah, I simply have lots of experience... you could say that by now I'm an expert at creating bugs in code :P 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)
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?
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.)

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.

kokonuts
Posts: 15
Joined: Tue Jul 11, 2017 6:39 pm

Re: Why do I need thread safety if Symmetric Multiprocessing protects memory

Postby kokonuts » Wed Feb 08, 2023 1:15 pm

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?

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 

}
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." :?

ESP_Sprite
Posts: 9709
Joined: Thu Nov 26, 2015 4:08 am

Re: Why do I need thread safety if Symmetric Multiprocessing protects memory

Postby ESP_Sprite » Thu Feb 09, 2023 12:56 am

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 :P 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 239 guests