Casting lvalues

tsbockman thomas.bockman at gmail.com
Fri Apr 2 20:23:42 UTC 2021


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`.)


More information about the Digitalmars-d-learn mailing list