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