Very limited shared promotion

Ola Fosheim Grøstad ola.fosheim.grostad at gmail.com
Wed Jun 19 09:56:18 UTC 2019


On Wednesday, 19 June 2019 at 08:45:10 UTC, Jonathan M Davis 
wrote:
> scope doesn't care where the object is stored. If something is 
> scope, then you can't take any references to it in @safe code. 
> The whole point is to ensure that no references escape. Whether 
> the objects are GC-allocated are not is irrelevant.

So, the same as this:
https://dlang.org/spec/function.html#return-scope-parameters

Does this mean then that when you obtain write access to an 
object that "write access reference" will be scope qualified?

And then you cannot use that reference with any function that 
does not have scope qualified parameters?

So basically all library code has to add "scope" to all its 
parameters whether it is written with shared in mind or not.

So, "scope" becomes like "pure". Something that ought to be the 
default, but has to be added manually to all function prototypes 
in order to make safe multi-threaded programming less annoying.

Or will the compiler automatically deduce that a function 
parameter is fulfilling the "scope" requirements even when it has 
not been specified?

I guess it should, otherwise you'll end up with the "const" 
transition in C++, where you had to do ugly cast-hacks when 
calling functions that did treat parameters as const but the 
function signature had not specified it.

> implicitly cast to or from shared. As it is, in general, D 
> can't even know if there are multiple references to the same 
> object. Without that kind of information, it's not even 
> possible to know whether it's safe to pass an object from one 
> thread to another.

You can do this with dataflow in many cases, maybe in most useful 
cases. Although something like unique_ptr in C++ helps (by 
convention).

> And without some understanding of which concurrency primitives 
> are being used to protect an object or group of objects, it 
> can't do something as simple as know that locking a mutex has 
> actually protected those objects such that they can safely be 
> manipulated from the current thread.

Right. But you could do something dynamic with reference counting 
and "framework conventions".

e.g..:

auto dblock = database.allocate_lock();
auto obj_ref_lockcounting = dblock.require_write_access(obj_id);

Then maybe you just need some metaprogramming mechanisms to 
inform the type system what the framework 
"reference_counting_lock_object" does, so that the type system 
knows what objects have been locked and that when dblock goes out 
of scope then all locks have been released.

Seems plausible, but I haven't given it much thought.

> be necessary to make that happen, because it would require 
> significant changes to how D works.

Maybe, although I wonder how much you could do as library code. 
So I don't know.

If metaprogramming is going to be the main focus of D then it 
makes sense to focus on allowing library authors to inform the 
type system of what their library types are capable of. Basically 
the opposite of a traditional type system where the typesystem 
checks constraints.

So, the basic idea would be that the library tells the type 
system "my code provide these guarantees", rather than the the 
library asking the type system to verify that a set of guarantees 
hold.

> casting. _Maybe_, there will be a few, small places like with 
> what Manu is suggesting here where we will be able to leverage 
> what the compiler knows to implicitly convert something, but in 
> general, that's really not going to work.

I think that make such special cases is a bit dangerous. It is 
really going down the path of C++ that leads to a situation where 
you only appeal to the same audience as C++.

It also leaves the field open to Rust. I agree that borrowing 
might be tedious for memory safety, but might be generally 
suitable for making safer multi-threading available to more 
programmers. At least on the surface level, but I haven't spent a 
lot of time thinking about the limitations of borrow-checking in 
relation to multi-threading. So, it is just a hunch.

I think it would be a mistake to outright dismiss tracking 
references.  There might be libraries available that can do it 
for you even, e.g. provide a compiler switch that enables the 
compiler to generate a graph that is passed onto an external 
library that does the checking.

> I'd be very surprised if anything like that were considered 
> acceptable for D. When stuff like Rust's borrowing has been 
> brought up before, Walter and Andrei have made it pretty clear 
> that we're not going to do something like that with D's type 
> system.

Ok, but there are two issues here:

1. What you can query the type system for.

This has to be available at all times and has to be tightly 
integrated into the type system. There is a high cost to adding 
such features.

2. Type annotations that enable verification.

This does not have to be tightly integrated into the type system. 
You can provide it as a separate module with clear boundaries. 
And you might even be able to turn it off for faster 
recompilation.
There is a low cost to adding such features.


> Even getting something like DIP 1000 has been a major ordeal, 
> and I don't think that it would have ever happened unless 
> Walter had been convinced that it was absolutely needed for 
> @safe code to be able to do stuff like reference counting.

DIP1000 looks too limiting, but I don't know what it has enabled 
and where it falls short in practice.

I guess strength and weaknesses will show up as people build new 
frameworks around it.

> shared exists so that almost everything in D can be treated as 
> thread-local, and the type system prevents you from converting 
> between thread-local and shared without casting. Even immutable 
> ends up as part of it, because it's implicitly shared. The 
> compiler knows which objects are thread-local and which are 
> shared and can use that information in code generation.

Ok, so the basic idea is to allow D to shuffle instructions/code 
more around because the compiler can assume that nothing held in 
registers is ever stale.

And "shared" was just added to push "everything multi-threaded" 
into the future. So, now you have to figure out what that means 
in order to make it more useful to people who do more advanced 
programming in D (like Manu).

> force the matter. @system/@trusted then gets involved in the 
> sense that the cast is @system, but beyond that, @safety 
> doesn't have anything to do with it. I don't see how any of 
> that could be done via a library rather than the type system.

Well, it is currently just a question. And in my view the answer 
is not known.

I think there might be options to do it if we change your view on 
what a type system is from:

1. The usage of the type system specifies what the compiler 
should verify.

to

2. Vetted libraries can inform the type system of what it is 
capable of.


Ola.


More information about the Digitalmars-d mailing list