A few thoughts on std.allocator
Jakob Ovrum via Digitalmars-d
digitalmars-d at puremagic.com
Wed May 13 17:00:39 PDT 2015
On Wednesday, 13 May 2015 at 17:49:38 UTC, Steven Schveighoffer
wrote:
> OK, then consider that this:
>
> void main()
> {
> string x;
> x ~= "hello";
> x ~= " world";
> }
>
> would require locking. That's unacceptable. Nobody would append
> with strings if this all required locking for no reason. The
> runtime currently does NOT lock for this case, it considers
> immutable and const to be thread-local.
Well, it's necessary because the design of druntime arrays is
incompatible with D2's type system. Without locking,
multi-threaded applications that use dynamic array operations
could easily contain some particularly hard to track concurrency
bugs.
Simply not doing the locking and hoping that everything is fine
doesn't sound like a good plan (which I think we agree on, since
shared(immutable(T)) would solve it).
> No, I think the answer is simpler. Introduce shared(immutable),
> and then we can distinguish between immutable data that is
> shared and data that is not shared. It also makes implementing
> local heaps easier. Shared really is orthogonal to mutability.
Basically, shared(immutable(T)) would only be useful to
allocators, including arrays because they may need to allocate
when growing. I don't think it would be useful beyond that; the
sharedness of immutable data is probably not interesting to any
other kind of code.
It would make immutable considerably harder to use than it is
today. shared(immutable(T)) would be implicitly convertible to
shared(const(T)), but not const(T), which precludes the vast
majority of mutation-agnostic D code out there today (I have
never seen shared(const(T)) used in the wild). We would no longer
be able to do even the simplest things, like passing a path
string to another thread and use std.file.read on it.
> Here's something even weirder: If you append to y
> (const(int)[]) as a reference to x (immutable(int)[]), then
> part of the array is immutable and shareable, and the part that
> was appended is const and not shareable.
This should be the case for user-defined containers as well as
long as the element type doesn't have mutable indirection.
Appending on slices seems to handle this correctly as well,
rejecting attempts to append a const(T)[] to an immutable(T)[]
when T has mutable indirection.
> This should work:
>
> void main()
> {
> auto s = new S;
> passToOtherThread(&s.b);
> }
>
> Which makes s.a unshared, and s.b shared. But the memory block
> needs to be shareable.
That is an interesting example. Plain shared has the same problem:
struct S {
int a;
shared int b;
}
void main() {
import std.concurrency;
auto tid = spawn(() {});
auto s = new S;
tid.send(&s.b);
}
Types that contain shared anywhere within the type would have to
be allocated from a global allocator, and with the current state
of immutable, the same would have to be done for types that
contain immutable anywhere within it. Note that it's only the
case for head-shared/head-immutable; types like immutable(T)[] or
shared(T)* don't count.
More information about the Digitalmars-d
mailing list