www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How long does the context of a delegate exist?

reply frame <frame86 live.com> writes:
I'm using a buffer delegate to fill data from a DLL and use it as 
range like this:

Pseudo-Code:
```d
void DLLFun(out Collector collector) {
   auto something;

   collector = Collector(...);
   collector.registerFillBufferMethod({
      auto data = [];
      //...
      // use of something;
      return data;
   )};
}
```

This collector takes care of the data so the GC cannot collect it 
while not explicitly closed on the caller side.

But what about the data used in the context of the delegate? Does 
this data stay as long DllFun is not called again because the DLL 
stack is paused? Or is this data also volatile and may be 
collected by the GC anytime?
May 27
next sibling parent reply Alain De Vos <devosalain ymail.com> writes:
I think of a dynamic array as a value-type consisting of a 
pointer to the actual data and a length, which gets copied.
The data pointing to will continue to live.
The pointer and length might disappear from the stack or get 
copied.
I think there is no problem.
May 27
parent frame <frame86 live.com> writes:
On Thursday, 27 May 2021 at 16:17:12 UTC, Alain De Vos wrote:
 I think of a dynamic array as a value-type consisting of a 
 pointer to the actual data and a length, which gets copied.
 The data pointing to will continue to live.
 The pointer and length might disappear from the stack or get 
 copied.
 I think there is no problem.
Seems logical, I just do not know exactly what a delegate internally does with captured variables from the function body. But you are right it must be something like that.
May 27
prev sibling parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 27 May 2021 at 12:59:02 UTC, frame wrote:
 But what about the data used in the context of the delegate?
If the delegate is created by the GC and stored it will still be managed by the GC, along with its captured vars. As long as the GC can see the delegate in your example you should be OK. But if it is held on to by a C or OS lib, the GC might not see it and you'd have to addRoot or something to ensure it stays in.
May 27
next sibling parent reply frame <frame86 live.com> writes:
On Thursday, 27 May 2021 at 18:13:17 UTC, Adam D. Ruppe wrote:
 On Thursday, 27 May 2021 at 12:59:02 UTC, frame wrote:
 But what about the data used in the context of the delegate?
If the delegate is created by the GC and stored it will still be managed by the GC, along with its captured vars. As long as the GC can see the delegate in your example you should be OK. But if it is held on to by a C or OS lib, the GC might not see it and you'd have to addRoot or something to ensure it stays in.
Did you mean to add the delegate as GC root or the data? It would be nicer to just add the delegate but addRoot does not accept it and casting to void* is deprecated. Does it work if I supply the .ptr property instead? This "GC silently no-op on wrong data" is annoying.
May 27
parent reply Adam D. Ruppe <destructionator gmail.com> writes:
On Thursday, 27 May 2021 at 20:44:21 UTC, frame wrote:
 Did you mean to add the delegate as GC root or the data?
The delegate.ptr property.
May 27
parent reply cc <cc nevernet.com> writes:
On Thursday, 27 May 2021 at 20:46:22 UTC, Adam D. Ruppe wrote:
 On Thursday, 27 May 2021 at 20:44:21 UTC, frame wrote:
 Did you mean to add the delegate as GC root or the data?
The delegate.ptr property.
Is there any way to enforce at compile time that we're not accidentally allocating when creating a delegate, other than being carefully aware of what variables are referenced inside the body? Something like: ```d auto dg = delegate {...} assert(dg.ptr is null, "Oops, we unintentionally allocated on GC here, check delegate body!"); // can't static assert ```
May 30
next sibling parent Paul Backus <snarwin gmail.com> writes:
On Sunday, 30 May 2021 at 09:39:28 UTC, cc wrote:
 Is there any way to enforce at compile time that we're not 
 accidentally allocating when creating a delegate, other than 
 being carefully aware of what variables are referenced inside 
 the body?  Something like:
 ```d
 auto dg = delegate {...}
 assert(dg.ptr is null, "Oops, we unintentionally allocated on 
 GC here, check delegate body!"); // can't static assert
 ```
` nogc`. Or check the output of `dmd -vgc` if you want something less strict.
May 30
prev sibling parent Adam D. Ruppe <destructionator gmail.com> writes:
On Sunday, 30 May 2021 at 09:39:28 UTC, cc wrote:
 Is there any way to enforce at compile time that we're not 
 accidentally allocating when creating a delegate, other than 
 being carefully aware of what variables are referenced inside 
 the body?
Use `function` instead of `delegate`. Then it doesn't have a context pointer at all.
May 30
prev sibling parent reply IGotD- <nise nise.com> writes:
On Thursday, 27 May 2021 at 18:13:17 UTC, Adam D. Ruppe wrote:
 If the delegate is created by the GC and stored it will still 
 be managed by the GC, along with its captured vars.

 As long as the GC can see the delegate in your example you 
 should be OK. But if it is held on to by a C or OS lib, the GC 
 might not see it and you'd have to addRoot or something to 
 ensure it stays in.
With lambdas in C++ you can cherrypick captures the way you want, then somehow this can be converted to an std::function and this is where it goes on the heap as well. In D, how does the compiler know what captures it is supposed to store, or does it take the whole lot?
May 27
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/27/21 4:46 PM, IGotD- wrote:
 On Thursday, 27 May 2021 at 18:13:17 UTC, Adam D. Ruppe wrote:
 If the delegate is created by the GC and stored it will still be 
 managed by the GC, along with its captured vars.

 As long as the GC can see the delegate in your example you should be 
 OK. But if it is held on to by a C or OS lib, the GC might not see it 
 and you'd have to addRoot or something to ensure it stays in.
With lambdas in C++ you can cherrypick captures the way you want, then somehow this can be converted to an std::function and this is where it goes on the heap as well. In D, how does the compiler know what captures it is supposed to store, or does it take the whole lot?
For closures, it uses semantic analysis to see which variables are used in the lambda, and then allocates a block at the start of the function to hold those variables. -Steve
May 27