Controller
queueOrdered functionality
This commit is contained in:
@@ -12,16 +12,19 @@ pub const Controller = Resource.Controller;
|
|||||||
const MAX_SYSTEM_REQUESTS = 8;
|
const MAX_SYSTEM_REQUESTS = 8;
|
||||||
const DEFAULT_SYSTEM_CAPACITY = 16;
|
const DEFAULT_SYSTEM_CAPACITY = 16;
|
||||||
const DEFAULT_CONTROLLERS = 2;
|
const DEFAULT_CONTROLLERS = 2;
|
||||||
|
const DEFAULT_DUDS_PER_CONTROLLER = 4;
|
||||||
|
|
||||||
const ResourceMap = std.AutoArrayHashMapUnmanaged(utils.Hash, Resource);
|
const ResourceMap = std.AutoArrayHashMapUnmanaged(utils.Hash, Resource);
|
||||||
const SystemQueue = std.ArrayListUnmanaged(System);
|
const SystemQueue = std.ArrayListUnmanaged(System);
|
||||||
const Controllers = std.ArrayListUnmanaged(Controller);
|
const Controllers = std.ArrayListUnmanaged(Controller);
|
||||||
|
const Duds = std.ArrayListUnmanaged(System.Dud);
|
||||||
|
|
||||||
/// Assumed to be thread-safe
|
/// Assumed to be thread-safe
|
||||||
alloc: std.mem.Allocator,
|
alloc: std.mem.Allocator,
|
||||||
resources: ResourceMap,
|
resources: ResourceMap,
|
||||||
system_queue: SystemQueue,
|
system_queue: SystemQueue,
|
||||||
controllers: Controllers,
|
controllers: Controllers,
|
||||||
|
duds: Duds,
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
pub fn init(alloc: std.mem.Allocator) !Self {
|
pub fn init(alloc: std.mem.Allocator) !Self {
|
||||||
@@ -38,8 +41,16 @@ pub fn init(alloc: std.mem.Allocator) !Self {
|
|||||||
controller.deinit();
|
controller.deinit();
|
||||||
};
|
};
|
||||||
|
|
||||||
for (0..DEFAULT_CONTROLLERS) |_| {
|
var duds = try Duds.initCapacity(alloc, DEFAULT_CONTROLLERS * DEFAULT_DUDS_PER_CONTROLLER);
|
||||||
const controller = try Controller.create(alloc);
|
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);
|
controllers.appendAssumeCapacity(controller);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,6 +59,7 @@ pub fn init(alloc: std.mem.Allocator) !Self {
|
|||||||
.resources = resources,
|
.resources = resources,
|
||||||
.system_queue = system_queue,
|
.system_queue = system_queue,
|
||||||
.controllers = controllers,
|
.controllers = controllers,
|
||||||
|
.duds = duds,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,6 +80,8 @@ pub fn deinit(self: *Self) void {
|
|||||||
controller.deinit();
|
controller.deinit();
|
||||||
}
|
}
|
||||||
self.controllers.deinit(self.alloc);
|
self.controllers.deinit(self.alloc);
|
||||||
|
|
||||||
|
self.duds.deinit(self.alloc);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enqueueSystem(self: *Self, system: System) !void {
|
fn enqueueSystem(self: *Self, system: System) !void {
|
||||||
@@ -76,15 +90,41 @@ fn enqueueSystem(self: *Self, system: System) !void {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn runAllSystems(self: *Self) GraphError!void {
|
fn runAllSystems(self: *Self) GraphError!void {
|
||||||
while (self.system_queue.pop()) |next_system| {
|
while (self.system_queue.items.len > 0) {
|
||||||
defer next_system.deinit(self.alloc);
|
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);
|
try self.runSystem(next_system);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Does not deallocate the system
|
/// Does not deallocate the system
|
||||||
fn runSystem(self: *Self, system: System) GraphError!void {
|
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 buffer: [MAX_SYSTEM_REQUESTS]*anyopaque = undefined;
|
||||||
var controller: ?Controller = null;
|
var controller: ?Controller = null;
|
||||||
errdefer if (controller) |*c| c.deinit();
|
errdefer if (controller) |*c| c.deinit();
|
||||||
@@ -103,6 +143,9 @@ fn runSystem(self: *Self, system: System) GraphError!void {
|
|||||||
buffer_len += 1;
|
buffer_len += 1;
|
||||||
}
|
}
|
||||||
system.function_runner(buffer[0..buffer_len]);
|
system.function_runner(buffer[0..buffer_len]);
|
||||||
|
if (system.submit_dud) |dud_id| {
|
||||||
|
self.duds.items[dud_id].required_count -= 1;
|
||||||
|
}
|
||||||
|
|
||||||
if (controller) |c| {
|
if (controller) |c| {
|
||||||
defer controller = null;
|
defer controller = null;
|
||||||
@@ -123,7 +166,15 @@ fn getController(self: *Self) !Controller {
|
|||||||
if (self.controllers.pop()) |c| {
|
if (self.controllers.pop()) |c| {
|
||||||
return 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)
|
/// 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{
|
const GraphError = error{
|
||||||
MissingResource,
|
MissingResource,
|
||||||
OutOfMemory,
|
OutOfMemory,
|
||||||
|
SystemDeadlock,
|
||||||
};
|
};
|
||||||
|
|
||||||
test {
|
test {
|
||||||
@@ -174,9 +226,25 @@ test {
|
|||||||
fn addTen(rsc: *@This()) void {
|
fn addTen(rsc: *@This()) void {
|
||||||
rsc.number += 10;
|
rsc.number += 10;
|
||||||
}
|
}
|
||||||
|
fn addThousand(rsc: *@This()) void {
|
||||||
|
rsc.number += 1000;
|
||||||
|
}
|
||||||
|
fn subThousand(rsc: *@This()) void {
|
||||||
|
rsc.number -= 1000;
|
||||||
|
}
|
||||||
fn addEleven(cmd: *Controller) void {
|
fn addEleven(cmd: *Controller) void {
|
||||||
cmd.queueSystem(addTen);
|
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,
|
alloc: std.mem.Allocator,
|
||||||
command_buffer: std.ArrayListUnmanaged(Command),
|
command_buffer: std.ArrayListUnmanaged(Command),
|
||||||
error_state: ErrorState,
|
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) {
|
pub const Command = union(enum) {
|
||||||
add_resource: Resource,
|
add_resource: Resource,
|
||||||
@@ -36,6 +39,9 @@ pub const Controller = struct {
|
|||||||
.alloc = alloc,
|
.alloc = alloc,
|
||||||
.command_buffer = try std.ArrayListUnmanaged(Command).initCapacity(alloc, DEFAULT_CONTROLLER_CAPACITY),
|
.command_buffer = try std.ArrayListUnmanaged(Command).initCapacity(alloc, DEFAULT_CONTROLLER_CAPACITY),
|
||||||
.error_state = .ok,
|
.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;
|
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 {
|
pub fn clear(self: *Controller) void {
|
||||||
self.command_buffer.clearRetainingCapacity();
|
self.command_buffer.clearRetainingCapacity();
|
||||||
switch (self.error_state) {
|
switch (self.error_state) {
|
||||||
@@ -53,6 +64,7 @@ pub const Controller = struct {
|
|||||||
.recoverable => |msg| self.alloc.free(msg),
|
.recoverable => |msg| self.alloc.free(msg),
|
||||||
}
|
}
|
||||||
self.error_state = .ok;
|
self.error_state = .ok;
|
||||||
|
self.submit_dud = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Adds resource to the global storage, discarding any previously existing data
|
/// 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);
|
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 {
|
fn queueSystemInternal(self: *Controller, comptime function: anytype) !void {
|
||||||
var system = try System.fromFunction(function, self.alloc);
|
var system = try System.fromFunction(function, self.alloc);
|
||||||
errdefer system.deinit(self.alloc);
|
errdefer system.deinit(self.alloc);
|
||||||
|
@@ -4,6 +4,14 @@ const Controller = @import("resource.zig").Controller;
|
|||||||
|
|
||||||
function_runner: *const fn ([]const *anyopaque) void,
|
function_runner: *const fn ([]const *anyopaque) void,
|
||||||
requested_types: []const Request,
|
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) {
|
pub const Request = union(enum) {
|
||||||
resource: utils.Hash,
|
resource: utils.Hash,
|
||||||
@@ -27,6 +35,8 @@ pub fn fromFunction(comptime function: anytype, alloc: std.mem.Allocator) !Self
|
|||||||
return Self{
|
return Self{
|
||||||
.requested_types = try alloc.dupe(Request, &requests),
|
.requested_types = try alloc.dupe(Request, &requests),
|
||||||
.function_runner = utils.generateRunner(function),
|
.function_runner = utils.generateRunner(function),
|
||||||
|
.requires_dud = null,
|
||||||
|
.submit_dud = null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user