www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Is GC.setAttr(p, GC.BlkAttr.NO_MOVE) guaranteed to work?

reply =?UTF-8?B?QWxpIMOHZWhyZWxp?= <acehreli yahoo.com> writes:
There is an example in GC.addRoot() documentation where the programmer 
is trying to mark a memory block as NO_MOVE:



     auto context = new Object;
     GC.addRoot(cast(void*)context);
     GC.setAttr(cast(void*)context, GC.BlkAttr.NO_MOVE);

If I understand it correctly, as soon as addRoot() succeeds, the block 
may have already been moved perhaps due to the needs of another thread.

Will setAttr() still work if that happened? Perhaps the GC is supposed 
to track any previously used memory block reference like 'context' so 
that the call will succeed? (I doubt it.)

If the example can indeed fail, will swapping the last two statements 
work as in the following code?

     auto context = new Object;
     GC.setAttr(cast(void*)context, GC.BlkAttr.NO_MOVE);
     GC.addRoot(cast(void*)context);

How about the reverse operations: Can I still use 'ctx' to refer to 
potentially moved memory block in the following code?

     GC.removeRoot(ctx);
     GC.clrAttr(ctx, GC.BlkAttr.NO_MOVE);

It seems to me that as a general rule, one cannot trust setting NO_MOVE 
on a GC-managed block at all. If that's true, addRoot() must have an 
overload that takes attributes as well and work atomically in that 
regard, right?

Ali
Jul 01 2014
parent Rainer Schuetze <r.sagitario gmx.de> writes:
On 02.07.2014 08:17, Ali Çehreli wrote:
 There is an example in GC.addRoot() documentation where the programmer
 is trying to mark a memory block as NO_MOVE:



      auto context = new Object;
      GC.addRoot(cast(void*)context);
      GC.setAttr(cast(void*)context, GC.BlkAttr.NO_MOVE);

 If I understand it correctly, as soon as addRoot() succeeds, the block
 may have already been moved perhaps due to the needs of another thread.
Yes, but the local variable "context" also has been changed to the new location by a moving GC (also any temporaries on the stack).
 Will setAttr() still work if that happened? Perhaps the GC is supposed
 to track any previously used memory block reference like 'context' so
 that the call will succeed? (I doubt it.)

 If the example can indeed fail, will swapping the last two statements
 work as in the following code?

      auto context = new Object;
      GC.setAttr(cast(void*)context, GC.BlkAttr.NO_MOVE);
      GC.addRoot(cast(void*)context);

 How about the reverse operations: Can I still use 'ctx' to refer to
 potentially moved memory block in the following code?

      GC.removeRoot(ctx);
      GC.clrAttr(ctx, GC.BlkAttr.NO_MOVE);

 It seems to me that as a general rule, one cannot trust setting NO_MOVE
 on a GC-managed block at all. If that's true, addRoot() must have an
 overload that takes attributes as well and work atomically in that
 regard, right?

 Ali
addRoot tells the GC that there are external references into GC managed memory. These references are not visible to the GC, so it cannot modify them when moving the referenced memory. As a consequence, addRoot implies NO_MOVE. I think the example is flawed. The issue raised does show a rule to follow, though: do not store a GC pointer to external memory before calling addRoot() or setAttr(NO_MOVE), it might get moved after the assignment, but before being fixed. BTW: The current GC has no support for moving, and the NO_MOVE flag isn't even stored anywhere (getAttr after setAttr will not report it being set).
Jul 01 2014