Jump to content
ertank

Changing label text in thread leads to weird display

Recommended Posts

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
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
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
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
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
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
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
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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×