Skip to content

Commit bef6742

Browse files
authored
feat: add shared future (PROOF-928) (#253)
add shared future
1 parent 85ce9b5 commit bef6742

File tree

9 files changed

+332
-0
lines changed

9 files changed

+332
-0
lines changed

sxt/execution/async/BUILD

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,30 @@ sxt_cc_component(
143143
],
144144
)
145145

146+
sxt_cc_component(
147+
name = "shared_future",
148+
test_deps = [
149+
"//sxt/base/test:unit_test",
150+
],
151+
deps = [
152+
":future",
153+
":shared_future_state",
154+
],
155+
)
156+
157+
sxt_cc_component(
158+
name = "shared_future_state",
159+
test_deps = [
160+
"//sxt/base/test:unit_test",
161+
"//sxt/memory/resource:counting_resource",
162+
],
163+
deps = [
164+
":future",
165+
":promise",
166+
"//sxt/base/error:assert",
167+
],
168+
)
169+
146170
sxt_cc_component(
147171
name = "future_utility",
148172
impl_deps = [

sxt/execution/async/coroutine.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "sxt/execution/async/awaiter.h"
2222
#include "sxt/execution/async/coroutine_promise.h"
2323
#include "sxt/execution/async/future.h"
24+
#include "sxt/execution/async/shared_future.h"
2425

2526
namespace sxt {
2627
//--------------------------------------------------------------------------------------------------
@@ -38,6 +39,10 @@ auto operator co_await(Fut&& fut) noexcept {
3839
using T = typename Fut::value_type;
3940
return xena::awaiter<T>{xena::future<T>{std::move(fut)}};
4041
}
42+
43+
template <class T> xena::awaiter<T> operator co_await(const xena::shared_future<T>& fut) noexcept {
44+
return xena::awaiter<T>{fut.make_future()};
45+
}
4146
} // namespace sxt
4247

4348
//--------------------------------------------------------------------------------------------------

sxt/execution/async/coroutine.t.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ using namespace sxt::xena;
2828
static future<> f_v() noexcept;
2929
static future<int> f_i() noexcept;
3030
static future<int> f_i2() noexcept;
31+
static future<int> f_mi() noexcept;
3132
static future<int> f_dev(promise<int>& p);
3233

3334
TEST_CASE("futures interoperate with coroutines") {
@@ -57,6 +58,12 @@ TEST_CASE("futures interoperate with coroutines") {
5758
REQUIRE(res.value() == 124);
5859
REQUIRE(basdv::get_device() == 0);
5960
}
61+
62+
SECTION("we can await shared futures") {
63+
auto res = f_mi();
64+
REQUIRE(res.ready());
65+
REQUIRE(res.value() == 124);
66+
}
6067
}
6168

6269
static future<> f_v() noexcept { co_return; }
@@ -68,6 +75,11 @@ static future<int> f_i2() noexcept {
6875
co_return x + 1;
6976
}
7077

78+
static future<int> f_mi() noexcept {
79+
auto x = co_await shared_future<int>{f_i()};
80+
co_return x + 1;
81+
}
82+
7183
static future<int> f_dev(promise<int>& p) {
7284
auto device = basdv::get_num_devices() - 1u;
7385
basdv::active_device_guard active_guard{device};
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU.
2+
*
3+
* Copyright 2025-present Space and Time Labs, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include "sxt/execution/async/shared_future.h"
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU.
2+
*
3+
* Copyright 2025-present Space and Time Labs, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#pragma once
18+
19+
#include <cassert>
20+
#include <utility>
21+
22+
#include "sxt/execution/async/future.h"
23+
#include "sxt/execution/async/shared_future_state.h"
24+
25+
namespace sxt::xena {
26+
//--------------------------------------------------------------------------------------------------
27+
// shared_future
28+
//--------------------------------------------------------------------------------------------------
29+
template <class T = void> class shared_future {
30+
public:
31+
shared_future() noexcept = default;
32+
33+
shared_future(future<T>&& fut) noexcept {
34+
assert(fut.promise() != nullptr || fut.ready());
35+
state_ = std::make_shared<shared_future_state<T>>(std::move(fut));
36+
}
37+
38+
future<T> make_future() const noexcept {
39+
assert(state_ != nullptr);
40+
return state_->make_future();
41+
}
42+
43+
private:
44+
std::shared_ptr<shared_future_state<T>> state_;
45+
};
46+
} // namespace sxt::xena
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU.
2+
*
3+
* Copyright 2025-present Space and Time Labs, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include "sxt/execution/async/shared_future.h"
18+
19+
#include "sxt/base/test/unit_test.h"
20+
21+
using namespace sxt;
22+
using namespace sxt::xena;
23+
24+
TEST_CASE("we can manage a shared future") {
25+
promise<int> ps;
26+
shared_future<int> fut{future<int>{ps}};
27+
auto futp = fut.make_future();
28+
REQUIRE(!futp.ready());
29+
ps.set_value(123);
30+
REQUIRE(futp.ready());
31+
REQUIRE(futp.value() == 123);
32+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU.
2+
*
3+
* Copyright 2025-present Space and Time Labs, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include "sxt/execution/async/shared_future_state.h"
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU.
2+
*
3+
* Copyright 2025-present Space and Time Labs, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#pragma once
18+
19+
#include <list>
20+
#include <memory>
21+
#include <utility>
22+
23+
#include "sxt/base/error/assert.h"
24+
#include "sxt/execution/async/future.h"
25+
#include "sxt/execution/async/promise.h"
26+
#include "sxt/execution/async/task.h"
27+
28+
namespace sxt::xena {
29+
//--------------------------------------------------------------------------------------------------
30+
// shared_future_state
31+
//--------------------------------------------------------------------------------------------------
32+
/**
33+
* Manage state for a future that can be awaited multiple times.
34+
*
35+
* This is a highly simplified version of a shared_future derived from seastar
36+
*
37+
* See https://seastar.io/futures-promises/
38+
*/
39+
template <class T>
40+
class shared_future_state final : public task,
41+
public std::enable_shared_from_this<shared_future_state<T>> {
42+
public:
43+
shared_future_state() noexcept = default;
44+
shared_future_state(future<T>&& fut) noexcept : fut_{std::move(fut)} {
45+
SXT_DEBUG_ASSERT(fut_.promise() != nullptr || fut_.ready());
46+
}
47+
48+
shared_future_state(const shared_future_state&) = delete;
49+
shared_future_state(shared_future_state&&) = delete;
50+
51+
shared_future_state& operator=(const shared_future_state&) = delete;
52+
shared_future_state& operator=(shared_future_state&&) = delete;
53+
54+
future<T> make_future() noexcept {
55+
if (fut_.ready()) {
56+
if constexpr (std::is_same_v<T, void>) {
57+
return make_ready_future();
58+
} else {
59+
return make_ready_future<T>(T{fut_.value()});
60+
}
61+
}
62+
if (promises_.empty()) {
63+
fut_.promise()->set_continuation(*this);
64+
keep_alive_ = this->shared_from_this();
65+
}
66+
promises_.emplace_back();
67+
return future<T>{promises_.back()};
68+
};
69+
70+
private:
71+
void run_and_dispose() noexcept override {
72+
while (!promises_.empty()) {
73+
if constexpr (std::is_same_v<T, void>) {
74+
promises_.back().make_ready();
75+
} else {
76+
promises_.back().set_value(fut_.value());
77+
}
78+
promises_.pop_back();
79+
}
80+
keep_alive_.reset();
81+
}
82+
83+
std::shared_ptr<shared_future_state<T>> keep_alive_;
84+
future<T> fut_;
85+
std::list<promise<T>> promises_;
86+
};
87+
} // namespace sxt::xena
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/** Proofs GPU - Space and Time's cryptographic proof algorithms on the CPU and GPU.
2+
*
3+
* Copyright 2025-present Space and Time Labs, Inc.
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
#include "sxt/execution/async/shared_future_state.h"
18+
19+
#include "sxt/base/test/unit_test.h"
20+
#include "sxt/memory/resource/counting_resource.h"
21+
22+
using namespace sxt;
23+
using namespace sxt::xena;
24+
25+
TEST_CASE("we can manage shared future state") {
26+
promise<int> ps;
27+
auto s = std::make_shared<shared_future_state<int>>(future<int>{ps});
28+
29+
SECTION("we can create a future from a shared ready state") {
30+
ps.set_value(123);
31+
auto fut = s->make_future();
32+
REQUIRE(fut.ready());
33+
REQUIRE(fut.value() == 123);
34+
}
35+
36+
SECTION("we can create a future from a shared pending event") {
37+
auto fut = s->make_future();
38+
REQUIRE(!fut.ready());
39+
ps.set_value(123);
40+
REQUIRE(fut.ready());
41+
REQUIRE(fut.value() == 123);
42+
}
43+
44+
SECTION("we can create multiple futures from a shared state") {
45+
auto fut1 = s->make_future();
46+
REQUIRE(!fut1.ready());
47+
48+
auto fut2 = s->make_future();
49+
REQUIRE(!fut2.ready());
50+
51+
ps.set_value(123);
52+
53+
REQUIRE(fut1.ready());
54+
REQUIRE(fut1.value() == 123);
55+
56+
REQUIRE(fut2.ready());
57+
REQUIRE(fut2.value() == 123);
58+
}
59+
}
60+
61+
TEST_CASE("the lifetime of future states are properly managed") {
62+
memr::counting_resource resource;
63+
REQUIRE(resource.bytes_allocated() == 0);
64+
promise<int> ps;
65+
auto s = std::allocate_shared<shared_future_state<int>>(
66+
std::pmr::polymorphic_allocator<>{&resource}, future<int>{ps});
67+
REQUIRE(resource.bytes_allocated() > 0);
68+
69+
SECTION("shared future states are kept alive if there is a pending promise") {
70+
auto fut = s->make_future();
71+
s.reset();
72+
REQUIRE(resource.bytes_deallocated() == 0);
73+
REQUIRE(s == nullptr);
74+
REQUIRE(!fut.ready());
75+
ps.set_value(123);
76+
REQUIRE(fut.ready());
77+
REQUIRE(fut.value() == 123);
78+
REQUIRE(resource.bytes_deallocated() == resource.bytes_allocated());
79+
}
80+
}
81+
82+
TEST_CASE("we can manage void future states") {
83+
promise<> ps;
84+
auto s = std::make_shared<shared_future_state<void>>(future<void>{ps});
85+
86+
SECTION("we can create a future from a shared ready state") {
87+
auto fut = s->make_future();
88+
REQUIRE(!fut.ready());
89+
ps.make_ready();
90+
REQUIRE(fut.ready());
91+
}
92+
}

0 commit comments

Comments
 (0)