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