Compare commits
No commits in common. "331d86224682b8881a4ec23ca002a418a26e2841" and "306e085d1bca05f4572b341c71f873669fc69d9c" have entirely different histories.
331d862246
...
306e085d1b
@ -7,7 +7,6 @@ pub const AllocationError = error {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub const AllocationOptions = struct {
|
pub const AllocationOptions = struct {
|
||||||
return_address: usize,
|
|
||||||
allocation: ?[]u8 = null,
|
allocation: ?[]u8 = null,
|
||||||
size: usize,
|
size: usize,
|
||||||
};
|
};
|
||||||
@ -182,10 +181,9 @@ pub fn allocate_many(allocator: Allocator, amount: usize, comptime Type: type) A
|
|||||||
@compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type));
|
@compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type));
|
||||||
}
|
}
|
||||||
|
|
||||||
return @ptrCast([*]Type, @alignCast(@alignOf(Type), allocator.invoke(.{
|
return @ptrCast([*]Type, @alignCast(@alignOf(Type), allocator.invoke(.{.size = @sizeOf(Type) * amount}) orelse {
|
||||||
.size = @sizeOf(Type) * amount,
|
return error.OutOfMemory;
|
||||||
.return_address = @returnAddress(),
|
}))[0 .. amount];
|
||||||
}) orelse return error.OutOfMemory))[0 .. amount];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn allocate_one(allocator: Allocator, value: anytype) AllocationError!*@TypeOf(value) {
|
pub fn allocate_one(allocator: Allocator, value: anytype) AllocationError!*@TypeOf(value) {
|
||||||
@ -195,10 +193,9 @@ pub fn allocate_one(allocator: Allocator, value: anytype) AllocationError!*@Type
|
|||||||
@compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type));
|
@compileError("Cannot allocate memory for 0-byte type " ++ @typeName(Type));
|
||||||
}
|
}
|
||||||
|
|
||||||
const allocation = @ptrCast(*Type, @alignCast(@alignOf(Type), allocator.invoke(.{
|
const allocation = @ptrCast(*Type, @alignCast(@alignOf(Type), allocator.invoke(.{.size = @sizeOf(Type)}) orelse {
|
||||||
.size = @sizeOf(Type),
|
return error.OutOfMemory;
|
||||||
.return_address = @returnAddress(),
|
}));
|
||||||
}) orelse return error.OutOfMemory));
|
|
||||||
|
|
||||||
allocation.* = value;
|
allocation.* = value;
|
||||||
|
|
||||||
@ -244,7 +241,6 @@ pub fn deallocate(allocator: Allocator, allocation: anytype) void {
|
|||||||
.Many, .C => @compileError("length of allocation must be known to deallocate"),
|
.Many, .C => @compileError("length of allocation must be known to deallocate"),
|
||||||
},
|
},
|
||||||
|
|
||||||
.return_address = @returnAddress(),
|
|
||||||
.size = 0,
|
.size = 0,
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
@ -52,7 +52,7 @@ pub fn Stack(comptime Value: type) type {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
io.deallocate(allocator, self.values.ptr[0 .. self.capacity]);
|
io.deallocate(allocator, self.values);
|
||||||
|
|
||||||
self.values = &.{};
|
self.values = &.{};
|
||||||
self.capacity = 0;
|
self.capacity = 0;
|
||||||
@ -132,7 +132,7 @@ pub fn Stack(comptime Value: type) type {
|
|||||||
pub fn push_all(self: *Self, allocator: io.Allocator, values: []const Value) io.AllocationError!void {
|
pub fn push_all(self: *Self, allocator: io.Allocator, values: []const Value) io.AllocationError!void {
|
||||||
const new_length = self.values.len + values.len;
|
const new_length = self.values.len + values.len;
|
||||||
|
|
||||||
if (new_length > self.capacity) {
|
if (new_length >= self.capacity) {
|
||||||
try self.grow(allocator, values.len + values.len);
|
try self.grow(allocator, values.len + values.len);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,11 +7,7 @@ const std = @import("std");
|
|||||||
///
|
///
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
const AllocationInfo = struct {
|
const stack_trace_frame_size = if (std.debug.sys_can_stack_trace) 4 else 0;
|
||||||
trace: std.debug.Trace,
|
|
||||||
next_info: ?*AllocationInfo,
|
|
||||||
size: usize,
|
|
||||||
};
|
|
||||||
|
|
||||||
///
|
///
|
||||||
///
|
///
|
||||||
@ -20,16 +16,19 @@ const Context = struct {
|
|||||||
allocation_info_head: ?*AllocationInfo = null,
|
allocation_info_head: ?*AllocationInfo = null,
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Attempts to allocate a buffer of `size` length from `self`, with `return_address` as the location of the
|
|
||||||
/// allocation request origin.
|
|
||||||
///
|
///
|
||||||
/// A reference to the allocated buffer is returned via a slice if the allocation was successful, otherwise `null`
|
|
||||||
/// is returned.
|
|
||||||
///
|
///
|
||||||
/// *Note* the returned buffer must be deallocated with [deallocate] before program exit or it will cause a memory
|
const AllocationInfo = struct {
|
||||||
/// leak.
|
stack_frames: [stack_trace_frame_size]usize,
|
||||||
|
stack_trace: std.builtin.StackTrace,
|
||||||
|
next_info: ?*AllocationInfo,
|
||||||
|
size: usize,
|
||||||
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
fn allocate(self: *Context, size: usize, return_address: usize) ?[]u8 {
|
///
|
||||||
|
///
|
||||||
|
fn allocate(self: *Context, size: usize) ?[]u8 {
|
||||||
const allocation_info_size = @sizeOf(AllocationInfo);
|
const allocation_info_size = @sizeOf(AllocationInfo);
|
||||||
const total_allocation_size = allocation_info_size + size;
|
const total_allocation_size = allocation_info_size + size;
|
||||||
const allocation = ext.SDL_malloc(total_allocation_size) orelse return null;
|
const allocation = ext.SDL_malloc(total_allocation_size) orelse return null;
|
||||||
@ -38,10 +37,15 @@ const Context = struct {
|
|||||||
allocation_info.* = .{
|
allocation_info.* = .{
|
||||||
.size = size,
|
.size = size,
|
||||||
.next_info = self.allocation_info_head,
|
.next_info = self.allocation_info_head,
|
||||||
.trace = .{},
|
.stack_frames = [_]usize{0} ** stack_trace_frame_size,
|
||||||
|
|
||||||
|
.stack_trace = .{
|
||||||
|
.instruction_addresses = &allocation_info.stack_frames,
|
||||||
|
.index = 0,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
allocation_info.trace.addAddr(return_address, "");
|
std.debug.captureStackTrace(null, &allocation_info.stack_trace);
|
||||||
|
|
||||||
self.allocation_info_head = allocation_info;
|
self.allocation_info_head = allocation_info;
|
||||||
|
|
||||||
@ -49,10 +53,7 @@ const Context = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Deallocates a the allocation buffer referenced by `allocation`.
|
|
||||||
///
|
///
|
||||||
/// *Note* the pointer and length of `allocation` must match valid values known to `allocator` otherwise safety-
|
|
||||||
/// checked behavior will occur.
|
|
||||||
///
|
///
|
||||||
fn deallocate(self: *Context, allocation: []u8) void {
|
fn deallocate(self: *Context, allocation: []u8) void {
|
||||||
const target_allocation_info = @intToPtr(*AllocationInfo, @ptrToInt(allocation.ptr) - @sizeOf(AllocationInfo));
|
const target_allocation_info = @intToPtr(*AllocationInfo, @ptrToInt(allocation.ptr) - @sizeOf(AllocationInfo));
|
||||||
@ -91,19 +92,7 @@ const Context = struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Attempts to reallocate the buffer referenced by `allocation` to be `size` length from `self`.
|
|
||||||
///
|
///
|
||||||
/// A reference to the reallocated buffer is returned via a slice if the reallocation was successful, otherwise
|
|
||||||
/// `null` is returned.
|
|
||||||
///
|
|
||||||
/// *Note* the returned buffer must be deallocated with [deallocate] before program exit or it will cause a memory
|
|
||||||
/// leak.
|
|
||||||
///
|
|
||||||
/// *Note* the pointer and length of `allocation` must match valid values known to `allocator` otherwise safety-
|
|
||||||
/// checked behavior will occur.
|
|
||||||
///
|
|
||||||
/// *Note* the allocation referenced by `allocation` should be considered invalid once the function returns,
|
|
||||||
/// discarding it in favor of the return value.
|
|
||||||
///
|
///
|
||||||
fn reallocate(self: *Context, allocation: []u8, size: usize) ?[]u8 {
|
fn reallocate(self: *Context, allocation: []u8, size: usize) ?[]u8 {
|
||||||
const allocation_info_size = @sizeOf(AllocationInfo);
|
const allocation_info_size = @sizeOf(AllocationInfo);
|
||||||
@ -144,7 +133,7 @@ const Context = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Heap context.
|
///
|
||||||
///
|
///
|
||||||
var context = Context{};
|
var context = Context{};
|
||||||
|
|
||||||
@ -160,30 +149,24 @@ pub const allocator = coral.io.Allocator.bind(Context, &context, struct {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.allocate(0, options.return_address);
|
return self.allocate(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.allocation) |allocation| {
|
if (options.allocation) |allocation| {
|
||||||
return self.reallocate(allocation, options.size);
|
return self.reallocate(allocation, options.size);
|
||||||
}
|
}
|
||||||
|
|
||||||
return self.allocate(options.size, options.return_address);
|
return self.allocate(options.size);
|
||||||
}
|
}
|
||||||
}.reallocate);
|
}.reallocate);
|
||||||
|
|
||||||
///
|
///
|
||||||
/// Checks for any allocations belonging to the process heap allocated through the [allocator] interface that are still
|
|
||||||
/// alive and reports the stack traces of any detected allocations to stderr along with the allocation address and
|
|
||||||
/// length.
|
|
||||||
///
|
///
|
||||||
pub fn trace_leaks() void {
|
///
|
||||||
|
pub fn trace_allocations() void {
|
||||||
var current_allocation_info = context.allocation_info_head;
|
var current_allocation_info = context.allocation_info_head;
|
||||||
|
|
||||||
while (current_allocation_info) |allocation_info| : (current_allocation_info = allocation_info.next_info) {
|
while (current_allocation_info) |allocation_info| : (current_allocation_info = allocation_info) {
|
||||||
std.debug.print("{d} byte leak at 0x{x} detected: {}", .{
|
std.debug.dumpStackTrace(allocation_info.stack_trace);
|
||||||
allocation_info.size,
|
|
||||||
@ptrToInt(allocation_info) + @sizeOf(AllocationInfo),
|
|
||||||
allocation_info.trace
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -69,6 +69,21 @@ const Object = struct {
|
|||||||
|
|
||||||
self.ref_count += 1;
|
self.ref_count += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn release(self: *Object, env: *Self) bool {
|
||||||
|
coral.debug.assert(self.ref_count != 0);
|
||||||
|
|
||||||
|
self.ref_count -= 1;
|
||||||
|
|
||||||
|
if (self.ref_count == 0) {
|
||||||
|
coral.io.deallocate(env.allocator, self.state.userdata);
|
||||||
|
self.state.fields.deinit(env.allocator);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub const ObjectInfo = struct {
|
pub const ObjectInfo = struct {
|
||||||
@ -170,10 +185,8 @@ pub fn check(self: *Self, condition: bool, failure_message: []const u8) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub fn deinit(self: *Self) void {
|
pub fn deinit(self: *Self) void {
|
||||||
self.discard(.{.object = self.global_object});
|
|
||||||
self.values.deinit(self.allocator);
|
self.values.deinit(self.allocator);
|
||||||
self.calls.deinit(self.allocator);
|
self.calls.deinit(self.allocator);
|
||||||
self.heap.deinit(self.allocator);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn discard(self: *Self, val: types.Val) void {
|
pub fn discard(self: *Self, val: types.Val) void {
|
||||||
@ -181,19 +194,7 @@ pub fn discard(self: *Self, val: types.Val) void {
|
|||||||
.object => |object| {
|
.object => |object| {
|
||||||
var data = self.heap.fetch(object);
|
var data = self.heap.fetch(object);
|
||||||
|
|
||||||
coral.debug.assert(data.ref_count != 0);
|
if (data.release(self)) {
|
||||||
|
|
||||||
data.ref_count -= 1;
|
|
||||||
|
|
||||||
if (data.ref_count == 0) {
|
|
||||||
data.state.info.deinitializer(.{
|
|
||||||
.env = self,
|
|
||||||
.obj = val.as_ref(),
|
|
||||||
});
|
|
||||||
|
|
||||||
coral.io.deallocate(self.allocator, data.state.userdata);
|
|
||||||
data.state.fields.deinit(self.allocator);
|
|
||||||
|
|
||||||
self.heap.remove(object);
|
self.heap.remove(object);
|
||||||
} else {
|
} else {
|
||||||
self.heap.assign(object, data);
|
self.heap.assign(object, data);
|
||||||
@ -213,7 +214,6 @@ pub fn execute_data(self: *Self, source: DataSource) types.RuntimeError!types.Va
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var compiled_chunk = init_compiled_chunk: {
|
|
||||||
var chunk = try Chunk.init(self, source.name);
|
var chunk = try Chunk.init(self, source.name);
|
||||||
|
|
||||||
errdefer chunk.deinit();
|
errdefer chunk.deinit();
|
||||||
@ -224,10 +224,7 @@ pub fn execute_data(self: *Self, source: DataSource) types.RuntimeError!types.Va
|
|||||||
return compile_error;
|
return compile_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
break: init_compiled_chunk chunk;
|
const script = try self.new_object(coral.io.bytes_of(&chunk), .{
|
||||||
};
|
|
||||||
|
|
||||||
const script = try self.new_object(coral.io.bytes_of(&compiled_chunk), .{
|
|
||||||
.identity = typeid,
|
.identity = typeid,
|
||||||
.deinitializer = Behaviors.deinitialize,
|
.deinitializer = Behaviors.deinitialize,
|
||||||
});
|
});
|
||||||
@ -349,11 +346,12 @@ pub fn native_cast(self: *Self, castable: types.Ref, id: *const anyopaque, compt
|
|||||||
try self.check(castable == .object, "invalid type conversion: object");
|
try self.check(castable == .object, "invalid type conversion: object");
|
||||||
|
|
||||||
const object = self.heap.fetch(castable.object);
|
const object = self.heap.fetch(castable.object);
|
||||||
const is_expected_type = (object.state.info.identity == id) and (object.state.userdata.len == @sizeOf(Type));
|
const alignment = @alignOf(Type);
|
||||||
|
const is_expected_type = (object.state.info.identity == id) and (object.state.userdata.len == alignment);
|
||||||
|
|
||||||
try self.check(is_expected_type, "invalid object cast: native type");
|
try self.check(is_expected_type, "invalid object cast: native type");
|
||||||
|
|
||||||
return @ptrCast(*Type, @alignCast(@alignOf(Type), object.state.userdata));
|
return @ptrCast(*Type, @alignCast(alignment, object.state.userdata));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_array(self: *Self) coral.io.AllocationError!types.Val {
|
pub fn new_array(self: *Self) coral.io.AllocationError!types.Val {
|
||||||
|
@ -56,8 +56,6 @@ const AppManifest = struct {
|
|||||||
};
|
};
|
||||||
|
|
||||||
pub fn run_app(base_file_system: file.System) void {
|
pub fn run_app(base_file_system: file.System) void {
|
||||||
defer heap.trace_leaks();
|
|
||||||
|
|
||||||
const Logger = struct {
|
const Logger = struct {
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
|
||||||
@ -137,4 +135,6 @@ pub fn run_app(base_file_system: file.System) void {
|
|||||||
ext.SDL_RenderPresent(renderer);
|
ext.SDL_RenderPresent(renderer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
heap.trace_allocations();
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user