September 08, 2008
On Mon, Sep 8, 2008 at 8:33 AM, bearophile <bearophileHUGS@lycos.com> wrote:
> Jarrett Billingsley:
>> There's no way to do it with any heap-allocated value, which does seem a bit like a hole to me.
>
> I think there's both a syntax problem (what syntax to use?), and the fact that when possible D wants to avoid programming mistakes, so it wants to initialize memory to a clean state (this is even more important for the GC).
>
> A first possible syntax?
> auto a = new int[10] = void;
>
> That syntax is also usable to replace this:
> auto a = new int[10];
> a[] = 5;
>
> With:
> auto a = new int[10] = 5;

My first thought was something more like

  auto a = new int(5)[10];

to init to 5, or

  auto a = new int(void)[10];

to not init.


> But maybe this isn't important enough.

Yeh, maybe not this one thing.  But enough grains of sand like this and you have a sizable obstacle.  And D has a fair number of such grains.

--bb
September 08, 2008
On Sun, Sep 7, 2008 at 9:23 PM, bearophile <bearophileHUGS@lycos.com> wrote:

> That may be not enough to design a bit more complex data strutures, I don't know, we'll see.

How complex are you trying to get?  How horribly are you planning on subverting the GC and for what purpose?
September 08, 2008
Bill Baxter wrote:

>   auto a = new int(void)[10];

Does this imply the decision, that on upsizing no change of the init
value is allowed?
Otherwise how to change the init value?

-manfred
-- 
If life is going to exist in this Universe, then the one thing it cannot afford to have is a sense of proportion. (Douglas Adams)

September 08, 2008
On Mon, Sep 8, 2008 at 2:12 PM, Manfred_Nowak <svv1999@hotmail.com> wrote:
> Bill Baxter wrote:
>
>>   auto a = new int(void)[10];
>
> Does this imply the decision, that on upsizing no change of the init
> value is allowed?
> Otherwise how to change the init value?

Well, you really don't want to have to carry around an init value with every array (or slice of one).  So I think it would have to be the case that the initializer only applied to the original creation.

Maybe that means the OP would still want to roll his own non-intializing array.



--bb
September 08, 2008
Bill Baxter <wbaxter@gmail.com> wrote:
> On Mon, Sep 8, 2008 at 8:33 AM, bearophile <bearophileHUGS@lycos.com> wrote:
> > Jarrett Billingsley:
> >> There's no way to do it with any heap-allocated value, which does seem a bit like a hole to me.
> >
> > I think there's both a syntax problem (what syntax to use?), and the fact that when possible D wants to avoid programming mistakes, so it wants to initialize memory to a clean state (this is even more important for the GC).
> 
> > But maybe this isn't important enough.
> 
> Yeh, maybe not this one thing.  But enough grains of sand like this and you have a sizable obstacle.  And D has a fair number of such grains.

A non-initialized array must never be scanned for pointers, even if the underlying type contains them.  This is a very dangerous feature which must not be available in SafeD.  You can always use

  T[] allocArrayNoInit(T)(size_t count) {
    void[] mem = std.gc.malloc(count * T.sizeof);
    std.gc.hasNoPointers(mem.ptr);
    return cast(T[])mem;
  }
September 08, 2008
Jarrett Billingsley <jarrett.billingsley@gmail.com> wrote:
> On Sun, Sep 7, 2008 at 12:59 PM, bearophile <bearophileHUGS@lycos.com> wrote:
> > Uhm... I don't understand fully: do you mean roots don't become deleted
> > when they have nothing that points to them?
> > The docs say:
> > "Roots are references to memory allocated by the collector that are
> > maintained in memory outside the collector pool."
> 
> I think what you should be using instead is addRange.  addRoot more or less tells the GC "never collect memory referenced by this pointer", but it doesn't actually scan the memory _pointed to_ by the roots for pointers.

This just cannot be.  A root is a place where a tree of live memory objects is grafted.  That's why it is called a "root".  A memory block pointed to by a root is kept alive and, obviously, scanned for pointers to other memory blocks if it hasPointers().

There's a problem with addRoot() that, if the memory block is re- allocated, the old root stops serving its purpose.  In that case you need to remove an old root and add the new one, pointing into the re- allocated block.
September 08, 2008
Sergey Gromov wrote:

> You can always use
>   return cast(T[])mem;

I doubt this, because that `cast' might not be valid for all types `T'.

-manfred

-- 
If life is going to exist in this Universe, then the one thing it cannot afford to have is a sense of proportion. (Douglas Adams)

September 08, 2008
Jarrett Billingsley Wrote:
> How complex are you trying to get?

Well, I may want to try to implement finger trees, ecc.


>How horribly are you planning on subverting the GC and for what purpose?<

I'm planning in making the GC become evil. The purpose is total world domination, of course.

Be scared,
bearophile
September 08, 2008
bearophile <bearophileHUGS@lycos.com> wrote:
> So I have tried this, I have written this assuming that addRoot() adds
> a single pointer to the GC collected pool, but this last solution
> doesn't work, I don't know why:
> auto new_block = cast(Block*)mem_alloc(Block.sizeof);
> new_block.data = cast(int*)mem_alloc(nints * int.sizeof);
> new_block.len = nints;
> new_block.next = null;
> addRoot(new_block.next);
> addRoot(new_block.data);

What you do here is add two roots to GC: null and a pointer to your ints.  While the latter is relatively valid, the former is obviously wrong: you tell GC to never collect a memory block which includes an address of 0.  What happens next is a) whatever you assign to block.next is not kept alive by block since the .next field itself is not checked for pointers;  and b) if you ever change .data the old block will live forever while the new one will not be referenced for the same reason as a).

Here are the possible solutions.  Note that they are independent, you need to implement only one of them to make your code work.

1.  Replace Block.next with

    private Block* _next;
    Block* next() {return _next;}
    Block* next(Block* other) {
        removeRoot(_next);
        _next = other;
        addRoot(_next);
        return _next;
    }

2.  Replace

    addRoot(new_block.next);
    addRoot(new_block.data);

with

    addRange(&new_block.next, &new_block.data+1);

which will make GC scan Block.next and Block.data for pointers on every collection cycle, therefore automatically picking any changes to these pointers.

Both changes make your code work.  Both require Block.~this() to remove any unneeded roots or ranges, since they are permanent by their nature and will keep any pointed memory allocated until the process terminates.
September 08, 2008
Manfred_Nowak <svv1999@hotmail.com> wrote:
> Sergey Gromov wrote:
> 
> > You can always use
> >   return cast(T[])mem;
> 
> I doubt this, because that `cast' might not be valid for all types `T'.

Could you explain your point in a bit more detail?  std.gc.malloc() is guaranteed to return a memory block with alignment requirements sufficient for any type.