Last active
May 2, 2023 18:09
-
-
Save Himujjal/410fde20f71467b59d996dc34921744d to your computer and use it in GitHub Desktop.
Zig simple event loop javascript async functions
This file contains 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 characters
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