Fr0sT.Brutal 900 Posted November 25, 2019 Regarding the strings - assignment is performed in these steps: ( Initially we have string S1 that is pointer P1 pointing to memory M1 and assigning to it string S2 that is pointer P2 pointing to memory M2 ) 1) ref count of S2 is interlocked-incremented 2) P1 is saved to Ptmp 3) value of P1 is replaced with value of P2 4) ref count of string that Ptmp points to is interlocked-decremented 5) if new ref count is 0, M1 is deallocated All these steps are not performed as single atomic operation so could be interrupted by another thread. So, if another thread is reading, it could interfere in any place between and even inside these steps. For example: 0) Initially Thr1 has string field that Thr2 reads and holds the copy in its internal variable so the field has ref count 2 1) Thr1 assigns new value to its field, thus decrements ref-count, new ref-count is 1 2) Thr2 reads new value from Thr1's field thus decrements ref-count, new ref-count is 0 3) Thr1 checks if ref-count is 0 - true 4) Thr2 checks if ref-count is 0 - true 5) Thr1 deallocates memory 6) Thr2 deallocates memory - KABOOM! Share this post Link to post
Pawel Piotrowski 18 Posted November 25, 2019 4 hours ago, Fr0sT.Brutal said: It's a rare case for variables that are accessed from several threads to be independent. Usually they are fields of a structure/object/class so nothing could guarantee they're aligned without explicit measures. So you'll have to either ensure alignment (by using paddings, dummy fields, $A directives etc) or just accept that accessing variables of size more than 1 byte is probably not atomic. The situation is not that grim and hopeless 🙂 dummzeuch is correct. Delphi already performs aligning. Not only for global variables, but also for fields inside a record or a class. See my post above for measurements of the sample record. You can see, that all of those fields are aligned, Delphi already injects dummy bytes to ensure this. This is the default setting. So you only need to be aware of special cases, like packed records and such. Share this post Link to post
dummzeuch 1505 Posted November 25, 2019 4 hours ago, Fr0sT.Brutal said: It's a rare case for variables that are accessed from several threads to be independent. Usually they are fields of a structure/object/class so nothing could guarantee they're aligned without explicit measures. So you'll have to either ensure alignment (by using paddings, dummy fields, $A directives etc) or just accept that accessing variables of size more than 1 byte is probably not atomic. Actually, unless you explicitly declare them otherwise, structure members will be 32 bit aligned. In my example I declared the record as packed, to turn that off. Share this post Link to post
Fr0sT.Brutal 900 Posted November 25, 2019 31 minutes ago, Pawel Piotrowski said: Delphi already performs aligning. Not only for global variables, but also for fields inside a record or a class. See my post above for measurements of the sample record. You can see, that all of those fields are aligned, Delphi already injects dummy bytes to ensure this. Yes, and I see that w2 field is not aligned. And it is 2 bytes long so, AFAIU, potentially prone to non-atomic change. 33 minutes ago, dummzeuch said: Actually, unless you explicitly declare them otherwise, structure members will be 32 bit aligned. Well, Pawel's experiment shows that in some cases they won't. Moreover, alignment lesser than 32 could be forced by project options. If you ship a library unit that gets recompiled along with 3rd party's project, you'll have to explicitly specify alignment to avoid access collisions. Share this post Link to post
Pawel Piotrowski 18 Posted November 25, 2019 9 minutes ago, Fr0sT.Brutal said: Yes, and I see that w2 field is not aligned. And it is 2 bytes long so, AFAIU, potentially prone to non-atomic change. I might unintentionally confused you... sorry for that. The header in my test saying "aligned" should rather say "starts with memory divisible by 4". But that was too long for the header. I tried - and failed it seems 😉 - to clarified that at the bottom of that same post. even if a variable doesn't start right away aligned (so its memory allocation is not divisible by 4), but doesn't cross the 32bit boundary, it is guaranteed to be atomic. Basically, a word is atomic, no matter if the 2 bytes that have to be discarded are at the start of the memory or at the end of it. Share this post Link to post
Fr0sT.Brutal 900 Posted November 25, 2019 1 hour ago, Pawel Piotrowski said: even if a variable doesn't start right away aligned (so its memory allocation is not divisible by 4), but doesn't cross the 32bit boundary, it is guaranteed to be atomic. Basically, a word is atomic, no matter if the 2 bytes that have to be discarded are at the start of the memory or at the end of it. Don't bother, you explained it pretty clear but 2 bytes could easily cross 32bit boundary couldn't they? Share this post Link to post
FredS 138 Posted November 25, 2019 7 hours ago, Fr0sT.Brutal said: Usually they are fields of a structure/object/class so nothing could guarantee they're aligned Default Align setting does that already. With that default (A+) the only way to get fields that do not conform is by by packing a record. Share this post Link to post
Pawel Piotrowski 18 Posted November 25, 2019 3 hours ago, Fr0sT.Brutal said: Don't bother, you explained it pretty clear but 2 bytes could easily cross 32bit boundary couldn't they? Thanks 🙂 The answer to your question is yes and no 😉 No: not with the default Delphi settings and not in a regular record or class, no a word can not cross 32bit boundaries. The compiler ensures that for you by default. Yes: You can force it to cross the 32bit boundaries. Delphi gives you this option. Like with packed records. Or by changing the compiler settings, or by using the compiler directive... and maybe something else 😉 But that is usually a conscious choice. Share this post Link to post