Controller
queueOrdered functionality
This commit is contained in:
@@ -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,
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
|
@@ -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);
|
||||
|
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user