#Using MemoryPool with deinit()

1 messages · Page 1 of 1 (latest)

last goblet
#

If I use std.heap.MemoryPool for an object like

struct Foo {
    stuff: std.ArrayListUnmanaged(usize)
}

and i pool it like this:

pool = try std.heap.MemoryPool(Foo).initPreheated(allocator, 1000);

and then later i pool.create() and pool.destroy() some foo's, and then put stuff in them like foo.stuff.ensureTotalCapacity(allocator, 1000), how do I in my deinit() iterate across the memory pool freeing memory properly:

fn deinit(self: *Self) void {
    for (self.pool...) |foo| {
        foo.stuff.deinit(allocator);
    }
}

what does that iterator code block like?

slate wharf
#

its not very pretty

last goblet
#

Is it at least possible?

turbid zephyr
#

I think you get more out of memory pool if you don't store ArrayListUnmanged, but rather bounded array or similar

#

the Foo is already allocated

last goblet
#

I’m trying to pool things that can grow

slate wharf
#

actually i don't think its possible

turbid zephyr
#

I don't think it's possible to iterate live nodes

#

only freed ones

slate wharf
#

MemoryPool only stores a linked list of free objects

turbid zephyr
#

you'd have to use separate linked list

last goblet
#

I’m fine to just iterate the free objects and deinit them

#

The live ones I track separately

slate wharf
turbid zephyr
#

you can iterate free_list for free items

slate wharf
#

you... should not have to deinit the free objects

#

in fact that is almost certainly a bug

last goblet
#

Oh never mind I meant the live ones

#

Sorry, got it backwards

slate wharf
#

okay so do you track the live ones separately or not

last goblet
#

I pool.create() and use items, and pool.destroy() them to be used again, but I don’t want to deinit any of the allocated stuff until some end of computation

#

I guess this is not what MemoryPool is for

#

I can just make my own pool

#

I see that destroy() overwrites it

slate wharf
#

can you show us your code?

last goblet
#

sure

#

something like this:

const std = @import("std");

const Foo = struct {
    id: usize,
    stuff: std.ArrayListUnmanaged(usize),

    pub fn init(id: usize) Foo {
        return .{
            .id = id,
            .stuff = std.ArrayListUnmanaged(usize){},
        };
    }

    pub fn deinit(self: *Foo, allocator: std.mem.Allocator) void {
        self.stuff.deinit(allocator);
    }
};

const Bar = struct {
    allocator: std.mem.Allocator,
    pool: std.heap.MemoryPool(Foo),
    foos: std.ArrayList(*Foo),

    pub fn init(allocator: std.mem.Allocator) !Bar {
        return .{
            .allocator = allocator,
            .pool = try std.heap.MemoryPool(Foo).initPreheated(allocator, 1000),
            .foos = std.ArrayList(*Foo).init(allocator),
        };
    }

    pub fn deinit(self: *Bar) void {
        self.pool.deinit();
        self.foos.deinit();
    }

    pub fn run(self: *Bar) !void {
        for (0..2000) |i| {
            if (i % 15 == 0) {
                const foo = try self.pool.create();
                foo.* = Foo.init(i);
                try foo.stuff.ensureTotalCapacity(self.allocator, 100);
                try self.foos.append(foo);
                continue;
            }

            if (self.foos.items.len > 0) {
                const foo = self.foos.items[0];
                if (i % 5 == 0) {
                    _ = self.foos.swapRemove(0);
                    self.pool.destroy(foo);
                } else {
                    try foo.stuff.ensureUnusedCapacity(self.allocator, 1);
                    foo.stuff.appendAssumeCapacity(i);
                }
            }
        }
    }
};

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    var bar = try Bar.init(allocator);
    defer bar.deinit();
    try bar.run();
}
#

in run() it uses the pool, and appends into it, but when i destroy it back to the pool, i didn't want to set it to undefined, so i think MemoryPool just isn't what i want. I want the thing to be reusable pooled with the pointer to the thing that grows

#

obviously leaks memory as-is

turbid zephyr
#

The memory pool preheats arena for you and it uses the node for both storing your memory and the free list. You'll need additional linked list to track live objects.
The memory pool is good when you have N size pool where you want to quickly create and destroy fixed size nodes.
The nodes containing also dynamic memory slightly defeats the point, but it's still possible to do it with the additional linked list.

last goblet
#

That makes sense, I misunderstood how MemoryPool worked. I thought the memory was invalidated on pool.deinit(), not on pool.destroy(obj), so i thought the thing would still be a valid pointer

turbid zephyr
#

It's valid, but the contents will be a list node rather than your T

last goblet
#

Right