Memory safe in D

Jonathan M Davis newsgroup.d at jmdavisprog.com
Wed Mar 13 21:57:17 UTC 2024


On Wednesday, March 13, 2024 1:43:23 AM MDT Alex via Digitalmars-d wrote:
> On Wednesday, 13 March 2024 at 06:05:35 UTC, Walter Bright wrote:
> > Memory safety is not something we've uniquely defined. It's
> > generally accepted that it specifically means no memory
> > corruption. "Safe" programs can offer additional guarantees,
> > like no race conditions.
>
> Yeah, race condition is the second headache after memory safety
> (include null pointer issues) :)
> I know only one language which give guarantee at compilation time
> about absence of race condition (but not deadlock). It is Rust.
> But D have `shared` keyword and as I understand it can provide
> such guarantee at compilation time for SafeD, right?

What shared is supposed to do is give an error if you attempt do anything
with a shared variable that isn't guaranteed to be thread-safe - which
basically means that it should give an error when you actually try to do
much of anything with a shared variable. However, it's not fully implemented
by default right now (it will currently give an error in some cases but not
all). The -preview=nosharedaccess switch can be used to make accessing
shared variables an error in general (like it's supposed to be), but it
hasn't been enabled by default yet.

Ideally, the compiler would know when it was thread-safe to access a shared
variable and implicitly remove shared within that code so that you could
safely access the variable, but in practice, the compiler has no way of
knowing that your code has done what's necessary to protect access to that
variable, since that involves doing stuff like locking a mutex whenever that
variable is accessed, and the language has no understanding of any of that
(and it's not at all easy to give the language such an understanding except
for in very simple cases).

So, what happens in practice is that the programmer has to lock the
appropriate mutex, then temporarily cast away shared to operate on the
variable, then make sure that no thread-safe references to the data exist
any longer prior to releasing the mutex. So, you get code like

synchronized(mutex)
{
    int* local = cast()&sharedVar;
    *local = 42;
}

The result is that the code is @system, not @safe, and the programmer has to
verify its correctness and mark it with @trusted for it to be useable by
@safe code.

However, higher level objects can be written such that they have shared,
@safe/@trusted member functions, and those member functions then take care
of all of the locking and casting internally so that you can just use the
type without directly dealing with the locking or casting.

But ultimately what shared is doing is segregating the code that deals with
concurrency and making it so that you have to cast to actually do much of
anything with it so that you can't shoot yourself in the foot with shared
elsewhere. You have to outright tell the compiler that you want to take the
risk. You then only have to examine certain sections of the code to make
sure that the code that's actually interacting with shared data does so
correctly (whereas in a language like C++, the type system doesn't help you
with any of that).

So the way that you write thread-safe code in D is pretty similar to what
you'd do in a language like C++ or Java, but shared makes it so that you
know which data is shared and so that you can't accidentally access shared
data in a manner which isn't thread-safe, whereas the type system really
doesn't help you with any of that in C++ or Java.

- Jonathan M Davis





More information about the Digitalmars-d mailing list