Unexpected Leak

I am trying to write this program but a unexpected memory leak occurs.

const std = @import("std");
const Io = std.Io;

pub fn main(init: std.process.Init) !void {
    const gpa = init.gpa;

    var client = std.http.Client{
        .allocator = gpa,
        .io = init.io,
    };

    var redirect_buffer: [4096]u8 = undefined;

    var response_body = try std.ArrayList(u8).initCapacity(gpa, 4096);
    var response_writer = Io.Writer.fromArrayList(&response_body);

    defer response_body.deinit(gpa);

    defer client.deinit(); // <-  Here

    const response = try client.fetch(.{
        .location = .{
            .url = "https://jsonplaceholder.typicode.com/posts/1",
        },
        .method = .GET,
        .redirect_buffer = &redirect_buffer,
        .response_writer = &response_writer,
    });

    if (response.status == .ok) {
        std.debug.print("Stored Input:\n{s}\n", .{response_body.items});
    } else {
        std.debug.print("HTTP Error Status: {}\n", .{response.status});
    }
}

For some reason the leak only happens when I am trying to use the arraylist but the error occurs in client.deinit() .
From lldb I get TlsInitializationFailed if it is any help .
here is the error I get

error(DebugAllocator): memory address 0x7f9d2f061000 leaked:
/home/usr/Applications/zig-0.16.0/lib/std/array_list.zig:1235:56: 0x15b68f7 in ensureTotalCapacityPrecise (std.zig)
                const new_memory = try gpa.alignedAlloc(T, alignment, new_capacity);
                                                       ^
/home/usr/Applications/zig-0.16.0/lib/std/array_list.zig:606:48: 0x15b9907 in initCapacity (std.zig)
            try self.ensureTotalCapacityPrecise(gpa, num);
                                               ^
/home/usr/Documents/zig/no1/src/main.zig:15:59: 0x11fa368 in main (main.zig)
    var response_body = try std.ArrayList(u8).initCapacity(gpa, 4096);
                                                          ^
/home/rmashrafi/Applications/zig-0.16.0/lib/std/start.zig:737:30: 0x11fb29e in callMain (std.zig)
    return wrapMain(root.main(.{
                             ^
/home/usr/Applications/zig-0.16.0/lib/std/start.zig:190:5: 0x11fa0e1 in _start (std.zig)
    asm volatile (switch (native_arch) {
    ^

Defers should be written in order of initialization, and you should use Io.Writer.Allocating:

const std = @import("std");
const Io = std.Io;

pub fn main(init: std.process.Init) !void {
    const gpa = init.gpa;

    var client: std.http.Client = .{ .allocator = gpa, .io = init.io };
    defer client.deinit();

    var body: Io.Writer.Allocating = .init(gpa);
    defer body.deinit();

    var redirect_buffer: [4096]u8 = undefined;
    const response = try client.fetch(.{
        .location = .{
            .url = "https://jsonplaceholder.typicode.com/posts/1",
        },
        .method = .GET,
        .redirect_buffer = &redirect_buffer,
        .response_writer = &body.writer,
    });

    if (response.status == .ok) {
        std.debug.print("Stored Input:\n{s}\n", .{body.written()});
    } else {
        std.debug.print("HTTP Error Status: {}\n", .{response.status});
    }
}


2 Likes

I copied your code
and I did like this and we are getting the output

const std = @import("std");
const Io = std.Io;

pub fn main(init: std.process.Init) !void {
    const gpa = init.gpa;

    var client = std.http.Client{ .allocator = gpa, .io = init.io };
    defer client.deinit(); // <-  Here

    var redirect_buffer: [4096]u8 = undefined;
    var allocating_writer = std.Io.Writer.Allocating.init(gpa);
    defer allocating_writer.deinit();

    const response = client.fetch(.{
        .location = .{
            .url = "https://jsonplaceholder.typicode.com/posts/1",
        },
        .method = .GET,
        .redirect_buffer = &redirect_buffer,
        .response_writer = &allocating_writer.writer,
    }) catch |err| {
        std.debug.print("Error fetch response: {any}", .{err});
        return;
    };

    if (response.status == .ok) {
        var data = allocating_writer.toArrayList();
        defer data.deinit(gpa);

        std.debug.print("Stored Input:\n{s}\n", .{data.items});
    } else {
        std.debug.print("HTTP Error Status: {}\n", .{response.status});
    }
}

This is a common mistake; Writer.fromArrayList does not create a growable writer, rather it only writes to the unused capacity of the array lists buffer without growing.

The reason you got a leak is because it sets the array list to empty, it does not know about the allocated buffer anymore so it can’t free it on deinit.
Instead, you would have to free response_writer.buffer.

What you likely want instead is Writer.Allocating as @rosew0od suggested.


when you get an adequate response you can mark it as the solution with the checkbox button on the bottom of the response.

2 Likes