Skip to content

Commit 3a836a9

Browse files
authored
Merge pull request #2190 from fallintoplace/fix/nvexec-continues-on-error-cleanup
Destroy nvexec continues_on storage on CUDA errors
2 parents e46fd4b + bb13404 commit 3a836a9

2 files changed

Lines changed: 95 additions & 4 deletions

File tree

include/nvexec/stream/continues_on.cuh

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,11 +83,20 @@ namespace nv::execution::_strm
8383
storage->template emplace<tuple_t>(Tag(), static_cast<Args&&>(args)...);
8484
}
8585

86+
auto complete_error = [storage, &opstate = opstate_](cudaError_t status) noexcept
87+
{
88+
if constexpr (!construct_on_device)
89+
{
90+
storage->~storage_t();
91+
}
92+
opstate.propagate_completion_signal(STDEXEC::set_error, std::move(status));
93+
};
94+
8695
int dev_id{};
8796
if (cudaError_t status = STDEXEC_LOG_CUDA_API(cudaGetDevice(&dev_id));
8897
status != cudaSuccess)
8998
{
90-
opstate_.propagate_completion_signal(STDEXEC::set_error, std::move(status));
99+
complete_error(std::move(status));
91100
return;
92101
}
93102

@@ -98,7 +107,7 @@ namespace nv::execution::_strm
98107
dev_id));
99108
status != cudaSuccess)
100109
{
101-
opstate_.propagate_completion_signal(STDEXEC::set_error, std::move(status));
110+
complete_error(std::move(status));
102111
return;
103112
}
104113

@@ -110,7 +119,7 @@ namespace nv::execution::_strm
110119
cudaMemPrefetchAsync(storage, sizeof(storage_t), dev_id, stream));
111120
status != cudaSuccess)
112121
{
113-
opstate_.propagate_completion_signal(STDEXEC::set_error, std::move(status));
122+
complete_error(std::move(status));
114123
return;
115124
}
116125
}
@@ -123,7 +132,7 @@ namespace nv::execution::_strm
123132
if (cudaError_t status = STDEXEC_LOG_CUDA_API(cudaPeekAtLastError());
124133
status != cudaSuccess)
125134
{
126-
opstate_.propagate_completion_signal(STDEXEC::set_error, std::move(status));
135+
complete_error(std::move(status));
127136
return;
128137
}
129138
}

test/nvexec/continues_on.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,64 @@
11
#include <stdexec/execution.hpp>
22
#include <test_common/catch2.hpp>
33

4+
#include "common.cuh"
45
#include "nvexec/stream_context.cuh"
56

7+
#include <memory_resource>
8+
69
namespace
710
{
11+
class pinned_memory_resource_t : public std::pmr::memory_resource
12+
{
13+
void* do_allocate(std::size_t bytes, std::size_t) override
14+
{
15+
void* storage{};
16+
STDEXEC_TRY_CUDA_API(cudaMallocHost(&storage, bytes));
17+
return storage;
18+
}
19+
20+
void do_deallocate(void* storage, std::size_t, std::size_t) override
21+
{
22+
STDEXEC_ASSERT_CUDA_API(cudaFreeHost(storage));
23+
}
24+
25+
auto do_is_equal(std::pmr::memory_resource const & other) const noexcept -> bool override
26+
{
27+
return this == &other;
28+
}
29+
};
30+
31+
class destruction_probe_t
32+
{
33+
flags_storage_t<>::flags_t flags_;
34+
bool owns_{true};
35+
36+
public:
37+
destruction_probe_t() = delete;
38+
destruction_probe_t(destruction_probe_t const &) = delete;
39+
auto operator=(destruction_probe_t const &) -> destruction_probe_t& = delete;
40+
auto operator=(destruction_probe_t&&) -> destruction_probe_t& = delete;
41+
42+
__host__ __device__ explicit destruction_probe_t(flags_storage_t<>::flags_t flags)
43+
: flags_(flags)
44+
{}
45+
46+
__host__ __device__ destruction_probe_t(destruction_probe_t&& other)
47+
: flags_(other.flags_)
48+
, owns_(other.owns_)
49+
{
50+
other.owns_ = false;
51+
}
52+
53+
__host__ __device__ ~destruction_probe_t()
54+
{
55+
if (owns_)
56+
{
57+
flags_.set();
58+
}
59+
}
60+
};
61+
862
TEST_CASE("continues on after just", "[cuda][stream][adaptors][continues_on]")
963
{
1064
nvexec::stream_context ctx;
@@ -43,4 +97,32 @@ namespace
4397

4498
REQUIRE(result.has_value());
4599
}
100+
101+
TEST_CASE("continues_on destroys host-constructed storage after a CUDA error",
102+
"[cuda][stream][adaptors][continues_on]")
103+
{
104+
int device{};
105+
STDEXEC_TRY_CUDA_API(cudaGetDevice(&device));
106+
107+
int concurrent_managed_access{};
108+
STDEXEC_TRY_CUDA_API(cudaDeviceGetAttribute(&concurrent_managed_access,
109+
cudaDevAttrConcurrentManagedAccess,
110+
device));
111+
if (!concurrent_managed_access)
112+
{
113+
SKIP("device does not support concurrent managed access");
114+
}
115+
116+
pinned_memory_resource_t pinned_memory;
117+
nvexec::stream_context ctx;
118+
auto scheduler = ctx.get_scheduler();
119+
scheduler.ctx_.managed_resource_ = &pinned_memory;
120+
121+
flags_storage_t<> destructions{};
122+
auto sndr = STDEXEC::just(destruction_probe_t{destructions.get()})
123+
| STDEXEC::continues_on(scheduler);
124+
125+
REQUIRE_THROWS(STDEXEC::sync_wait(std::move(sndr)));
126+
REQUIRE(destructions.all_set_once());
127+
}
46128
} // namespace

0 commit comments

Comments
 (0)