|
11 | 11 | #include <executorch/runtime/platform/platform.h> |
12 | 12 | #include <gtest/gtest.h> |
13 | 13 |
|
| 14 | +#include <type_traits> |
| 15 | + |
14 | 16 | using namespace executorch::backends::cuda; |
15 | 17 | using namespace executorch::runtime; |
16 | 18 |
|
@@ -265,3 +267,60 @@ TEST_F(CUDAStreamGuardTest, NullStreamPointer) { |
265 | 267 | auto current_stream_result = getCurrentCUDAStream(0); |
266 | 268 | ASSERT_TRUE(current_stream_result.ok()); |
267 | 269 | } |
| 270 | + |
| 271 | +// CallerStreamGuard / getCallerStream select the backend's stream through pure |
| 272 | +// thread-local state and never touch a device. They still need the CUDA headers |
| 273 | +// to build, but no CUDA device at runtime, so they run outside the device-gated |
| 274 | +// fixture above using opaque (fake) stream values. |
| 275 | +namespace { |
| 276 | +// Opaque, distinct, never-dereferenced stream handles; using object addresses |
| 277 | +// avoids an int-to-pointer cast. |
| 278 | +cudaStream_t fake_stream(int index) { |
| 279 | + static char storage[3]; |
| 280 | + return reinterpret_cast<cudaStream_t>(&storage[index]); |
| 281 | +} |
| 282 | +} // namespace |
| 283 | + |
| 284 | +TEST(CallerStreamGuardTest, NoGuardReportsNullopt) { |
| 285 | + EXPECT_FALSE(getCallerStream().has_value()); |
| 286 | +} |
| 287 | + |
| 288 | +TEST(CallerStreamGuardTest, GuardSelectsThenRestores) { |
| 289 | + const cudaStream_t selected = fake_stream(0); |
| 290 | + { |
| 291 | + CallerStreamGuard guard(selected); |
| 292 | + EXPECT_EQ(getCallerStream(), selected); |
| 293 | + } |
| 294 | + EXPECT_FALSE(getCallerStream().has_value()); |
| 295 | +} |
| 296 | + |
| 297 | +TEST(CallerStreamGuardTest, NestedGuardsRestoreOuter) { |
| 298 | + const cudaStream_t outer = fake_stream(1); |
| 299 | + const cudaStream_t inner = fake_stream(2); |
| 300 | + CallerStreamGuard outer_guard(outer); |
| 301 | + { |
| 302 | + CallerStreamGuard inner_guard(inner); |
| 303 | + EXPECT_EQ(getCallerStream(), inner); |
| 304 | + } |
| 305 | + EXPECT_EQ(getCallerStream(), outer); |
| 306 | +} |
| 307 | + |
| 308 | +TEST(CallerStreamGuardCompileTimeTest, NotCopyable) { |
| 309 | + static_assert( |
| 310 | + !std::is_copy_constructible_v<CallerStreamGuard>, |
| 311 | + "CallerStreamGuard should not be copy constructible"); |
| 312 | + static_assert( |
| 313 | + !std::is_copy_assignable_v<CallerStreamGuard>, |
| 314 | + "CallerStreamGuard should not be copy assignable"); |
| 315 | +} |
| 316 | + |
| 317 | +TEST(CUDAStreamRegistryTest, PeekDoesNotCreateAndClearResets) { |
| 318 | + // An explicit index skips the cudaGetDevice path, so this needs no device; |
| 319 | + // use an index no other test touches. |
| 320 | + constexpr DeviceIndex kIdx = 5; |
| 321 | + EXPECT_FALSE(peekCurrentCUDAStream(kIdx).has_value()); |
| 322 | + ASSERT_EQ(setCurrentCUDAStream(fake_stream(0), kIdx), Error::Ok); |
| 323 | + EXPECT_EQ(peekCurrentCUDAStream(kIdx), fake_stream(0)); |
| 324 | + clearCurrentCUDAStream(kIdx); |
| 325 | + EXPECT_FALSE(peekCurrentCUDAStream(kIdx).has_value()); |
| 326 | +} |
0 commit comments