An asynchronous IO runtime
at 0acaa78fa8ffc6bc0217feb9b471dc504bcc6084 111 lines 4.3 kB view raw
1// This code is mostly a copy of the intrusive queue code from libxev. I've modified it to be a 2// doubly linked list that also ensures a certain state is set on each node when put into the list 3// 4// MIT License 5// 6// Copyright (c) 2023 Mitchell Hashimoto 7// 8// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and 9// associated documentation files (the "Software"), to deal in the Software without restriction, 10// including without limitation the rights to use, copy, modify, merge, publish, distribute, 11// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is 12// furnished to do so, subject to the following conditions: 13// 14// The above copyright notice and this permission notice shall be included in all copies or 15// substantial portions of the Software. 16// 17// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT 18// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 23const std = @import("std"); 24const assert = std.debug.assert; 25 26/// An intrusive queue implementation. The type T must have a field 27/// "next" of type `?*T` and a field "state" which is an enum with a value matching the passed in 28/// value 29pub fn Intrusive(comptime T: type, comptime state: @Type(.enum_literal)) type { 30 return struct { 31 const Self = @This(); 32 33 const set_state = state; 34 35 /// Head is the front of the queue and tail is the back of the queue. 36 head: ?*T = null, 37 tail: ?*T = null, 38 39 /// Enqueue a new element to the back of the queue. 40 pub fn push(self: *Self, v: *T) void { 41 assert(v.next == null); 42 v.state = set_state; 43 44 if (self.tail) |tail| { 45 // If we have elements in the queue, then we add a new tail. 46 tail.next = v; 47 v.prev = tail; 48 self.tail = v; 49 } else { 50 // No elements in the queue we setup the initial state. 51 self.head = v; 52 self.tail = v; 53 } 54 } 55 56 /// Dequeue the next element from the queue. 57 pub fn pop(self: *Self) ?*T { 58 // The next element is in "head". 59 const next = self.head orelse return null; 60 61 // If the head and tail are equal this is the last element 62 // so we also set tail to null so we can now be empty. 63 if (self.head == self.tail) self.tail = null; 64 65 // Head is whatever is next (if we're the last element, 66 // this will be null); 67 self.head = next.next; 68 if (self.head) |head| head.prev = null; 69 70 // We set the "next" field to null so that this element 71 // can be inserted again. 72 next.next = null; 73 next.prev = null; 74 return next; 75 } 76 77 /// Returns true if the queue is empty. 78 pub fn empty(self: Self) bool { 79 return self.head == null; 80 } 81 82 /// Removes the item from the queue. Asserts that Queue contains the item 83 pub fn remove(self: *Self, item: *T) void { 84 assert(self.hasItem(item)); 85 if (item.prev) |prev| prev.next = item.next else self.head = item.next; 86 87 if (item.next) |next| next.prev = item.prev else self.tail = item.prev; 88 89 item.prev = null; 90 item.next = null; 91 } 92 93 pub fn hasItem(self: Self, item: *T) bool { 94 var maybe_node = self.head; 95 while (maybe_node) |node| { 96 if (node == item) return true; 97 maybe_node = node.next; 98 } else return false; 99 } 100 101 pub fn len(self: Self) usize { 102 var count: usize = 0; 103 var maybe_node = self.head; 104 while (maybe_node) |node| { 105 count += 1; 106 maybe_node = node.next; 107 } 108 return count; 109 } 110 }; 111}