1 static const char help[] = "Tests PetscDeviceContextMarkIntentFromID().\n\n"; 2 3 #include "petscdevicetestcommon.h" 4 #include <petscviewer.h> 5 6 #include <petsc/private/cpp/type_traits.hpp> 7 #include <petsc/private/cpp/array.hpp> 8 9 #include <cstdarg> // std::va_list 10 #include <vector> // std:vector 11 #include <unordered_map> // std::take_a_wild_guess 12 #include <algorithm> // std::find 13 #include <iterator> // std::distance, std::next 14 15 struct Marker { 16 PetscMemoryAccessMode mode{}; 17 18 PetscErrorCode operator()(PetscDeviceContext dctx, PetscContainer cont) const noexcept 19 { 20 const auto obj = reinterpret_cast<PetscObject>(cont); 21 PetscObjectId id = 0; 22 const char *name = nullptr; 23 24 PetscFunctionBegin; 25 PetscCall(PetscObjectGetId(obj, &id)); 26 PetscCall(PetscObjectGetName(obj, &name)); 27 PetscCall(PetscDeviceContextMarkIntentFromID(dctx, id, this->mode, name)); 28 PetscFunctionReturn(PETSC_SUCCESS); 29 } 30 }; 31 32 static constexpr auto mem_read = Marker{PETSC_MEMORY_ACCESS_READ}; 33 static constexpr auto mem_write = Marker{PETSC_MEMORY_ACCESS_WRITE}; 34 static constexpr auto mem_read_write = Marker{PETSC_MEMORY_ACCESS_READ_WRITE}; 35 static constexpr auto mark_funcs = Petsc::util::make_array(mem_read, mem_write, mem_read_write); 36 37 static PetscErrorCode MarkedObjectMapView(PetscViewer vwr, std::size_t nkeys, const PetscObjectId *keys, const PetscMemoryAccessMode *modes, const std::size_t *ndeps, const PetscEvent **dependencies) 38 { 39 PetscFunctionBegin; 40 if (!vwr) PetscCall(PetscViewerASCIIGetStdout(PETSC_COMM_WORLD, &vwr)); 41 PetscCall(PetscViewerFlush(vwr)); 42 PetscCall(PetscViewerASCIIPushSynchronized(vwr)); 43 PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "Marked Object Map:\n")); 44 PetscCall(PetscViewerASCIIPushTab(vwr)); 45 PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "size: %zu\n", nkeys)); 46 PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "entries:\n")); 47 PetscCall(PetscViewerASCIIPushTab(vwr)); 48 for (std::size_t i = 0; i < nkeys; ++i) { 49 PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "id %" PetscInt64_FMT " -> {\n", keys[i])); 50 PetscCall(PetscViewerASCIIPushTab(vwr)); 51 PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "mode: %s\n", PetscMemoryAccessModeToString(modes[i]))); 52 PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "dependencies:\n")); 53 PetscCall(PetscViewerASCIIPushTab(vwr)); 54 for (std::size_t j = 0; j < ndeps[i]; ++j) { 55 const auto event = dependencies[i][j]; 56 57 PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "event %zu {dtype: %s, dctx_id: %" PetscInt64_FMT ", dctx_state: %" PetscInt64_FMT ", data: %p, destroy: %p}\n", j, PetscDeviceTypes[event->dtype], event->dctx_id, event->dctx_state, event->data, 58 reinterpret_cast<void *>(event->destroy))); 59 } 60 PetscCall(PetscViewerASCIIPopTab(vwr)); 61 PetscCall(PetscViewerASCIIPopTab(vwr)); 62 PetscCall(PetscViewerASCIISynchronizedPrintf(vwr, "}\n")); 63 } 64 PetscCall(PetscViewerASCIIPopTab(vwr)); 65 PetscCall(PetscViewerASCIIPopTab(vwr)); 66 PetscCall(PetscViewerFlush(vwr)); 67 PetscCall(PetscViewerASCIIPopSynchronized(vwr)); 68 PetscFunctionReturn(PETSC_SUCCESS); 69 } 70 71 PETSC_ATTRIBUTE_FORMAT(10, 11) 72 static PetscErrorCode CheckMarkedObjectMap_Private(PetscBool cond, const char cond_str[], MPI_Comm comm, PetscDeviceContext dctx, std::size_t nkeys, const PetscObjectId *keys, const PetscMemoryAccessMode *modes, const std::size_t *ndeps, const PetscEvent **dependencies, const char *format, ...) 73 { 74 PetscFunctionBegin; 75 if (PetscUnlikely(!cond)) { 76 std::array<char, 2048> buf; 77 std::va_list argp; 78 std::size_t len; 79 PetscViewer vwr; 80 81 PetscCallCXX(buf.fill(0)); 82 va_start(argp, format); 83 PetscCall(PetscVSNPrintf(buf.data(), buf.size(), format, &len, argp)); 84 va_end(argp); 85 PetscCall(PetscViewerASCIIGetStdout(comm, &vwr)); 86 if (dctx) PetscCall(PetscDeviceContextView(dctx, vwr)); 87 PetscCall(MarkedObjectMapView(vwr, nkeys, keys, modes, ndeps, dependencies)); 88 SETERRQ(comm, PETSC_ERR_PLIB, "Condition '%s' failed, marked object map in corrupt state: %s", cond_str, buf.data()); 89 } 90 PetscFunctionReturn(PETSC_SUCCESS); 91 } 92 #define CheckMarkedObjectMap(__cond__, ...) CheckMarkedObjectMap_Private((PetscBool)(!!(__cond__)), PetscStringize(__cond__), PETSC_COMM_SELF, dctx, nkeys, keys, modes, ndeps, const_cast<const PetscEvent **>(dependencies), __VA_ARGS__); 93 94 static PetscErrorCode TestAllCombinations(PetscDeviceContext dctx, const std::vector<PetscContainer> &cont) 95 { 96 std::vector<PetscObjectId> cont_ids; 97 PetscObjectId dctx_id; 98 PetscDeviceType dtype; 99 100 PetscFunctionBegin; 101 PetscCallCXX(cont_ids.reserve(cont.size())); 102 for (auto &&c : cont) { 103 PetscObjectId id; 104 105 PetscCall(PetscObjectGetId((PetscObject)c, &id)); 106 PetscCallCXX(cont_ids.emplace_back(id)); 107 } 108 PetscCall(PetscObjectGetId(PetscObjectCast(dctx), &dctx_id)); 109 PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype)); 110 for (auto &&func_i : mark_funcs) { 111 for (auto &&func_j : mark_funcs) { 112 for (auto it = cont.cbegin(), next = std::next(it); it != cont.cend(); ++it, ++next) { 113 std::vector<int> found_keys; 114 std::size_t nkeys; 115 PetscObjectId *keys; 116 PetscMemoryAccessMode *modes; 117 std::size_t *ndeps; 118 PetscEvent **dependencies; 119 120 if (next >= cont.cend()) next = cont.cbegin(); 121 PetscCall(func_i(dctx, *it)); 122 PetscCall(func_j(dctx, *next)); 123 PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies)); 124 PetscCallCXX(found_keys.resize(nkeys)); 125 { 126 // The underlying marked object map is *unordered*, and hence the order in which we 127 // get the keys is not necessarily the same as the order of operations. This is 128 // confounded by the fact that k and knext are not necessarily "linear", i.e. k could 129 // be 2 while knext is 0. So we need to map these back to linear space so we can loop 130 // over them. 131 const auto keys_end = keys + nkeys; 132 const auto num_expected_keys = std::min(cont.size(), static_cast<std::size_t>(2)); 133 const auto check_applied_mode = [&](PetscContainer container, PetscMemoryAccessMode mode) { 134 std::ptrdiff_t key_idx = 0; 135 PetscObjectId actual_key; 136 137 PetscFunctionBegin; 138 PetscCall(PetscObjectGetId((PetscObject)container, &actual_key)); 139 // search the list of keys from the map for the selected key 140 key_idx = std::distance(keys, std::find(keys, keys_end, actual_key)); 141 PetscCheck(key_idx >= 0, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Key index %" PetscCount_FMT " < 0, this indicates keys_begin > keys_end?", key_idx); 142 found_keys[key_idx]++; 143 PetscCall(CheckMarkedObjectMap(key_idx < std::distance(keys, keys_end), "marked object map could not find expected key %" PetscInt64_FMT, actual_key)); 144 // OK found it, now check the rest of the entries are as we expect them to be 145 PetscCall(CheckMarkedObjectMap(modes[key_idx] == mode, "unexpected mode %s, expected %s", PetscMemoryAccessModeToString(modes[key_idx]), PetscMemoryAccessModeToString(mode))); 146 PetscCall(CheckMarkedObjectMap(ndeps[key_idx] == 1, "unexpected number of dependencies %zu, expected 1", ndeps[key_idx])); 147 PetscCall(CheckMarkedObjectMap(dependencies[key_idx][0]->dtype == dtype, "unexpected device type on event: %s, expected %s", PetscDeviceTypes[dependencies[key_idx][0]->dtype], PetscDeviceTypes[dtype])); 148 PetscFunctionReturn(PETSC_SUCCESS); 149 }; 150 151 // if it == next, then even though we might num_expected_keys keys we never "look 152 // for" the missing key 153 PetscCheck(cont.size() == 1 || it != next, PETSC_COMM_SELF, PETSC_ERR_PLIB, "Test assumes different inputs, otherwise key check may fail (cont.size(): %zu, it != next: %s)", cont.size(), it != next ? "true" : "false"); 154 PetscCall(CheckMarkedObjectMap(nkeys == num_expected_keys, "marked object map has %zu keys expected %zu", nkeys, num_expected_keys)); 155 // check that each function properly applied its mode, it == next if cont.size() = 1, 156 // i.e. testing identity 157 if (it != next) PetscCall(check_applied_mode(*it, func_i.mode)); 158 PetscCall(check_applied_mode(*next, func_j.mode)); 159 } 160 // Check that the map contained only keys we were looking for. Any extra keys will have 161 // zero find count 162 for (auto it = found_keys.cbegin(); it != found_keys.cend(); ++it) PetscCall(CheckMarkedObjectMap(*it > 0, "Marked Object Map has extra object entry: id %" PetscInt64_FMT, keys[std::distance(found_keys.cbegin(), it)])); 163 164 PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies)); 165 166 PetscCall(PetscDeviceContextSynchronize(dctx)); 167 PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies)); 168 PetscCall(CheckMarkedObjectMap(nkeys == 0, "synchronizing device context did not empty dependency map, have %zu keys", nkeys)); 169 PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies)); 170 } 171 } 172 } 173 PetscCall(PetscDeviceContextSynchronize(dctx)); 174 PetscFunctionReturn(PETSC_SUCCESS); 175 } 176 177 template <typename... T> 178 PETSC_NODISCARD static std::pair<PetscObjectId, std::pair<PetscMemoryAccessMode, std::vector<PetscDeviceContext>>> make_map_entry(PetscObjectId id, PetscMemoryAccessMode mode, T &&...dctxs) 179 { 180 return { 181 id, {mode, {std::forward<T>(dctxs)...}} 182 }; 183 } 184 185 static PetscErrorCode CheckMapEqual(std::unordered_map<PetscObjectId, std::pair<PetscMemoryAccessMode, std::vector<PetscDeviceContext>>> expected_map) 186 { 187 std::size_t nkeys; 188 PetscObjectId *keys; 189 PetscMemoryAccessMode *modes; 190 std::size_t *ndeps; 191 PetscEvent **dependencies; 192 PetscDeviceContext dctx = nullptr; 193 194 PetscFunctionBegin; 195 PetscCall(PetscGetMarkedObjectMap_Internal(&nkeys, &keys, &modes, &ndeps, &dependencies)); 196 { 197 const auto key_end = keys + nkeys; 198 auto mode_it = modes; 199 auto ndep_it = ndeps; 200 auto dep_it = dependencies; 201 202 for (auto key_it = keys; key_it != key_end; ++key_it, ++mode_it, ++ndep_it, ++dep_it) { 203 const auto found_it = expected_map.find(*key_it); 204 205 PetscCall(CheckMarkedObjectMap(found_it != expected_map.cend(), "marked object map did not contain key %" PetscInt64_FMT, *key_it)); 206 { 207 // must do these here since found_it may be expected_map.cend() 208 const auto &expected_mode = found_it->second.first; 209 const auto &expected_dctxs = found_it->second.second; 210 auto sub_dep_it = *dep_it; 211 212 PetscCall(CheckMarkedObjectMap(expected_mode == *mode_it, "unexpected mode %s, expected %s", PetscMemoryAccessModeToString(expected_mode), PetscMemoryAccessModeToString(*mode_it))); 213 PetscCall(CheckMarkedObjectMap(expected_dctxs.size() == *ndep_it, "unexpected number of dependencies %zu, expected %zu", *ndep_it, expected_dctxs.size())); 214 // purposefully hide "dctx" with the loop variable, so we get more detailed output in 215 // the error message 216 for (auto &&dctx : expected_dctxs) { 217 const auto event = *sub_dep_it; 218 PetscDeviceType dtype; 219 PetscObjectId id; 220 221 PetscCall(PetscDeviceContextGetDeviceType(dctx, &dtype)); 222 PetscCall(PetscObjectGetId(PetscObjectCast(dctx), &id)); 223 PetscCall(CheckMarkedObjectMap(event->dtype == dtype, "unexpected device type on event: %s, expected %s", PetscDeviceTypes[event->dtype], PetscDeviceTypes[dtype])); 224 PetscCall(CheckMarkedObjectMap(event->dctx_id == id, "unexpected dctx id on event: %" PetscInt64_FMT ", expected %" PetscInt64_FMT, event->dctx_id, id)); 225 ++sub_dep_it; 226 } 227 } 228 // remove the found iterator from the map, this ensure we either run out of map (which is 229 // caught by the first check in the loop), or we run out of keys to check, which is 230 // caught in the end of the loop 231 PetscCallCXX(expected_map.erase(found_it)); 232 } 233 } 234 PetscCall(CheckMarkedObjectMap(expected_map.empty(), "Not all keys in marked object map accounted for!")); 235 PetscCall(PetscRestoreMarkedObjectMap_Internal(nkeys, &keys, &modes, &ndeps, &dependencies)); 236 PetscFunctionReturn(PETSC_SUCCESS); 237 } 238 239 int main(int argc, char *argv[]) 240 { 241 PetscContainer x, y, z; 242 PetscObjectId x_id, y_id, z_id; 243 PetscDeviceContext dctx_a, dctx_b, dctx_c; 244 auto container_view = PETSC_FALSE; 245 const auto create_container = [&](PetscContainer *c, const char name[], PetscObjectId *id) { 246 PetscFunctionBegin; 247 PetscCall(PetscContainerCreate(PETSC_COMM_WORLD, c)); 248 PetscCall(PetscObjectSetName((PetscObject)(*c), name)); 249 PetscCall(PetscObjectGetId((PetscObject)(*c), id)); 250 if (container_view) PetscCall(PetscPrintf(PETSC_COMM_WORLD, "Container '%s' -> id %" PetscInt64_FMT "\n", name, *id)); 251 PetscFunctionReturn(PETSC_SUCCESS); 252 }; 253 const auto sync_all = [&] { 254 PetscFunctionBegin; 255 for (auto &&ctx : {dctx_a, dctx_b, dctx_c}) PetscCall(PetscDeviceContextSynchronize(ctx)); 256 PetscFunctionReturn(PETSC_SUCCESS); 257 }; 258 259 PetscFunctionBeginUser; 260 PetscCall(PetscInitialize(&argc, &argv, nullptr, help)); 261 262 PetscOptionsBegin(PETSC_COMM_WORLD, nullptr, "Test Options", "Sys"); 263 PetscCall(PetscOptionsBool("-container_view", "View container names and ID's", nullptr, container_view, &container_view, nullptr)); 264 PetscOptionsEnd(); 265 266 PetscCall(create_container(&x, "x", &x_id)); 267 PetscCall(create_container(&y, "y", &y_id)); 268 PetscCall(create_container(&z, "z", &z_id)); 269 270 PetscCall(PetscDeviceContextCreate(&dctx_a)); 271 PetscCall(PetscObjectSetName(PetscObjectCast(dctx_a), "dctx_a")); 272 PetscCall(PetscDeviceContextSetStreamType(dctx_a, PETSC_STREAM_DEFAULT_BLOCKING)); 273 PetscCall(PetscDeviceContextSetFromOptions(PETSC_COMM_WORLD, dctx_a)); 274 PetscCall(PetscDeviceContextDuplicate(dctx_a, &dctx_b)); 275 PetscCall(PetscObjectSetName(PetscObjectCast(dctx_b), "dctx_b")); 276 PetscCall(PetscDeviceContextDuplicate(dctx_a, &dctx_c)); 277 PetscCall(PetscObjectSetName(PetscObjectCast(dctx_c), "dctx_c")); 278 PetscCall(PetscDeviceContextViewFromOptions(dctx_a, nullptr, "-dctx_a_view")); 279 PetscCall(PetscDeviceContextViewFromOptions(dctx_b, nullptr, "-dctx_b_view")); 280 PetscCall(PetscDeviceContextViewFromOptions(dctx_c, nullptr, "-dctx_c_view")); 281 282 // ensure they are all idle 283 PetscCall(sync_all()); 284 PetscCall(CheckMapEqual({})); 285 286 // do the bulk combination tests, these test only the very basic combinations for simple 287 // correctness 288 PetscCall(TestAllCombinations(dctx_a, {x})); 289 PetscCall(TestAllCombinations(dctx_a, {x, y, z})); 290 291 // Now do some specific tests, these should test more complicated scenarios. First and 292 // foremost, ensure they are all idle, and that it does not change the map 293 PetscCall(sync_all()); 294 // Map should be empty 295 PetscCall(CheckMapEqual({})); 296 297 // Syncing again shouldn't magically fill the map back up 298 PetscCall(sync_all()); 299 PetscCall(CheckMapEqual({})); 300 301 const auto test_multiple_readers = [&](std::array<PetscDeviceContext, 2> readers, std::size_t sync_idx) { 302 // the reader which synchronizes 303 const auto sync_reader = readers[sync_idx]; 304 // the reader that will remain in the map after sync_reader synchronizes 305 const auto remain_idx = sync_idx + 1 >= readers.size() ? 0 : sync_idx + 1; 306 const auto remain_reader = readers[remain_idx]; 307 308 PetscFunctionBegin; 309 for (auto &&ctx : readers) PetscCall(mem_read(ctx, x)); 310 for (auto &&ctx : readers) PetscCall(mem_read(ctx, y)); 311 PetscCall(CheckMapEqual({ 312 make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, readers[0], readers[1]), 313 make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, readers[0], readers[1]), 314 })); 315 // synchronizing sync_reader should remove it from the dependency list -- but leave remain_reader 316 // intact 317 PetscCall(PetscDeviceContextSynchronize(sync_reader)); 318 PetscCall(CheckMapEqual({ 319 make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, remain_reader), 320 make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, remain_reader), 321 })); 322 PetscCall(PetscDeviceContextSynchronize(remain_reader)); 323 PetscCall(CheckMapEqual({})); 324 PetscFunctionReturn(PETSC_SUCCESS); 325 }; 326 327 // Test that multiple readers can simultaneously read -- even if one of them is synchronized 328 PetscCall(test_multiple_readers({dctx_a, dctx_b}, 0)); 329 PetscCall(test_multiple_readers({dctx_a, dctx_b}, 1)); 330 331 // Test that sync of unrelated ctx does not affect the map 332 PetscCall(mem_read(dctx_a, x)); 333 PetscCall(mem_read(dctx_b, y)); 334 PetscCall(PetscDeviceContextSynchronize(dctx_c)); 335 // clang-format off 336 PetscCall(CheckMapEqual({ 337 make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a), 338 make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, dctx_b) 339 })); 340 // clang-format on 341 PetscCall(PetscDeviceContextSynchronize(dctx_a)); 342 PetscCall(PetscDeviceContextSynchronize(dctx_b)); 343 // Now the map is empty again 344 PetscCall(CheckMapEqual({})); 345 346 // Test another context writing over two reads 347 PetscCall(mem_read(dctx_a, x)); 348 PetscCall(mem_read(dctx_b, x)); 349 // C writing should kick out both A and B 350 PetscCall(mem_write(dctx_c, x)); 351 PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_c)})); 352 PetscCall(PetscDeviceContextSynchronize(dctx_c)); 353 PetscCall(CheckMapEqual({})); 354 355 // Test that write and synchronize does not interfere with unrelated read 356 PetscCall(mem_read_write(dctx_a, x)); 357 PetscCall(mem_read(dctx_a, y)); 358 PetscCall(mem_read_write(dctx_b, x)); 359 PetscCall(mem_read(dctx_b, y)); 360 // Synchronizing B here must clear everything *but* A's read on Y! 361 PetscCall(PetscDeviceContextSynchronize(dctx_b)); 362 PetscCall(CheckMapEqual({make_map_entry(y_id, PETSC_MEMORY_ACCESS_READ, dctx_a)})); 363 PetscCall(PetscDeviceContextSynchronize(dctx_a)); 364 // Now the map is empty again 365 PetscCall(CheckMapEqual({})); 366 367 // Test that implicit stream-dependencies are properly tracked 368 PetscCall(mem_read(dctx_a, x)); 369 PetscCall(mem_read(dctx_b, y)); 370 // A waits for B 371 PetscCall(PetscDeviceContextWaitForContext(dctx_a, dctx_b)); 372 // Because A waits on B, synchronizing A implicitly implies B read must have finished so the 373 // map must be empty 374 PetscCall(PetscDeviceContextSynchronize(dctx_a)); 375 PetscCall(CheckMapEqual({})); 376 377 PetscCall(mem_write(dctx_a, x)); 378 PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_a)})); 379 PetscCall(PetscDeviceContextWaitForContext(dctx_b, dctx_a)); 380 PetscCall(PetscDeviceContextWaitForContext(dctx_c, dctx_b)); 381 // We have created the chain C -> B -> A, so synchronizing C should trickle down to synchronize and 382 // remove A from the map 383 PetscCall(PetscDeviceContextSynchronize(dctx_c)); 384 PetscCall(CheckMapEqual({})); 385 386 // Test that superfluous stream-dependencies are properly ignored 387 PetscCall(mem_read(dctx_a, x)); 388 PetscCall(mem_read(dctx_b, y)); 389 PetscCall(PetscDeviceContextWaitForContext(dctx_c, dctx_b)); 390 // C waited on B, so synchronizing C should remove B from the map but *not* remove A 391 PetscCall(PetscDeviceContextSynchronize(dctx_c)); 392 PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a)})); 393 PetscCall(PetscDeviceContextSynchronize(dctx_a)); 394 PetscCall(CheckMapEqual({})); 395 396 // Test that read->write correctly wipes out the map 397 PetscCall(mem_read(dctx_a, x)); 398 PetscCall(mem_read(dctx_b, x)); 399 PetscCall(mem_read(dctx_c, x)); 400 PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_READ, dctx_a, dctx_b, dctx_c)})); 401 PetscCall(mem_write(dctx_a, x)); 402 PetscCall(CheckMapEqual({make_map_entry(x_id, PETSC_MEMORY_ACCESS_WRITE, dctx_a)})); 403 PetscCall(PetscDeviceContextSynchronize(dctx_a)); 404 PetscCall(CheckMapEqual({})); 405 406 PetscCall(PetscDeviceContextDestroy(&dctx_a)); 407 PetscCall(PetscDeviceContextDestroy(&dctx_b)); 408 PetscCall(PetscDeviceContextDestroy(&dctx_c)); 409 410 PetscCall(PetscContainerDestroy(&x)); 411 PetscCall(PetscContainerDestroy(&y)); 412 PetscCall(PetscContainerDestroy(&z)); 413 PetscCall(PetscPrintf(PETSC_COMM_WORLD, "EXIT_SUCCESS\n")); 414 PetscCall(PetscFinalize()); 415 return 0; 416 } 417 418 /*TEST 419 420 testset: 421 requires: cxx 422 output_file: ./output/ExitSuccess.out 423 test: 424 requires: !device 425 suffix: host_no_device 426 test: 427 requires: device 428 args: -default_device_type host 429 suffix: host_with_device 430 test: 431 requires: cuda 432 args: -default_device_type cuda 433 suffix: cuda 434 test: 435 requires: hip 436 args: -default_device_type hip 437 suffix: hip 438 test: 439 requires: sycl 440 args: -default_device_type sycl 441 suffix: sycl 442 443 TEST*/ 444