Hacker News new | ask | show | jobs
by john_fushi 1031 days ago
Doesn't the singleton example contain an error?

On one hand, the author says that instanciating the singleton and storing it is not safe because there is no synchronization, but on the other hand he says that null check before is safe because the mutex introduces synchronization.

My understanding would be that the mutex protects this whole section and that the atomic is not necessary at all in this case.

Please correct me if I'm wrong.

2 comments

It looks correct to me.

Consider the code without the initial null check optimization. Everything occurs under the mutex in this case, so no atomics are needed -- it's fully serialized. (That is, all memory operations which occur within the mutex will be visible to any other thread which subsequently acquires the mutex.)

Now add in the initial null check. Because it occurs outside the mutex, it has no ordering established with anything that occurs under the mutex. Beside that p itself must be atomic to avoid a data race with itself (torn read), you need some way to ensure that App is visible if p was found to be set. Since the mutex isn't touched here, it doesn't provide any help. You need to establish that relationship using p itself. Hence the additional release-acquire sequence on p.

Btw a simple mental model for mutexes is that of the spin lock, acquired using an acquire-release operation (e.g. test-and-set), released using a release operation (e.g. write). There's no other "magic" that happens in a mutex to make them special memory-order wise. E.g. mutexes (nor memory ordering generally) do not "force" things out to memory or such.

This paper will help a lot: https://www.aristeia.com/Papers/DDJ_Jul_Aug_2004_revised.pdf

tl;dr: Remember that the "new" operator does two operations: allocates memory and fills in the memory with the data. Now, if you have two threads (A & B):

- A allocates memory and then gets pre-empted

- B will pass the null pointer check and attempt to use the non-filled in memory block

Thanks for the explanation!

That's the part I missed : the first null check can be skipped if the memory is allocated but the constructor hasn't been executed.