Member variables in method are null when called as delegate from thread
tsbockman
thomas.bockman at gmail.com
Tue Jan 12 22:00:51 UTC 2021
On Tuesday, 12 January 2021 at 14:00:11 UTC, Steven Schveighoffer
wrote:
> On 1/11/21 8:49 PM, tsbockman wrote:
>> However, this re-ordering IS permitted to freely alter the
>> behavior of your code from the perspective of OTHER threads. A
>> likely cause of your bug is that the write to db by the
>> constructor's thread is being committed to memory after the
>> read of db by the MessageService thread.
>
> I don't think this is valid.
You might be right, but your analysis below assumes the answers
to a number of questions which aren't answered in the source code
provided by the OP. Perhaps you are familiar with the
implementations of the APIs in question, but I'm not and thought
it unwise to assume too much, given that the whole reason we're
having this discussion is that the code doesn't actually work...
Regardless, the simple way to find out if I'm on the right track
or not is just to protect access to Foo's fields with a mutex and
see if that fixes the problem. If it does, then either it's a
memory ordering issue like I suggested (or a code gen bug), and
the mutex can be replaced with something more efficient if
necessary.
> 1. the compiler MUST NOT reorder the storage of db to after you
> pass a delegate into an opaque function (array allocation).
Is the function actually opaque, though? If the source code is
available to the compiler for inlining (or maybe if it's marked
`pure`?) then reordering is still allowed.
> 2. The CPU is not going to reorder, because the memory
> allocation is going to take a global lock anyway (mutex locks
> should ensure memory consistency).
This is not a safe assumption. It is quite easy to design a
thread-safe allocator that does not take a global lock for every
allocation, and indeed *necessary* if you want it to scale well
to heavy loads on high core count systems.
Even if that's how it works today, I wouldn't write code that
depends on this behavior, unless the language standard formally
guaranteed it, because someone will change it sooner or later as
core counts continue to climb.
> I can't ever imagine creating a thread (which is likely what
> MessageService ctor is doing) to not have a consistent memory
> with the creating thread on construction.
It seems reasonable to assume that thread creation includes a
write barrier somewhere, but what if MessageService is using an
existing thread pool?
> The CPU would have to go out of its way to make it inconsistent.
No, there are many levels of caching involved in the system, most
of which are not shared by all cores. The CPU has to go out of
its way to make memory appear consistent between cores, and this
is expensive enough that it doesn't do so by default. That's why
atomics and memory barriers exist, to tell the CPU to go out of
its way to make things consistent.
You often don't have to deal with these issues directly when
using higher-level multi-threading APIs, but that's because they
try to include the appropriate atomics/barriers internally, not
because the CPU has to "go out of its way" to make things
inconsistent.
More information about the Digitalmars-d-learn
mailing list