Application Context Implementation #4

Closed
kayomn wants to merge 93 commits from event-loop-dev into main
2 changed files with 55 additions and 54 deletions
Showing only changes of commit 84664b5962 - Show all commits

View File

@ -21,7 +21,7 @@ test {
_ = sys; _ = sys;
} }
fn run(app: *sys.App, graphics: *sys.GraphicsContext) anyerror!void { fn run(app: *sys.AppContext, graphics: *sys.GraphicsContext) anyerror!void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit(); defer _ = gpa.deinit();

View File

@ -12,7 +12,7 @@ const std = @import("std");
/// ///
/// A thread-safe platform abstraction over multiplexing system I/O processing and event handling. /// A thread-safe platform abstraction over multiplexing system I/O processing and event handling.
/// ///
pub const App = opaque { pub const AppContext = opaque {
/// ///
/// Linked list of asynchronous messages chained together to be processed by the work processor. /// Linked list of asynchronous messages chained together to be processed by the work processor.
/// ///
@ -71,13 +71,13 @@ pub const App = opaque {
}; };
/// ///
/// Casts `app` to a [Implementation] reference. /// Casts `app_context` to a [Implementation] reference.
/// ///
/// *Note* that if `app` does not have the same alignment as [Implementation], safety- /// *Note* that if `app_context` does not have the same alignment as [Implementation], safety-
/// checked undefined behavior will occur. /// checked undefined behavior will occur.
/// ///
fn cast(app: *App) *Implementation { fn cast(app_context: *AppContext) *Implementation {
return @ptrCast(*Implementation, @alignCast(@alignOf(Implementation), app)); return @ptrCast(*Implementation, @alignCast(@alignOf(Implementation), app_context));
} }
/// ///
@ -90,7 +90,7 @@ pub const App = opaque {
.kind = .quit, .kind = .quit,
}; };
@ptrCast(*App, implementation).schedule(&message); @ptrCast(*AppContext, implementation).schedule(&message);
{ {
var status = @as(c_int, 0); var status = @as(c_int, 0);
@ -132,7 +132,7 @@ pub const App = opaque {
/// occured. /// occured.
/// ///
fn processTasks(userdata: ?*anyopaque) callconv(.C) c_int { fn processTasks(userdata: ?*anyopaque) callconv(.C) c_int {
const implementation = Implementation.cast(@ptrCast(*App, userdata orelse unreachable)); const implementation = Implementation.cast(@ptrCast(*AppContext, userdata orelse unreachable));
while (true) { while (true) {
_ = ext.SDL_LockMutex(implementation.message_mutex); _ = ext.SDL_LockMutex(implementation.message_mutex);
@ -164,7 +164,9 @@ pub const App = opaque {
fn start(implementation: *Implementation) StartError!void { fn start(implementation: *Implementation) StartError!void {
if (implementation.message_thread != null) return error.AlreadyStarted; if (implementation.message_thread != null) return error.AlreadyStarted;
implementation.message_thread = ext.SDL_CreateThread(processTasks, "File System Worker", implementation) orelse { implementation.message_thread = ext.SDL_CreateThread(processTasks,
"File System Worker", implementation) orelse {
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION,
"Failed to create file-system work processor"); "Failed to create file-system work processor");
@ -176,16 +178,16 @@ pub const App = opaque {
/// ///
/// Returns a reference to the currently loaded data file-system. /// Returns a reference to the currently loaded data file-system.
/// ///
pub fn data(app: *App) *const FileSystem { pub fn data(app_context: *AppContext) *const FileSystem {
return &Implementation.cast(app).data_file_system; return &Implementation.cast(app_context).data_file_system;
} }
/// ///
/// Enqueues `message` to the message processor of `app` to be processed at a later, non- /// Enqueues `message` to the message processor of `app_context` to be processed at a later, non-
/// deterministic point. /// deterministic point.
/// ///
pub fn schedule(app: *App, message: *Message) void { pub fn schedule(app_context: *AppContext, message: *Message) void {
const implementation = Implementation.cast(app); const implementation = Implementation.cast(app_context);
// TODO: Error check these. // TODO: Error check these.
_ = ext.SDL_LockMutex(implementation.message_mutex); _ = ext.SDL_LockMutex(implementation.message_mutex);
@ -205,8 +207,8 @@ pub const App = opaque {
/// ///
/// Returns a reference to the currently loaded user file-system. /// Returns a reference to the currently loaded user file-system.
/// ///
pub fn user(app: *App) *const FileSystem { pub fn user(app_context: *AppContext) *const FileSystem {
return &Implementation.cast(app).user_file_system; return &Implementation.cast(app_context).user_file_system;
} }
}; };
@ -236,7 +238,7 @@ pub const FileAccess = opaque {
/// Freeing an invalid `file_access` has no effect on the file and logs a warning over the /// Freeing an invalid `file_access` has no effect on the file and logs a warning over the
/// wasted effort. /// wasted effort.
/// ///
pub fn close(file_access: *FileAccess, app: *App) void { pub fn close(file_access: *FileAccess, app_context: *AppContext) void {
const Task = struct { const Task = struct {
file_access: *FileAccess, file_access: *FileAccess,
@ -253,7 +255,7 @@ pub const FileAccess = opaque {
var task = Task{.file_access = file_access}; var task = Task{.file_access = file_access};
var message = App.Message{ var message = AppContext.Message{
.frame = @frame(), .frame = @frame(),
.kind = .{.task = .{ .kind = .{.task = .{
@ -262,7 +264,7 @@ pub const FileAccess = opaque {
}}, }},
}; };
suspend app.schedule(&message); suspend app_context.schedule(&message);
} }
/// ///
@ -271,7 +273,7 @@ pub const FileAccess = opaque {
/// Returns the number of bytes into the file that the cursor is relative to its beginning or a /// Returns the number of bytes into the file that the cursor is relative to its beginning or a
/// [Error] on failure. /// [Error] on failure.
/// ///
pub fn queryCursor(file_access: *FileAccess, app: *App) Error!u64 { pub fn queryCursor(file_access: *FileAccess, app_context: *AppContext) Error!u64 {
const Task = struct { const Task = struct {
file_access: *FileAccess, file_access: *FileAccess,
result: Error!u64, result: Error!u64,
@ -300,7 +302,7 @@ pub const FileAccess = opaque {
.result = error.FileInaccessible, .result = error.FileInaccessible,
}; };
var message = App.Message{ var message = AppContext.Message{
.frame = @frame(), .frame = @frame(),
.kind = .{.task = .{ .kind = .{.task = .{
@ -309,7 +311,7 @@ pub const FileAccess = opaque {
}}, }},
}; };
suspend app.schedule(&message); suspend app_context.schedule(&message);
return task.result; return task.result;
} }
@ -320,7 +322,7 @@ pub const FileAccess = opaque {
/// Returns the current length of the file at the time of the operation or a [Error] if the file /// Returns the current length of the file at the time of the operation or a [Error] if the file
/// failed to be queried. /// failed to be queried.
/// ///
pub fn queryLength(file_access: *FileAccess, app: *App) Error!u64 { pub fn queryLength(file_access: *FileAccess, app_context: *AppContext) Error!u64 {
const Task = struct { const Task = struct {
file_access: *FileAccess, file_access: *FileAccess,
result: Error!usize, result: Error!usize,
@ -349,7 +351,7 @@ pub const FileAccess = opaque {
.result = error.FileInaccessible, .result = error.FileInaccessible,
}; };
var message = App.Message{ var message = AppContext.Message{
.frame = @frame(), .frame = @frame(),
.kind = .{.task = .{ .kind = .{.task = .{
@ -358,18 +360,18 @@ pub const FileAccess = opaque {
}}, }},
}; };
suspend app.schedule(&message); suspend app_context.schedule(&message);
return task.result; return task.result;
} }
/// ///
/// Attempts to read `file_access` from the its current position into `buffer`, while using /// Attempts to read `file_access` from the its current position into `buffer`, while using
/// `app` as the execution context. /// `app_context` as the execution context.
/// ///
/// Returns the number of bytes that were available to be read, otherwise an [Error] on failure. /// Returns the number of bytes that were available to be read, otherwise an [Error] on failure.
/// ///
pub fn read(file_access: *FileAccess, app: *App, buffer: []u8) Error!usize { pub fn read(file_access: *FileAccess, app_context: *AppContext, buffer: []u8) Error!usize {
const Task = struct { const Task = struct {
file_access: *FileAccess, file_access: *FileAccess,
buffer: []u8, buffer: []u8,
@ -401,7 +403,7 @@ pub const FileAccess = opaque {
.result = error.FileInaccessible, .result = error.FileInaccessible,
}; };
var message = App.Message{ var message = AppContext.Message{
.frame = @frame(), .frame = @frame(),
.kind = .{.task = .{ .kind = .{.task = .{
@ -410,18 +412,18 @@ pub const FileAccess = opaque {
}}, }},
}; };
suspend app.schedule(&message); suspend app_context.schedule(&message);
return task.result; return task.result;
} }
/// ///
/// Attempts to seek `file_access` from the beginning of the file to `cursor` bytes while using /// Attempts to seek `file_access` from the beginning of the file to `cursor` bytes while using
/// `app` as the execution context. /// `app_context` as the execution context.
/// ///
/// Returns [Error] on failure. /// Returns [Error] on failure.
/// ///
pub fn seek(file_access: *FileAccess, app: *App, cursor: u64) Error!void { pub fn seek(file_access: *FileAccess, app_context: *AppContext, cursor: u64) Error!void {
const Task = struct { const Task = struct {
file_access: *FileAccess, file_access: *FileAccess,
cursor: u64, cursor: u64,
@ -458,7 +460,7 @@ pub const FileAccess = opaque {
.result = error.FileInaccessible, .result = error.FileInaccessible,
}; };
var message = App.Message{ var message = AppContext.Message{
.frame = @frame(), .frame = @frame(),
.kind = .{.task = .{ .kind = .{.task = .{
@ -467,18 +469,18 @@ pub const FileAccess = opaque {
}}, }},
}; };
suspend app.schedule(&message); suspend app_context.schedule(&message);
return task.result; return task.result;
} }
/// ///
/// Attempts to seek `file_access` to the end of the file while using `app` as the execution /// Attempts to seek `file_access` to the end of the file while using `app_context` as the execution
/// context. /// context.
/// ///
/// Returns [Error] on failure. /// Returns [Error] on failure.
/// ///
pub fn seekToEnd(file_access: *FileAccess, app: *App) Error!void { pub fn seekToEnd(file_access: *FileAccess, app_context: *AppContext) Error!void {
const Task = struct { const Task = struct {
file_access: *FileAccess, file_access: *FileAccess,
result: Error!void, result: Error!void,
@ -505,7 +507,7 @@ pub const FileAccess = opaque {
.result = error.FileInaccessible, .result = error.FileInaccessible,
}; };
var message = App.Message{ var message = AppContext.Message{
.frame = @frame(), .frame = @frame(),
.kind = .{.task = .{ .kind = .{.task = .{
@ -514,7 +516,7 @@ pub const FileAccess = opaque {
}}, }},
}; };
suspend app.schedule(&message); suspend app_context.schedule(&message);
return task.result; return task.result;
} }
@ -593,15 +595,15 @@ pub const FileSystem = union(enum) {
/// ///
/// Attempts to open the file identified by `path` with `mode` as the mode for opening the /// Attempts to open the file identified by `path` with `mode` as the mode for opening the
/// file and `app` as the execution context. /// file and `app_context` as the execution context.
/// ///
/// Returns a [FileAccess] reference that provides access to the file referenced by `path` /// Returns a [FileAccess] reference that provides access to the file referenced by `path`
/// or a [OpenError] if it failed. /// or a [OpenError] if it failed.
/// ///
pub fn open(path: Path, app: *App, mode: OpenMode) OpenError!*FileAccess { pub fn open(path: Path, app_context: *AppContext, mode: OpenMode) OpenError!*FileAccess {
const Task = struct { const Task = struct {
path: *const FileSystem.Path, path: *const FileSystem.Path,
app: *App, app_context: *AppContext,
mode: OpenMode, mode: OpenMode,
result: OpenError!*FileAccess, result: OpenError!*FileAccess,
@ -738,11 +740,11 @@ pub const FileSystem = union(enum) {
var task = Task{ var task = Task{
.mode = mode, .mode = mode,
.path = &path, .path = &path,
.app = app, .app_context = app_context,
.result = error.FileNotFound, .result = error.FileNotFound,
}; };
var message = App.Message{ var message = AppContext.Message{
.frame = @frame(), .frame = @frame(),
.kind = .{.task = .{ .kind = .{.task = .{
@ -751,7 +753,7 @@ pub const FileSystem = union(enum) {
}}, }},
}; };
suspend app.schedule(&message); suspend app_context.schedule(&message);
return task.result; return task.result;
} }
@ -863,7 +865,7 @@ pub const GraphicsContext = opaque {
/// Returns a graphics runner that uses `Errors` as its error set. /// Returns a graphics runner that uses `Errors` as its error set.
/// ///
pub fn GraphicsRunner(comptime Errors: type) type { pub fn GraphicsRunner(comptime Errors: type) type {
return fn (*App, *GraphicsContext) callconv(.Async) Errors!void; return fn (*AppContext, *GraphicsContext) callconv(.Async) Errors!void;
} }
/// ///
@ -882,10 +884,10 @@ pub const Log = enum(u32) {
warning = ext.SDL_LOG_PRIORITY_WARN, warning = ext.SDL_LOG_PRIORITY_WARN,
/// ///
/// Writes `utf8_message` as the log kind identified by `log` with `app` as the execution /// Writes `utf8_message` as the log kind identified by `log` with `app_context` as the execution
/// context. /// context.
/// ///
pub fn write(log: Log, app: *App, utf8_message: []const u8) void { pub fn write(log: Log, app_context: *AppContext, utf8_message: []const u8) void {
const Task = struct { const Task = struct {
log: Log, log: Log,
utf8_message: []const u8, utf8_message: []const u8,
@ -905,7 +907,7 @@ pub const Log = enum(u32) {
.utf8_message = utf8_message, .utf8_message = utf8_message,
}; };
var message = App.Message{ var message = AppContext.Message{
.frame = @frame(), .frame = @frame(),
.kind = .{.task = .{ .kind = .{.task = .{
@ -914,7 +916,7 @@ pub const Log = enum(u32) {
}} }}
}; };
suspend app.schedule(&message); suspend app_context.schedule(&message);
} }
}; };
@ -972,15 +974,14 @@ pub fn runGraphics(comptime Error: anytype,
defer ext.SDL_DestroyRenderer(renderer); defer ext.SDL_DestroyRenderer(renderer);
const user_path_prefix = ext.SDL_GetPrefPath("ona", "ona") orelse { const user_path_prefix = ext.SDL_GetPrefPath("ona", "ona") orelse {
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, "Failed to load user path");
"Failed to load user path");
return error.InitFailure; return error.InitFailure;
}; };
defer ext.SDL_free(user_path_prefix); defer ext.SDL_free(user_path_prefix);
var app = App.Implementation.init("./data.oar", user_path_prefix var app_context = AppContext.Implementation.init("./data.oar", user_path_prefix
[0 .. std.mem.len(user_path_prefix)]) catch |err| { [0 .. std.mem.len(user_path_prefix)]) catch |err| {
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, switch (err) { ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, switch (err) {
@ -992,9 +993,9 @@ pub fn runGraphics(comptime Error: anytype,
return error.InitFailure; return error.InitFailure;
}; };
defer app.deinit(); defer app_context.deinit();
app.start() catch |err| { app_context.start() catch |err| {
ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, switch (err) { ext.SDL_LogCritical(ext.SDL_LOG_CATEGORY_APPLICATION, switch (err) {
// Not possible for it to have already been started. // Not possible for it to have already been started.
error.AlreadyStarted => unreachable, error.AlreadyStarted => unreachable,
@ -1010,5 +1011,5 @@ pub fn runGraphics(comptime Error: anytype,
}, },
}; };
return run(@ptrCast(*App, &app), @ptrCast(*GraphicsContext, &graphics_context)); return run(@ptrCast(*AppContext, &app_context), @ptrCast(*GraphicsContext, &graphics_context));
} }