Thread overview
Shared, ref, arrays, and reserve template instantiation
September 12
When I add the "shared" attribute to an array, I am no longer able to call reserve because the template won't instantiate:

Error: template object.reserve cannot deduce function from argument types !()(shared(int[]), int), candidates are:
/dlang/dmd/linux/bin64/../../src/druntime/import/object.d(4091):  
      object.reserve(T)(ref T[] arr, size_t newcapacity)

1. Shared modifies the type, so the template does not match. Even casting does not seem to work however. Is there something about shared that makes it unable to be taken by reference?
2. Is there a workaround for me to be able to preallocate the array?

Kind regards
September 13
On Wednesday, 12 September 2018 at 23:41:16 UTC, James Blachly wrote:
> When I add the "shared" attribute to an array, I am no longer able to call reserve because the template won't instantiate:
>
> Error: template object.reserve cannot deduce function from argument types !()(shared(int[]), int), candidates are:
> /dlang/dmd/linux/bin64/../../src/druntime/import/object.d(4091):
>       object.reserve(T)(ref T[] arr, size_t newcapacity)
>
> 1. Shared modifies the type, so the template does not match. Even casting does not seem to work however. Is there something about shared that makes it unable to be taken by reference?
> 2. Is there a workaround for me to be able to preallocate the array?
>
> Kind regards

I'm guessing you tried something like:

    shared char[] x;
    // Doesn't work; it casts the result of x.reserve
    cast(char[])x.reserve(100);
    // Doesn't work; (cast(char[])x) is not an lvalue, so it can't be ref
    (cast(char[])x).reserve(100);

Arrays are passed and stored like pointers, and `reserve` modifies the array, which is why the thing needs to be ref.

Anyway, it works like this:

    // Cast and store in a variable so it can be ref
    auto b = cast(char[]) x;
    // Okay, reallocates b (changes b.ptr), doesn't change x
    b.reserve(100);
    // Copy changes back to the shared variable
    x = cast(shared) b;

September 12
On Wednesday, September 12, 2018 5:41:16 PM MDT James Blachly via Digitalmars-d-learn wrote:
> When I add the "shared" attribute to an array, I am no longer able to call reserve because the template won't instantiate:
>
> Error: template object.reserve cannot deduce function from
> argument types !()(shared(int[]), int), candidates are:
> /dlang/dmd/linux/bin64/../../src/druntime/import/object.d(4091):
>        object.reserve(T)(ref T[] arr, size_t newcapacity)
>
> 1. Shared modifies the type, so the template does not match. Even
> casting does not seem to work however. Is there something about
> shared that makes it unable to be taken by reference?
> 2. Is there a workaround for me to be able to preallocate the
> array?

You can't do much of anything with shared while it's shared, which is pretty much the whole point. The way that shared needs to be used in general is essentially

synchronized(mutexForSharedObj)
{
    auto local = cast(Type)sharedObj;
    // do stuff with local...

    // ensure that no thread-local references to local / sharedObj exist
    // before releasing the mutex
}

// shared object is now essentially unusable again

Doing pretty much _any_ operation on a shared object while it's shared (other than atomic operations from core.atomic) is wrong, because it's not thread-safe. The compiler prevents most operations but not as many as it should (e.g. copying is currently legal). That will likely be fixed in the future, but exactly what's going to happen to shared in all of the fine details hasn't been sorted out yet. The basics work, but not all of the details are as they should be yet.

So, if you're doing anything like calling reserve or ~= on a shared array, then you need to protect it with the mutex that you have for it and cast away shared first. However, if you're just dealing with constructing the array, then what you should do is create it as thread-local and then cast it to shared after you're done setting it up and are ready to share it across threads (after which, all further operations on it should be protected by a mutex or use atomics, otherwise they're not thread-safe).

- Jonathan M Davis



September 13
Great -- Thank you both.

I previously found Unqual, but it looks like that needs template support so wasn't feasible, hence my question.

Neia is right that I tried to cast as in the second case ( but without UFCS -- reserve( cast(int[]), N); ).  As an aside, what is going on behind the scenes with the compiler when casting away a property? I did not think cast operations copied data, so was surprised that a cast value is not an lvalue.

Regarding Jonathan's comments, we had definitely protected the ~= operations with Mutex, but realized we were doing lots of array appends in a hot loop, and since we have an idea of cardinality ahead of time just wanted to preallocate.  Since it is all initialization before concurrent code enters the picture, we'll do what you've suggested and set it up as TL and then cast to shared.

James
September 12
On Wednesday, September 12, 2018 9:42:19 PM MDT James Blachly via Digitalmars-d-learn wrote:
> Neia is right that I tried to cast as in the second case ( but
> without UFCS -- reserve( cast(int[]), N); ).  As an aside, what
> is going on behind the scenes with the compiler when casting away
> a property? I did not think cast operations copied data, so was
> surprised that a cast value is not an lvalue.

Well, you basically get a temporary variable when you cast an object, and those are rvalues. And while casting with regards to type qualifiers such as const or shared, you're not actually changing the data, plenty of other casts do - e.g. float and long don't even have the same size, but you can cast from one to the other. So, even in principle, only some casts could result in lvalues even if we wanted them to.

- Jonathan M Davis