Controller queueOrdered functionality

This commit is contained in:
duck
2025-05-04 21:05:17 +05:00
parent c19120c3fc
commit 4cb0c6aee9
3 changed files with 150 additions and 7 deletions

View File

@@ -12,16 +12,19 @@ pub const Controller = Resource.Controller;
const MAX_SYSTEM_REQUESTS = 8;
const DEFAULT_SYSTEM_CAPACITY = 16;
const DEFAULT_CONTROLLERS = 2;
const DEFAULT_DUDS_PER_CONTROLLER = 4;
const ResourceMap = std.AutoArrayHashMapUnmanaged(utils.Hash, Resource);
const SystemQueue = std.ArrayListUnmanaged(System);
const Controllers = std.ArrayListUnmanaged(Controller);
const Duds = std.ArrayListUnmanaged(System.Dud);
/// Assumed to be thread-safe
alloc: std.mem.Allocator,
resources: ResourceMap,
system_queue: SystemQueue,
controllers: Controllers,
duds: Duds,
const Self = @This();
pub fn init(alloc: std.mem.Allocator) !Self {
@@ -38,8 +41,16 @@ pub fn init(alloc: std.mem.Allocator) !Self {
controller.deinit();
};
for (0..DEFAULT_CONTROLLERS) |_| {
const controller = try Controller.create(alloc);
var duds = try Duds.initCapacity(alloc, DEFAULT_CONTROLLERS * DEFAULT_DUDS_PER_CONTROLLER);
errdefer duds.deinit(alloc);
for (0..DEFAULT_CONTROLLERS * DEFAULT_DUDS_PER_CONTROLLER) |_| {
duds.appendAssumeCapacity(.{});
}
for (0..DEFAULT_CONTROLLERS) |i| {
var controller = try Controller.create(alloc);
controller.setDuds(@intCast(DEFAULT_DUDS_PER_CONTROLLER * i), duds.items[DEFAULT_DUDS_PER_CONTROLLER * i .. DEFAULT_DUDS_PER_CONTROLLER * (i + 1)]);
controllers.appendAssumeCapacity(controller);
}
@@ -48,6 +59,7 @@ pub fn init(alloc: std.mem.Allocator) !Self {
.resources = resources,
.system_queue = system_queue,
.controllers = controllers,
.duds = duds,
};
}
@@ -68,6 +80,8 @@ pub fn deinit(self: *Self) void {
controller.deinit();
}
self.controllers.deinit(self.alloc);
self.duds.deinit(self.alloc);
}
fn enqueueSystem(self: *Self, system: System) !void {
@@ -76,15 +90,41 @@ fn enqueueSystem(self: *Self, system: System) !void {
}
fn runAllSystems(self: *Self) GraphError!void {
while (self.system_queue.pop()) |next_system| {
defer next_system.deinit(self.alloc);
while (self.system_queue.items.len > 0) {
var swap_with = self.system_queue.items.len - 1;
while (true) {
const system = &self.system_queue.items[self.system_queue.items.len - 1];
if (system.requires_dud) |dud_id| {
if (self.duds.items[dud_id].required_count == 0) {
break;
}
} else break;
if (swap_with > 1) {
swap_with -= 1;
std.mem.swap(
System,
&self.system_queue.items[self.system_queue.items.len - 1],
&self.system_queue.items[swap_with],
);
} else {
return GraphError.SystemDeadlock;
}
}
const next_system = self.system_queue.pop().?;
defer next_system.deinit(self.alloc);
try self.runSystem(next_system);
}
}
/// Does not deallocate the system
fn runSystem(self: *Self, system: System) GraphError!void {
if (system.requires_dud) |dud_id| {
std.debug.assert(self.duds.items[dud_id].required_count == 0);
}
var buffer: [MAX_SYSTEM_REQUESTS]*anyopaque = undefined;
var controller: ?Controller = null;
errdefer if (controller) |*c| c.deinit();
@@ -103,6 +143,9 @@ fn runSystem(self: *Self, system: System) GraphError!void {
buffer_len += 1;
}
system.function_runner(buffer[0..buffer_len]);
if (system.submit_dud) |dud_id| {
self.duds.items[dud_id].required_count -= 1;
}
if (controller) |c| {
defer controller = null;
@@ -123,7 +166,15 @@ fn getController(self: *Self) !Controller {
if (self.controllers.pop()) |c| {
return c;
}
return Controller.create(self.alloc);
const next_dud_id = self.duds.items.len;
for (try self.duds.addManyAsSlice(self.alloc, DEFAULT_DUDS_PER_CONTROLLER)) |*dud| {
dud.required_count = 0;
}
errdefer self.duds.shrinkRetainingCapacity(self.duds.items.len - DEFAULT_DUDS_PER_CONTROLLER);
var controller = try Controller.create(self.alloc);
controller.setDuds(@intCast(next_dud_id), self.duds.items[next_dud_id .. next_dud_id + DEFAULT_DUDS_PER_CONTROLLER]);
return controller;
}
/// Evaluates and clears the controller (even if errors out)
@@ -161,6 +212,7 @@ pub inline fn addResource(self: *Self, resource: Resource) !void {
const GraphError = error{
MissingResource,
OutOfMemory,
SystemDeadlock,
};
test {
@@ -174,9 +226,25 @@ test {
fn addTen(rsc: *@This()) void {
rsc.number += 10;
}
fn addThousand(rsc: *@This()) void {
rsc.number += 1000;
}
fn subThousand(rsc: *@This()) void {
rsc.number -= 1000;
}
fn addEleven(cmd: *Controller) void {
cmd.queueSystem(addTen);
cmd.queuesystem(addOne);
cmd.queueSystem(addOne);
cmd.queueOrdered(.{
addThousand,
addThousand,
addThousand,
}, .{
subThousand,
subThousand,
subThousand,
});
}
};

View File

@@ -20,6 +20,9 @@ pub const Controller = struct {
alloc: std.mem.Allocator,
command_buffer: std.ArrayListUnmanaged(Command),
error_state: ErrorState,
duds: [*]System.Dud,
dud_range: struct { System.Dud.Id, System.Dud.Id },
submit_dud: ?System.Dud.Id,
pub const Command = union(enum) {
add_resource: Resource,
@@ -36,6 +39,9 @@ pub const Controller = struct {
.alloc = alloc,
.command_buffer = try std.ArrayListUnmanaged(Command).initCapacity(alloc, DEFAULT_CONTROLLER_CAPACITY),
.error_state = .ok,
.duds = &[0]System.Dud{},
.dud_range = .{ 0, 0 },
.submit_dud = null,
};
}
@@ -45,7 +51,12 @@ pub const Controller = struct {
return self.command_buffer.items;
}
/// Clears the command buffer, but does not deallocate it's contents
pub fn setDuds(self: *Controller, start_id: System.Dud.Id, buffer: []System.Dud) void {
self.dud_range = .{ start_id, start_id + @as(System.Dud.Id, @intCast(buffer.len)) };
self.duds = @ptrCast(buffer);
}
/// Clears the command buffer for the next use (does not deallocate it's contents)
pub fn clear(self: *Controller) void {
self.command_buffer.clearRetainingCapacity();
switch (self.error_state) {
@@ -53,6 +64,7 @@ pub const Controller = struct {
.recoverable => |msg| self.alloc.free(msg),
}
self.error_state = .ok;
self.submit_dud = null;
}
/// Adds resource to the global storage, discarding any previously existing data
@@ -73,6 +85,59 @@ pub const Controller = struct {
self.queueSystemInternal(function) catch |err| self.fail(err);
}
/// Function sets are expected to be tuples of system functions
pub fn queueOrdered(
self: *Controller,
comptime function_set_first: anytype,
comptime function_set_second: anytype,
) void {
self.queueOrderedInternal(function_set_first, function_set_second) catch |err| self.fail(err);
}
pub fn queueOrderedInternal(
self: *Controller,
comptime function_set_first: anytype,
comptime function_set_second: anytype,
) !void {
if (self.dud_range[0] == self.dud_range[1]) {
// TODO: Make `Controller` request more ids
self.error_state = .unrecoverable;
return;
}
const commands_first = @typeInfo(@TypeOf(function_set_first)).@"struct".fields.len;
const commands_second = @typeInfo(@TypeOf(function_set_second)).@"struct".fields.len;
var new_commands: [commands_first + commands_second]Command = undefined;
var i: usize = 0;
errdefer for (0..i) |del_i| {
new_commands[del_i].queue_system.deinit(self.alloc);
};
std.debug.assert(self.duds[0].required_count == 0);
self.duds[0].required_count = commands_first;
const dud_id = self.dud_range[0];
self.duds += 1;
self.dud_range[0] += 1;
inline for (function_set_first) |fn_first| {
var system = try System.fromFunction(fn_first, self.alloc);
system.submit_dud = dud_id;
new_commands[i] = Command{ .queue_system = system };
i += 1;
}
inline for (function_set_second) |fn_second| {
var system = try System.fromFunction(fn_second, self.alloc);
system.requires_dud = dud_id;
system.submit_dud = self.submit_dud;
new_commands[i] = Command{ .queue_system = system };
i += 1;
}
std.debug.assert(i == new_commands.len);
try self.command_buffer.appendSlice(self.alloc, &new_commands);
}
fn queueSystemInternal(self: *Controller, comptime function: anytype) !void {
var system = try System.fromFunction(function, self.alloc);
errdefer system.deinit(self.alloc);

View File

@@ -4,6 +4,14 @@ const Controller = @import("resource.zig").Controller;
function_runner: *const fn ([]const *anyopaque) void,
requested_types: []const Request,
requires_dud: ?Dud.Id,
submit_dud: ?Dud.Id,
pub const Dud = struct {
pub const Id = u16;
required_count: usize = 0,
};
pub const Request = union(enum) {
resource: utils.Hash,
@@ -27,6 +35,8 @@ pub fn fromFunction(comptime function: anytype, alloc: std.mem.Allocator) !Self
return Self{
.requested_types = try alloc.dupe(Request, &requests),
.function_runner = utils.generateRunner(function),
.requires_dud = null,
.submit_dud = null,
};
}