From 4ac36d094c06b04c1c71d972d3f3e1187bccea95 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Fri, 27 Apr 2018 19:27:58 -0400 Subject: [PATCH] add std.atomic.Stack and std.atomic.Queue --- CMakeLists.txt | 3 +++ std/atomic/index.zig | 7 +++++++ std/atomic/queue.zig | 37 ++++++++++++++++++++++++++++++++++++ std/atomic/stack.zig | 45 ++++++++++++++++++++++++++++++++++++++++++++ std/index.zig | 2 ++ 5 files changed, 94 insertions(+) create mode 100644 std/atomic/index.zig create mode 100644 std/atomic/queue.zig create mode 100644 std/atomic/stack.zig diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bf4bdd70..721690e9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -415,6 +415,9 @@ set(ZIG_CPP_SOURCES set(ZIG_STD_FILES "array_list.zig" + "atomic/index.zig" + "atomic/stack.zig" + "atomic/queue.zig" "base64.zig" "buf_map.zig" "buf_set.zig" diff --git a/std/atomic/index.zig b/std/atomic/index.zig new file mode 100644 index 000000000..9d556a641 --- /dev/null +++ b/std/atomic/index.zig @@ -0,0 +1,7 @@ +pub const Stack = @import("stack.zig").Stack; +pub const Queue = @import("queue.zig").Queue; + +test "std.atomic" { + _ = @import("stack.zig").Stack; + _ = @import("queue.zig").Queue; +} diff --git a/std/atomic/queue.zig b/std/atomic/queue.zig new file mode 100644 index 000000000..54981d4a6 --- /dev/null +++ b/std/atomic/queue.zig @@ -0,0 +1,37 @@ +/// Many reader, many writer, non-allocating, thread-safe, lock-free +pub fn Queue(comptime T: type) type { + return struct { + head: &Node, + tail: &Node, + root: Node, + + pub const Self = this; + + pub const Node = struct { + next: ?&Node, + data: T, + }; + + // TODO: well defined copy elision + pub fn init(self: &Self) void { + self.root.next = null; + self.head = &self.root; + self.tail = &self.root; + } + + pub fn put(self: &Self, node: &Node) void { + node.next = null; + + const tail = @atomicRmw(&Node, &self.tail, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + _ = @atomicRmw(?&Node, &tail.next, AtomicRmwOp.Xchg, node, AtomicOrder.SeqCst); + } + + pub fn get(self: &Self) ?&Node { + var head = @atomicLoad(&Node, &self.head, AtomicOrder.Acquire); + while (true) { + const node = head.next ?? return null; + head = @cmpxchgWeak(&Node, &self.head, head, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? return node; + } + } + }; +} diff --git a/std/atomic/stack.zig b/std/atomic/stack.zig new file mode 100644 index 000000000..4ceecb7b1 --- /dev/null +++ b/std/atomic/stack.zig @@ -0,0 +1,45 @@ +/// Many reader, many writer, non-allocating, thread-safe, lock-free +pub fn Stack(comptime T: type) type { + return struct { + root: ?&Node, + + pub const Self = this; + + pub const Node = struct { + next: ?&Node, + data: T, + }; + + pub fn init() Self { + return Self { + .root = null, + }; + } + + /// push operation, but only if you are the first item in the stack. if you did not succeed in + /// being the first item in the stack, returns the other item that was there. + pub fn pushFirst(self: &Self, node: &Node) ?&Node { + node.next = null; + return @cmpxchgStrong(?&Node, &self.root, null, node, AtomicOrder.AcqRel, AtomicOrder.AcqRel); + } + + pub fn push(self: &Self, node: &Node) void { + var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); + while (true) { + node.next = root; + root = @cmpxchgWeak(?&Node, &self.root, root, node, AtomicOrder.Release, AtomicOrder.Acquire) ?? break; + } + } + + pub fn pop(self: &Self) ?&Node { + var root = @atomicLoad(?&Node, &self.root, AtomicOrder.Acquire); + while (true) { + root = @cmpxchgWeak(?&Node, &self.root, root, (root ?? return null).next, AtomicOrder.Release, AtomicOrder.Acquire) ?? return root; + } + } + + pub fn isEmpty(self: &Self) bool { + return @atomicLoad(?&Node, &self.root, AtomicOrder.Relaxed) == null; + } + }; +} diff --git a/std/index.zig b/std/index.zig index 07c4360aa..d6a1e3c94 100644 --- a/std/index.zig +++ b/std/index.zig @@ -8,6 +8,7 @@ pub const HashMap = @import("hash_map.zig").HashMap; pub const LinkedList = @import("linked_list.zig").LinkedList; pub const IntrusiveLinkedList = @import("linked_list.zig").IntrusiveLinkedList; +pub const atomic = @import("atomic/index.zig"); pub const base64 = @import("base64.zig"); pub const build = @import("build.zig"); pub const c = @import("c/index.zig"); @@ -34,6 +35,7 @@ pub const zig = @import("zig/index.zig"); test "std" { // run tests from these + _ = @import("atomic/index.zig"); _ = @import("array_list.zig"); _ = @import("buf_map.zig"); _ = @import("buf_set.zig");