aboutsummaryrefslogtreecommitdiff
path: root/lib/std/event/future.zig
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2019-09-26 01:54:45 -0400
committerGitHub <noreply@github.com>2019-09-26 01:54:45 -0400
commit68bb3945708c43109c48bda3664176307d45b62c (patch)
treeafb9731e10cef9d192560b52cd9ae2cf179775c4 /lib/std/event/future.zig
parent6128bc728d1e1024a178c16c2149f5b1a167a013 (diff)
parent4637e8f9699af9c3c6cf4df50ef5bb67c7a318a4 (diff)
downloadzig-68bb3945708c43109c48bda3664176307d45b62c.tar.gz
zig-68bb3945708c43109c48bda3664176307d45b62c.zip
Merge pull request #3315 from ziglang/mv-std-lib
Move std/ to lib/std/
Diffstat (limited to 'lib/std/event/future.zig')
-rw-r--r--lib/std/event/future.zig121
1 files changed, 121 insertions, 0 deletions
diff --git a/lib/std/event/future.zig b/lib/std/event/future.zig
new file mode 100644
index 0000000000..1e3508de41
--- /dev/null
+++ b/lib/std/event/future.zig
@@ -0,0 +1,121 @@
+const std = @import("../std.zig");
+const assert = std.debug.assert;
+const testing = std.testing;
+const builtin = @import("builtin");
+const Lock = std.event.Lock;
+const Loop = std.event.Loop;
+
+/// This is a value that starts out unavailable, until resolve() is called
+/// While it is unavailable, functions suspend when they try to get() it,
+/// and then are resumed when resolve() is called.
+/// At this point the value remains forever available, and another resolve() is not allowed.
+pub fn Future(comptime T: type) type {
+ return struct {
+ lock: Lock,
+ data: T,
+
+ /// TODO make this an enum
+ /// 0 - not started
+ /// 1 - started
+ /// 2 - finished
+ available: u8,
+
+ const Self = @This();
+ const Queue = std.atomic.Queue(anyframe);
+
+ pub fn init(loop: *Loop) Self {
+ return Self{
+ .lock = Lock.initLocked(loop),
+ .available = 0,
+ .data = undefined,
+ };
+ }
+
+ /// Obtain the value. If it's not available, wait until it becomes
+ /// available.
+ /// Thread-safe.
+ pub async fn get(self: *Self) *T {
+ if (@atomicLoad(u8, &self.available, .SeqCst) == 2) {
+ return &self.data;
+ }
+ const held = self.lock.acquire();
+ held.release();
+
+ return &self.data;
+ }
+
+ /// Gets the data without waiting for it. If it's available, a pointer is
+ /// returned. Otherwise, null is returned.
+ pub fn getOrNull(self: *Self) ?*T {
+ if (@atomicLoad(u8, &self.available, .SeqCst) == 2) {
+ return &self.data;
+ } else {
+ return null;
+ }
+ }
+
+ /// If someone else has started working on the data, wait for them to complete
+ /// and return a pointer to the data. Otherwise, return null, and the caller
+ /// should start working on the data.
+ /// It's not required to call start() before resolve() but it can be useful since
+ /// this method is thread-safe.
+ pub async fn start(self: *Self) ?*T {
+ const state = @cmpxchgStrong(u8, &self.available, 0, 1, .SeqCst, .SeqCst) orelse return null;
+ switch (state) {
+ 1 => {
+ const held = self.lock.acquire();
+ held.release();
+ return &self.data;
+ },
+ 2 => return &self.data,
+ else => unreachable,
+ }
+ }
+
+ /// Make the data become available. May be called only once.
+ /// Before calling this, modify the `data` property.
+ pub fn resolve(self: *Self) void {
+ const prev = @atomicRmw(u8, &self.available, .Xchg, 2, .SeqCst);
+ assert(prev == 0 or prev == 1); // resolve() called twice
+ Lock.Held.release(Lock.Held{ .lock = &self.lock });
+ }
+ };
+}
+
+test "std.event.Future" {
+ // https://github.com/ziglang/zig/issues/1908
+ if (builtin.single_threaded) return error.SkipZigTest;
+ // https://github.com/ziglang/zig/issues/3251
+ if (std.os.freebsd.is_the_target) return error.SkipZigTest;
+
+ const allocator = std.heap.direct_allocator;
+
+ var loop: Loop = undefined;
+ try loop.initMultiThreaded(allocator);
+ defer loop.deinit();
+
+ const handle = async testFuture(&loop);
+
+ loop.run();
+}
+
+fn testFuture(loop: *Loop) void {
+ var future = Future(i32).init(loop);
+
+ var a = async waitOnFuture(&future);
+ var b = async waitOnFuture(&future);
+ resolveFuture(&future);
+
+ const result = (await a) + (await b);
+
+ testing.expect(result == 12);
+}
+
+fn waitOnFuture(future: *Future(i32)) i32 {
+ return future.get().*;
+}
+
+fn resolveFuture(future: *Future(i32)) void {
+ future.data = 6;
+ future.resolve();
+}