| ... | @@ -5,6 +5,8 @@ const expect = std.testing.expect; | ... | @@ -5,6 +5,8 @@ const expect = std.testing.expect; |
| 5 | | 5 | |
| 6 | /// Many producer, many consumer, non-allocating, thread-safe. | 6 | /// Many producer, many consumer, non-allocating, thread-safe. |
| 7 | /// Uses a mutex to protect access. | 7 | /// Uses a mutex to protect access. |
| | 8 | /// The queue does not manage ownership and the user is responsible to |
| | 9 | /// manage the storage of the nodes. |
| 8 | pub fn Queue(comptime T: type) type { | 10 | pub fn Queue(comptime T: type) type { |
| 9 | return struct { | 11 | return struct { |
| 10 | head: ?*Node, | 12 | head: ?*Node, |
| ... | @@ -14,6 +16,8 @@ pub fn Queue(comptime T: type) type { | ... | @@ -14,6 +16,8 @@ pub fn Queue(comptime T: type) type { |
| 14 | pub const Self = @This(); | 16 | pub const Self = @This(); |
| 15 | pub const Node = std.TailQueue(T).Node; | 17 | pub const Node = std.TailQueue(T).Node; |
| 16 | | 18 | |
| | 19 | /// Initializes a new queue. The queue does not provide a `deinit()` |
| | 20 | /// function, so the user must take care of cleaning up the queue elements. |
| 17 | pub fn init() Self { | 21 | pub fn init() Self { |
| 18 | return Self{ | 22 | return Self{ |
| 19 | .head = null, | 23 | .head = null, |
| ... | @@ -22,6 +26,8 @@ pub fn Queue(comptime T: type) type { | ... | @@ -22,6 +26,8 @@ pub fn Queue(comptime T: type) type { |
| 22 | }; | 26 | }; |
| 23 | } | 27 | } |
| 24 | | 28 | |
| | 29 | /// Appends `node` to the queue. |
| | 30 | /// The lifetime of `node` must be longer than lifetime of queue. |
| 25 | pub fn put(self: *Self, node: *Node) void { | 31 | pub fn put(self: *Self, node: *Node) void { |
| 26 | node.next = null; | 32 | node.next = null; |
| 27 | | 33 | |
| ... | @@ -38,6 +44,9 @@ pub fn Queue(comptime T: type) type { | ... | @@ -38,6 +44,9 @@ pub fn Queue(comptime T: type) type { |
| 38 | } | 44 | } |
| 39 | } | 45 | } |
| 40 | | 46 | |
| | 47 | /// Gets a previously inserted node or returns `null` if there is none. |
| | 48 | /// It is safe to `get()` a node from the queue while another thread tries |
| | 49 | /// to `remove()` the same node at the same time. |
| 41 | pub fn get(self: *Self) ?*Node { | 50 | pub fn get(self: *Self) ?*Node { |
| 42 | const held = self.mutex.acquire(); | 51 | const held = self.mutex.acquire(); |
| 43 | defer held.release(); | 52 | defer held.release(); |
| ... | @@ -71,7 +80,9 @@ pub fn Queue(comptime T: type) type { | ... | @@ -71,7 +80,9 @@ pub fn Queue(comptime T: type) type { |
| 71 | } | 80 | } |
| 72 | } | 81 | } |
| 73 | | 82 | |
| 74 | /// Thread-safe with get() and remove(). Returns whether node was actually removed. | 83 | /// Removes a node from the queue, returns whether node was actually removed. |
| | 84 | /// It is safe to `remove()` a node from the queue while another thread tries |
| | 85 | /// to `get()` the same node at the same time. |
| 75 | pub fn remove(self: *Self, node: *Node) bool { | 86 | pub fn remove(self: *Self, node: *Node) bool { |
| 76 | const held = self.mutex.acquire(); | 87 | const held = self.mutex.acquire(); |
| 77 | defer held.release(); | 88 | defer held.release(); |
| ... | @@ -95,16 +106,23 @@ pub fn Queue(comptime T: type) type { | ... | @@ -95,16 +106,23 @@ pub fn Queue(comptime T: type) type { |
| 95 | return true; | 106 | return true; |
| 96 | } | 107 | } |
| 97 | | 108 | |
| | 109 | /// Returns `true` if the queue is currently empty. |
| | 110 | /// Note that in a multi-consumer environment a return value of `false` |
| | 111 | /// does not mean that `get` will yield a non-`null` value! |
| 98 | pub fn isEmpty(self: *Self) bool { | 112 | pub fn isEmpty(self: *Self) bool { |
| 99 | const held = self.mutex.acquire(); | 113 | const held = self.mutex.acquire(); |
| 100 | defer held.release(); | 114 | defer held.release(); |
| 101 | return self.head == null; | 115 | return self.head == null; |
| 102 | } | 116 | } |
| 103 | | 117 | |
| | 118 | /// Dumps the contents of the queue to `stderr`. |
| 104 | pub fn dump(self: *Self) void { | 119 | pub fn dump(self: *Self) void { |
| 105 | self.dumpToStream(std.io.getStdErr().outStream()) catch return; | 120 | self.dumpToStream(std.io.getStdErr().outStream()) catch return; |
| 106 | } | 121 | } |
| 107 | | 122 | |
| | 123 | /// Dumps the contents of the queue to `stream`. |
| | 124 | /// Up to 4 elements from the head are dumped and the tail of the queue is |
| | 125 | /// dumped as well. |
| 108 | pub fn dumpToStream(self: *Self, stream: var) !void { | 126 | pub fn dumpToStream(self: *Self, stream: var) !void { |
| 109 | const S = struct { | 127 | const S = struct { |
| 110 | fn dumpRecursive( | 128 | fn dumpRecursive( |