On Friday, 2 April 2021 at 12:47:35 UTC, z wrote:
> Even if the function is changed to only accept shared
parameters, .reserve
does not appear to support shared
so the function is impossible to use without somehow changing its type or using __gshared
.
There is no way that .reserve
can correctly support shared
, because it cannot know how to correctly synchronize the slice and its contents in the larger context of the whole program.
In general, just casting away shared
to make the compiler stop complaining completely defeats the purpose of shared
, and makes it just as unsafe as __gshared
.
Instead, you must do any necessary synchronization yourself using, for example, core.sync.mutex
. Only within properly synchronized regions should you cast away shared
. This is the purpose of shared
, and the only difference from __gshared
: to remind you to synchronize by requiring the use of a cast to do much of anything.
Typically, you should synchronize an entire region of code across which the shared
data under protection will begin and end with all invariants satisfied. Synchronizing individual operations (like just .reserve
by itself) is usually wrong - or rather, insufficient.
The synchronization must guarantee that whenever a thread is writing to the data, it has exclusive access. But, it is safe and fast to have multiple threads read data simultaneously, as long as it is not written to during that time. core.sync.rwmutex
can be used to implement this optimization.
(It is possible to design algorithms that support multiple simultaneous writers using lock-free algorithms (see core.atomic
) or other more complicated schemes, but this is much harder to do correctly and usually not necessary.)
Finally, if you only need to write the data once, after which it will only be read, then you can skip all of this confusion and complexity by just preparing the data from a single thread in a non-shared
container and then using cast(immutable)
(if there is only one extant reference to the data) or .idup
(otherwise). No synchronization is necessary for immutable
data.
> *(cast(Unqual!TT*)&a)
, not ideal
That is the correct way to perform an lvalue reinterpret cast (provided that the resulting type is actually compatible with the source type).
> (direct casting seems to create an rvalue because compilation fails.)
Yes, direct casting should result in an rvalue. (Although, I think it is possible to subvert this with a custom opCast
.)