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