Last active
October 10, 2025 02:12
-
-
Save andrewrk/1ad9d705ce6046fca76b4cb1220b3c53 to your computer and use it in GitHub Desktop.
Revisions
-
andrewrk revised this gist
Jul 10, 2025 . 1 changed file with 18 additions and 18 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -8,7 +8,7 @@ pub fn main() !void { const gpa = debug_allocator.allocator(); //const gpa = std.heap.smp_allocator; var thread_pool: std.Io.ThreadPool = undefined; try thread_pool.init(.{ .allocator = gpa, .n_jobs = std.Thread.getCpuCount() catch 1, @@ -23,29 +23,29 @@ pub fn main() !void { //const io = event_loop.io(); var first_half = io.async(calcSum, .{ 0, 100 }); var second_half = io.async(calcSum, .{ 100, 200 }); var rot13 = io.async(processTextFile, .{io}); var select_example = io.async(selectExample, .{ io, Io.Dir.cwd() }); defer select_example.cancel(io) catch {}; var queue: Io.Queue(i32) = .init(&.{}); var producer = io.async(producerRun, .{ io, &queue }); defer producer.cancel(io) catch {}; var consumer = io.async(consumerRun, .{ io, &queue }); defer _ = consumer.cancel(io); const total = first_half.await(io) + second_half.await(io); std.log.info("total: {d}", .{total}); try rot13.await(io); const consumer_sum = consumer.await(io); std.log.info("consumer sum = {d}", .{consumer_sum}); try select_example.await(io); } fn producerRun(io: Io, queue: *Io.Queue(i32)) !void { @@ -74,29 +74,29 @@ fn calcSum(start: usize, end: usize) usize { } fn processTextFile(io: Io) !void { var future_in_file = io.async(Io.Dir.openFile, .{ .cwd(), io, "example.txt", .{} }); defer if (future_in_file.cancel(io)) |f| f.close(io) else |_| {}; var future_out_file = io.async(Io.Dir.createFile, .{ .cwd(), io, "output.txt", .{} }); defer if (future_out_file.cancel(io)) |f| f.close(io) else |_| {}; const in_file = try future_in_file.await(io); var buffer: [5000]u8 = undefined; const n = try in_file.readAll(io, &buffer); const contents = buffer[0..n]; for (contents) |*elem| elem.* = elem.* +% 1; const out_file = try future_out_file.await(io); try out_file.writeAll(io, contents); } fn selectExample(io: Io, dir: Io.Dir) !void { var a_future = io.async(Io.Dir.writeFile, .{ dir, io, .{ .sub_path = "a.txt", .data = "a contents" } }); defer _ = a_future.cancel(io) catch {}; var b_future = io.async(Io.Dir.writeFile, .{ dir, io, .{ .sub_path = "b.txt", .data = "b contents" } }); defer _ = b_future.cancel(io) catch {}; var timeout = io.async(Io.sleepDuration, .{ io, .ms(100) }); defer timeout.cancel(io) catch {}; switch (io.select(.{ -
andrewrk revised this gist
Apr 4, 2025 . 1 changed file with 41 additions and 13 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -28,6 +28,9 @@ pub fn main() !void { var rot13 = io.@"async"(processTextFile, .{io}); var select_example = io.@"async"(selectExample, .{ io, Io.Dir.cwd() }); defer select_example.cancel(io) catch {}; var queue: Io.Queue(i32) = .init(&.{}); var producer = io.@"async"(producerRun, .{ io, &queue }); defer producer.cancel(io) catch {}; @@ -41,19 +44,21 @@ pub fn main() !void { const consumer_sum = consumer.@"await"(io); std.log.info("consumer sum = {d}", .{consumer_sum}); try select_example.@"await"(io); } fn producerRun(io: Io, queue: *Io.Queue(i32)) !void { for (0..100) |i| { try queue.putOne(io, @intCast(i * 2 + 1)); } try queue.putOne(io, 999); } fn consumerRun(io: Io, queue: *Io.Queue(i32)) i32 { var sum: i32 = 0; while (true) { const n = queue.getOne(io) catch return undefined; if (n == 999) return sum; sum += n; } @@ -69,23 +74,46 @@ fn calcSum(start: usize, end: usize) usize { } fn processTextFile(io: Io) !void { var future_in_file = io.@"async"(Io.Dir.openFile, .{ .cwd(), io, "example.txt", .{} }); defer if (future_in_file.cancel(io)) |f| f.close(io) else |_| {}; var future_out_file = io.@"async"(Io.Dir.createFile, .{ .cwd(), io, "output.txt", .{} }); defer if (future_out_file.cancel(io)) |f| f.close(io) else |_| {}; const in_file = try future_in_file.@"await"(io); var buffer: [5000]u8 = undefined; const n = try in_file.readAll(io, &buffer); const contents = buffer[0..n]; for (contents) |*elem| elem.* = elem.* +% 1; const out_file = try future_out_file.@"await"(io); try out_file.writeAll(io, contents); } fn selectExample(io: Io, dir: Io.Dir) !void { var a_future = io.@"async"(Io.Dir.writeFile, .{ dir, io, .{ .sub_path = "a.txt", .data = "a contents" } }); defer _ = a_future.cancel(io) catch {}; var b_future = io.@"async"(Io.Dir.writeFile, .{ dir, io, .{ .sub_path = "b.txt", .data = "b contents" } }); defer _ = b_future.cancel(io) catch {}; var timeout = io.@"async"(Io.sleepDuration, .{ io, .ms(100) }); defer timeout.cancel(io) catch {}; switch (io.select(.{ .a_future = &a_future, .b_future = &b_future, .timeout = &timeout, })) { .a_future => |res| { std.log.info("a future won the race", .{}); try res; }, .b_future => |res| { std.log.info("b future won the race", .{}); try res; }, .timeout => { return error.Timeout; }, } } -
andrewrk revised this gist
Mar 31, 2025 . 1 changed file with 25 additions and 0 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -28,10 +28,35 @@ pub fn main() !void { var rot13 = io.@"async"(processTextFile, .{io}); var queue: Io.Queue(i32) = .init(&.{}); var producer = io.@"async"(producerRun, .{ io, &queue }); defer producer.cancel(io) catch {}; var consumer = io.@"async"(consumerRun, .{ io, &queue }); defer _ = consumer.cancel(io); const total = first_half.@"await"(io) + second_half.@"await"(io); std.log.info("total: {d}", .{total}); try rot13.@"await"(io); const consumer_sum = consumer.@"await"(io); std.log.info("consumer sum = {d}", .{consumer_sum}); } fn producerRun(io: Io, queue: *Io.Queue(i32)) !void { for (0..100) |i| { queue.putOne(io, @intCast(i * 2 + 1)); } queue.putOne(io, 999); } fn consumerRun(io: Io, queue: *Io.Queue(i32)) i32 { var sum: i32 = 0; while (true) { const n = queue.getOne(io); if (n == 999) return sum; sum += n; } } fn calcSum(start: usize, end: usize) usize { -
andrewrk revised this gist
Mar 30, 2025 . 1 changed file with 24 additions and 18 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -8,20 +8,20 @@ pub fn main() !void { const gpa = debug_allocator.allocator(); //const gpa = std.heap.smp_allocator; var thread_pool: std.Thread.Pool = undefined; try thread_pool.init(.{ .allocator = gpa, .n_jobs = std.Thread.getCpuCount() catch 1, }); defer thread_pool.deinit(); const io = thread_pool.io(); //var event_loop: std.Io.EventLoop = undefined; //try event_loop.init(gpa); //defer event_loop.deinit(); //const io = event_loop.io(); var first_half = io.@"async"(calcSum, .{ 0, 100 }); var second_half = io.@"async"(calcSum, .{ 100, 200 }); @@ -44,17 +44,23 @@ fn calcSum(start: usize, end: usize) usize { } fn processTextFile(io: Io) !void { var future_in_file = io.@"async"(Io.openFile, .{ io, std.fs.cwd(), "example.txt", @as(Io.OpenFlags, .{}), }); defer if (future_in_file.cancel(io)) |f| f.close() else |_| {}; var future_out_file = io.@"async"(Io.createFile, .{ io, std.fs.cwd(), "output.txt", @as(Io.CreateFlags, .{}), }); defer if (future_out_file.cancel(io)) |f| f.close() else |_| {}; const in_file = try future_in_file.@"await"(io); var buffer: [5000]u8 = undefined; const n = try io.readAll(in_file, &buffer); const contents = buffer[0..n]; for (contents) |*elem| elem.* = elem.* +% 1; const out_file = try future_out_file.@"await"(io); try io.writeAll(out_file, contents); } -
andrewrk renamed this gist
Mar 30, 2025 . 1 changed file with 0 additions and 0 deletions.There are no files selected for viewing
File renamed without changes. -
andrewrk revised this gist
Mar 29, 2025 . 1 changed file with 25 additions and 44 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -23,57 +23,38 @@ pub fn main() !void { const io = event_loop.io(); var first_half = io.@"async"(calcSum, .{ 0, 100 }); var second_half = io.@"async"(calcSum, .{ 100, 200 }); var rot13 = io.@"async"(processTextFile, .{io}); const total = first_half.@"await"(io) + second_half.@"await"(io); std.log.info("total: {d}", .{total}); try rot13.@"await"(io); } fn calcSum(start: usize, end: usize) usize { var sum: usize = 0; for (start..end) |i| { sum += i; } std.log.debug("calcSum returning {d}", .{sum}); return sum; } fn processTextFile(io: Io) !void { const f = try io.openFile(std.fs.cwd(), "example.txt", .{}); defer io.closeFile(f); var buffer: [5000]u8 = undefined; const n = try io.readAll(f, &buffer); const contents = buffer[0..n]; for (contents) |*elem| elem.* = elem.* +% 1; const out_file = try io.createFile(std.fs.cwd(), "output.txt", .{}); defer io.closeFile(out_file); try io.writeAll(out_file, contents); } -
andrewrk revised this gist
Mar 29, 2025 . 1 changed file with 33 additions and 222 deletions.There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -6,6 +6,7 @@ const Io = std.Io; pub fn main() !void { var debug_allocator: std.heap.DebugAllocator(.{}) = .init; const gpa = debug_allocator.allocator(); //const gpa = std.heap.smp_allocator; //var thread_pool: std.Thread.Pool = undefined; //try thread_pool.init(.{ @@ -14,38 +15,32 @@ pub fn main() !void { //}); //defer thread_pool.deinit(); //const io = thread_pool.io(); var event_loop: std.Io.EventLoop = undefined; try event_loop.init(gpa); defer event_loop.deinit(); const io = event_loop.io(); var first_half = io.@"async"(struct { begin: usize, end: usize, pub fn start(s: @This()) usize { var sum: usize = 0; for (s.begin..s.end) |i| { sum += i; } std.log.info("first half = {d}", .{sum}); return sum; } }, .{ .begin = 0, .end = 100, }); var second_half = io.@"async"(struct { pub fn start(s: @This()) usize { _ = s; var sum: usize = 0; @@ -55,214 +50,30 @@ pub fn main() !void { std.log.info("second half = {d}", .{sum}); return sum; } }, .{}); var rot13 = io.@"async"(struct { io: Io, pub fn start(s: @This()) !void { const f = try s.io.openFile(std.fs.cwd(), "example.txt", .{}); defer s.io.closeFile(f); var buffer: [5000]u8 = undefined; const n = try s.io.readAll(f, &buffer); const contents = buffer[0..n]; for (contents) |*elem| elem.* = elem.* +% 1; const out_file = try s.io.createFile(std.fs.cwd(), "output.txt", .{}); defer s.io.closeFile(out_file); try s.io.writeAll(out_file, contents); } }, .{ .io = io }); const total = first_half.@"await"(io) + second_half.@"await"(io); std.log.info("total: {d}", .{total}); try rot13.@"await"(io); } -
andrewrk created this gist
Mar 27, 2025 .There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters. Learn more about bidirectional Unicode charactersOriginal file line number Diff line number Diff line change @@ -0,0 +1,268 @@ const std = @import("std"); const assert = std.debug.assert; const Allocator = std.mem.Allocator; const Io = std.Io; pub fn main() !void { var debug_allocator: std.heap.DebugAllocator(.{}) = .init; const gpa = debug_allocator.allocator(); //var thread_pool: std.Thread.Pool = undefined; //try thread_pool.init(.{ // .allocator = gpa, // .n_jobs = std.Thread.getCpuCount() catch 1, //}); //defer thread_pool.deinit(); //const io: Io = .{ // .userdata = &thread_pool, // .vtable = &.{ // .@"async" = std.Thread.Pool.@"async", // .@"await" = std.Thread.Pool.@"await", // }, //}; var event_loop: EventLoop = undefined; event_loop.init(gpa); const io: Io = .{ .userdata = &event_loop, .vtable = &.{ .@"async" = green_async, .@"await" = green_await, }, }; var first_half = io.@"async"(@as(struct { pub fn start(s: @This()) usize { _ = s; var sum: usize = 0; for (0..100) |i| { sum += i; } std.log.info("first half = {d}", .{sum}); return sum; } }, .{})); var second_half = io.@"async"(@as(struct { pub fn start(s: @This()) usize { _ = s; var sum: usize = 0; for (100..200) |i| { sum += i; } std.log.info("second half = {d}", .{sum}); return sum; } }, .{})); const total = first_half.@"await"(io) + second_half.@"await"(io); std.log.info("total: {d}", .{total}); } const EventLoop = struct { gpa: Allocator, queue: std.DoublyLinkedList(void), free: std.DoublyLinkedList(void), main_fiber_buffer: [@sizeOf(Fiber) + max_result_len]u8 align(@alignOf(Fiber)), threadlocal var my_fiber: *Fiber = undefined; const max_result_len = 64; const min_stack_size = 4 * 1024 * 1024; const Fiber = struct { regs: Regs, awaiter: ?*Fiber, queue_node: std.DoublyLinkedList(void).Node, const finished: ?*Fiber = @ptrFromInt(std.mem.alignBackward(usize, std.math.maxInt(usize), @alignOf(Fiber))); fn resultPointer(f: *Fiber) [*]u8 { const base: [*]u8 = @ptrCast(f); return base + @sizeOf(Fiber); } fn stackEndPointer(f: *Fiber) [*]u8 { const base: [*]u8 = @ptrCast(f); return base + std.mem.alignForward( usize, @sizeOf(Fiber) + max_result_len + min_stack_size, std.heap.page_size_max, ); } }; fn init(el: *EventLoop, gpa: Allocator) void { el.* = .{ .gpa = gpa, .queue = .{}, .free = .{}, .main_fiber_buffer = undefined, }; my_fiber = @ptrCast(&el.main_fiber_buffer); } fn allocateFiber(el: *EventLoop, result_len: usize) error{OutOfMemory}!*Fiber { assert(result_len <= max_result_len); const free_node = el.free.pop() orelse { const n = std.mem.alignForward( usize, @sizeOf(Fiber) + max_result_len + min_stack_size, std.heap.page_size_max, ); return @alignCast(@ptrCast(try el.gpa.alignedAlloc(u8, @alignOf(Fiber), n))); }; return @fieldParentPtr("queue_node", free_node); } fn yield(el: *EventLoop, optional_fiber: ?*Fiber) void { if (optional_fiber) |fiber| { const old = &my_fiber.regs; my_fiber = fiber; contextSwitch(old, &fiber.regs); return; } if (el.queue.pop()) |node| { const fiber: *Fiber = @fieldParentPtr("queue_node", node); const old = &my_fiber.regs; my_fiber = fiber; contextSwitch(old, &fiber.regs); return; } @panic("everything is done"); } /// Equivalent to calling `yield` and then giving the fiber back to the event loop. fn exit(el: *EventLoop, optional_fiber: ?*Fiber) noreturn { yield(el, optional_fiber); @panic("TODO recycle the fiber"); } fn schedule(el: *EventLoop, fiber: *Fiber) void { el.queue.append(&fiber.queue_node); } fn myFiber(el: *EventLoop) *Fiber { _ = el; return my_fiber; } }; fn green_async( userdata: ?*anyopaque, eager_result: []u8, context: ?*anyopaque, start: *const fn (context: ?*anyopaque, result: *anyopaque) void, ) ?*std.Io.AnyFuture { const event_loop: *EventLoop = @alignCast(@ptrCast(userdata)); const fiber = event_loop.allocateFiber(eager_result.len) catch { start(context, eager_result.ptr); return null; }; fiber.awaiter = null; fiber.queue_node = .{ .data = {} }; const closure: *AsyncClosure = @ptrFromInt(std.mem.alignBackward( usize, @intFromPtr(fiber.stackEndPointer() - @sizeOf(AsyncClosure)), @alignOf(AsyncClosure), )); closure.* = .{ .event_loop = event_loop, .context = context, .fiber = fiber, .start = start, }; const stack_end_ptr: [*]align(16) usize = @alignCast(@ptrCast(closure)); (stack_end_ptr - 1)[0] = @intFromPtr(&AsyncClosure.call); (stack_end_ptr - 2)[0] = @intFromPtr(closure); (stack_end_ptr - 3)[0] = @intFromPtr(&popRet); fiber.regs = .{ .rsp = @intFromPtr(stack_end_ptr - 3), .r15 = 0, .r14 = 0, .r13 = 0, .r12 = 0, .rbx = 0, .rbp = 0, }; event_loop.schedule(fiber); return @ptrCast(fiber); } const AsyncClosure = struct { _: void align(16) = {}, event_loop: *EventLoop, context: ?*anyopaque, fiber: *EventLoop.Fiber, start: *const fn (context: ?*anyopaque, result: *anyopaque) void, fn call(closure: *AsyncClosure) callconv(.c) void { std.log.debug("wrap called in async", .{}); closure.start(closure.context, closure.fiber.resultPointer()); const awaiter = @atomicRmw(?*EventLoop.Fiber, &closure.fiber.awaiter, .Xchg, EventLoop.Fiber.finished, .seq_cst); closure.event_loop.exit(awaiter); } }; fn green_await(userdata: ?*anyopaque, any_future: *std.Io.AnyFuture, result: []u8) void { const event_loop: *EventLoop = @alignCast(@ptrCast(userdata)); const future_fiber: *EventLoop.Fiber = @alignCast(@ptrCast(any_future)); const result_src = future_fiber.resultPointer()[0..result.len]; const my_fiber = event_loop.myFiber(); const prev = @atomicRmw(?*EventLoop.Fiber, &future_fiber.awaiter, .Xchg, my_fiber, .seq_cst); if (prev == EventLoop.Fiber.finished) { @memcpy(result, result_src); return; } event_loop.yield(prev); // Resumed when the value is available. std.log.debug("yield returned in await", .{}); @memcpy(result, result_src); } const Regs = extern struct { rsp: usize, r15: usize, r14: usize, r13: usize, r12: usize, rbx: usize, rbp: usize, }; const contextSwitch: *const fn (old: *Regs, new: *Regs) callconv(.c) void = @ptrCast(&contextSwitch_naked); noinline fn contextSwitch_naked() callconv(.naked) void { asm volatile ( \\movq %%rsp, 0x00(%%rdi) \\movq %%r15, 0x08(%%rdi) \\movq %%r14, 0x10(%%rdi) \\movq %%r13, 0x18(%%rdi) \\movq %%r12, 0x20(%%rdi) \\movq %%rbx, 0x28(%%rdi) \\movq %%rbp, 0x30(%%rdi) \\ \\movq 0x00(%%rsi), %%rsp \\movq 0x08(%%rsi), %%r15 \\movq 0x10(%%rsi), %%r14 \\movq 0x18(%%rsi), %%r13 \\movq 0x20(%%rsi), %%r12 \\movq 0x28(%%rsi), %%rbx \\movq 0x30(%%rsi), %%rbp \\ \\ret ); } fn popRet() callconv(.naked) void { asm volatile ( \\pop %%rdi \\ret ); }