Skip to content

Instantly share code, notes, and snippets.

@Himujjal
Last active May 2, 2023 18:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Himujjal/410fde20f71467b59d996dc34921744d to your computer and use it in GitHub Desktop.
Save Himujjal/410fde20f71467b59d996dc34921744d to your computer and use it in GitHub Desktop.
Zig simple event loop javascript async functions
const std = @import("std");
const Data = struct {
val: u64,
frame: ?anyframe = null,
};
const Node = struct {
data: Data = undefined,
next: ?*Node = null,
prev: ?*Node = null,
pub fn print(self: *Node) void {
std.debug.print("Node{{ {d}, ", .{self.data.val});
if (self.data.frame != null) {
std.debug.print("{s} }}", .{" <frame>"});
} else {
std.debug.print("{s} }}", .{"<nullframe>"});
}
}
};
const RingBuffer = struct {
head: ?*Node = null,
allocator: std.mem.Allocator,
pub fn init(allocator: std.mem.Allocator) !RingBuffer {
return RingBuffer{ .allocator = allocator };
}
pub fn deinit(self: *RingBuffer) void {
var node: ?*Node = self.head;
while (node) |n| {
var next = n.next;
self.allocator.destroy(n);
node = next;
if (node == self.head) break;
}
}
pub fn hasSingleNode(self: *RingBuffer) bool {
return if (self.head) |h| h.next == h else false;
}
pub fn add(self: *RingBuffer, val: u64, frame: ?anyframe) !*Node {
var node = try self.allocator.create(Node);
node.* = Node{
.data = Data{ .val = val, .frame = frame },
.next = self.head,
};
if (self.head) |head| {
node.prev = head.prev;
head.prev.?.next = node;
head.prev = node;
} else {
node.next = node;
node.prev = node;
}
self.head = node;
return node;
}
pub fn getHead(self: *RingBuffer) ?Data {
return if (self.head) |head| head.data else null;
}
pub fn remove(self: *RingBuffer, node: *Node) !Data {
var data: Data = undefined;
if (node.next == node and node.prev == node) {
data = node.data;
self.head = null;
self.allocator.destroy(node);
return data;
}
data = node.data;
node.prev.?.next = node.next;
node.next.?.prev = node.prev;
if (self.head == node) {
self.head = node.next;
}
self.allocator.destroy(node);
return data;
}
pub fn print(self: *RingBuffer) void {
var head = self.head;
var node: ?*Node = head;
std.debug.print("\n", .{});
var i: u64 = 0;
while (node) |n| : (i += 1) {
n.print();
if (n.next == head) break;
std.debug.print(" <-> ", .{});
node = n.next;
}
std.debug.print("\n", .{});
}
};
const EventLoop = struct {
allocator: std.mem.Allocator,
ringBuffer: RingBuffer,
current: ?*Node = null,
fn init(allocator: std.mem.Allocator) !*EventLoop {
var loop = try allocator.create(EventLoop);
loop.allocator = allocator;
loop.ringBuffer = try RingBuffer.init(allocator);
return loop;
}
fn deinit(self: *EventLoop) void {
self.ringBuffer.deinit();
self.allocator.destroy(self);
}
fn push(self: *EventLoop, val: u64, frame: ?anyframe) !*Node {
return self.ringBuffer.add(val, frame);
}
fn pop(self: *EventLoop, node: *Node) !Data {
return try self.ringBuffer.remove(node);
}
fn advance(self: *EventLoop) void {
if (self.current) |c| self.current = c.next;
}
fn run(self: *EventLoop) !void {
self.current = self.ringBuffer.head;
while (self.current) |current| {
if (self.ringBuffer.head == null) break;
if (current.data.frame) |f| {
self.advance();
resume f;
} else {
self.advance();
}
}
}
};
const SetTimeoutCallbackType = fn (loop: *EventLoop) void;
fn setTimeout(loop: *EventLoop, callback: SetTimeoutCallbackType, timeout: i64) !void {
const curr_time = std.time.milliTimestamp();
var node = try loop.push(@intCast(u64, timeout), null);
while (true) {
const new_time = std.time.milliTimestamp();
if (new_time - curr_time >= timeout) {
break;
} else {
suspend {
node.data.frame = @frame();
}
}
}
_ = try loop.pop(node);
callback(loop);
}
fn setInterval(loop: *EventLoop, callback: SetTimeoutCallbackType, interval: i64) !void {
var _time: u64 = @intCast(u64, std.time.milliTimestamp());
var node = try loop.push(_time, null);
while (true) {
var curr_time = @intCast(u64, std.time.milliTimestamp());
const prev_time = node.data.val;
if (curr_time - prev_time >= interval) {
node.data.val = @intCast(u64, curr_time);
callback(loop);
}
suspend {
node.data.frame = @frame();
}
}
return node;
}
fn setTimeoutCallback1(_: *EventLoop) void {
std.debug.print("setTimeoutCallback 100!!\n", .{});
}
fn setTimeoutCallback2(_: *EventLoop) void {
std.debug.print("setTimeoutCallback 200!!\n", .{});
}
fn setIntervalCallback(_: *EventLoop) void {
std.debug.print("setIntervalCallback 1000!!\n", .{});
}
fn asyncMain(loop: *EventLoop) !void {
_ = async setTimeout(loop, setTimeoutCallback1, 100);
_ = async setTimeout(loop, setTimeoutCallback2, 200);
_ = async setInterval(loop, setIntervalCallback, 2000);
suspend {}
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
const allocator = gpa.allocator();
defer _ = gpa.deinit();
var loop = try EventLoop.init(allocator);
defer loop.deinit();
_ = async asyncMain(loop);
std.debug.print("start\n", .{});
defer std.debug.print("end\n", .{});
try loop.run();
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment