Clarifications for ResourceAssignment interface.
[alexxy/gromacs.git] / api / gmxapi / cpp / context_impl.h
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2018,2019,2020, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 #ifndef GMXAPI_CONTEXT_IMPL_H
36 #define GMXAPI_CONTEXT_IMPL_H
37 /*! \file
38  * \brief Declare gmxapi::ContextImpl
39  *
40  * \author M. Eric Irrgang <ericirrgang@gmail.com>
41  * \ingroup gmxapi
42  */
43
44 #include <memory>
45 #include <string>
46
47 #include "gromacs/mdrun/legacymdrunoptions.h"
48 #include "gromacs/mdtypes/mdrunoptions.h"
49 #include "gromacs/utility/gmxmpi.h"
50
51 #include "gmxapi/context.h"
52 #include "gmxapi/session.h"
53
54 namespace gmxapi
55 {
56
57 /*!
58  * \brief Provide RAII management of library MPI context.
59  *
60  * To acquire an MpiContextManager is to have assurance that any external MPI
61  * environment is ready to use. When the MpiContextManager is released or
62  * goes out of scope, the destructor finalizes the resources.
63  *
64  * It is plausible that one or more ranks may not participate in an API session,
65  * but we have not historically handled such use cases at this level.
66  * For GROMACS built with an MPI library, the root communicator in the MpiContextManager
67  * must be valid. For other builds (e.g. tMPI), the communicator member must be MPI_COMM_NULL.
68  *
69  * If the client provides a communicator, the client promises to maintain the validity
70  * of the communicator for the life of the MpiContextManager.
71  *
72  * Note that thread-MPI chooses the number of ranks and constructs its
73  * MPI communicator internally, so does not and is unlikely to ever
74  * participate here.
75  *
76  * \todo Log errors during set up or tear down.
77  * There is no resource for logging or reporting errors during initialization or when misused.
78  *
79  * \ingroup gmxapi
80  */
81 class MpiContextManager
82 {
83 public:
84     /*!
85      * \brief Default constructor.
86      *
87      * Construct a valid instance with an appropriate default value for the
88      * base communicator. (Note that appropriate default value depends on whether
89      * the library was compiled with an external MPI library.)
90      *
91      * \throws BasicException if library cannot be initialized.
92      */
93     MpiContextManager();
94
95     /*!
96      * \brief Allow the communicator to be specified.
97      *
98      * Supports use cases in which a client provides a non-default communicator.
99      * Ensures that library environment is properly initialized and finalized,
100      * whether or not an externally managed communicator has been provided.
101      *
102      * \param communicator Optional communicator representing a client-managed MPI environment.
103      *
104      * Note that the communicator must be MPI_COMM_NULL if and only if GROMACS was built without an
105      * external MPI library.
106      *
107      * \throws BasicException if library cannot be initialized.
108      *
109      * \todo (#3650?) Decide whether to exclude this from tMPI environments or find a sensible invariant.
110      */
111     explicit MpiContextManager(MPI_Comm communicator);
112
113     ~MpiContextManager();
114
115     /*!
116      * \brief Exclusive ownership of a scoped context means copying is impossible.
117      *
118      * \{
119      */
120     MpiContextManager(const MpiContextManager&) = delete;
121     MpiContextManager& operator=(const MpiContextManager&) = delete;
122     //! \}
123
124     /*!
125      * \brief Transfer ownership of the managed GROMACS MPI context.
126      *
127      * \{
128      */
129     MpiContextManager(MpiContextManager&& source) noexcept = default;
130     MpiContextManager& operator=(MpiContextManager&& source) noexcept = default;
131     //! \}
132
133     /*!
134      * \brief Get the communicator for this context.
135      *
136      * \return Communicator with an appropriate initialized state for the current library
137      * configuration.
138      *
139      * \throws gmxapi::UsageError if MpiContextManager is in an invalid state, such as after
140      *  being moved from.
141      */
142     [[nodiscard]] MPI_Comm communicator() const;
143
144 private:
145     /*!
146      * \brief The resources provided by this manager.
147      *
148      * A valid MpiContextManager has a value for communicator_ that is appropriate
149      * for the library configuration.
150      */
151     std::unique_ptr<MPI_Comm> communicator_;
152 };
153
154 /*!
155  * \brief Context implementation.
156  *
157  * Execution contexts have a uniform interface specified by the API. Implementations for
158  * particular execution environments can specialize / derive from this base.
159  *
160  * \todo Separate interface and implementation.
161  *
162  * \warning Definition and semantics depend on configure-time details. For example,
163  *          MPI-enabled libraries always hold a valid MPI communicator via MpiContextManager,
164  *          whereas tMPI and non-MPI builds hold a meaningless MpiContextManager.
165  *
166  * \todo Provide functions or traits for introspection.
167  *
168  * \ingroup gmxapi
169  */
170 class ContextImpl final : public std::enable_shared_from_this<ContextImpl>
171 {
172 public:
173     ~ContextImpl();
174
175     /*!
176      * \brief Factory function
177      *
178      * Since this class provides `shared_from_this`, we need to make sure
179      * that it never exists without a shared_ptr owning it.
180      *
181      * If we can confirm `shared_from_this` is no longer necessary, implementation may change.
182      * \todo: Use registration/deregistration of launched Sessions to log warnings on shutdown
183      *        instead of letting Session keep ContextImpl alive.
184      *
185      * \return ownership of a new object
186      */
187     static std::shared_ptr<ContextImpl> create(MpiContextManager&& mpi);
188
189     /*!
190      * \brief Copy disallowed because Session state would become ambiguous.
191      *
192      * The API implementation needs to unambiguously determine
193      * which Sessions and Contexts are associated with each other.
194      * \{
195      */
196     ContextImpl(const ContextImpl&) = delete;
197     ContextImpl& operator=(const ContextImpl&) = delete;
198     //! \}
199
200     /*!
201      * \brief Objects are not trivial to move.
202      *
203      * \todo Implement move semantics. Requires a moveable const MpiContextManager and
204      *       LegacyMdrunOptions members.
205      *
206      * \{
207      */
208     ContextImpl(ContextImpl&&) = delete;
209     ContextImpl& operator=(ContextImpl&&) = delete;
210     //! \}
211
212     /*!
213      * \brief Translate the workflow to the execution context and launch.
214      *
215      * \param work workflow graph
216      * \return ownership of a new session
217      *
218      * \todo This probably makes more sense as a free function, but we need to determine access policies.
219      *
220      * Session is returned with shared_ptr ownership so that Context
221      * can hold a weak_ptr and because Session Resources handles
222      * are still evolving.
223      * \todo Hide lifetime management and ownership from handle object.
224      * We can achieve the necessary aspects of this shared_ptr at a lower level of implementation.
225      * Note also that returned value policies can be implemented in higher level wrappers to ensure
226      * correct object lifetime scope. (See pybind, for instance.)
227      */
228     std::shared_ptr<Session> launch(const Workflow& work);
229
230     /*!
231      * \brief Retain the ability to find a launched session while it exists.
232      *
233      * The client owns the Session launched by a Context, but it is helpful
234      * for the Context to know if it has an active Session associated with it.
235      *
236      * \todo Use registration/deregistration protocol instead.
237      *       Requires logging facility.
238      */
239     std::weak_ptr<Session> session_;
240
241     /*!
242      * \brief mdrun command line arguments.
243      *
244      * Store arguments provided by the client and pass them when launching
245      * a simulation runner. This allows client code to access the same
246      * options as are available to mdrun on the command line while the API
247      * evolves.
248      */
249     MDArgs mdArgs_;
250
251     /*!
252      * \brief Legacy option-handling and set up for mdrun.
253      *
254      * This object should not exist, but is necessary now to introduce
255      * the API in a way that means CLI and API work similarly and do not
256      * duplicate definitions e.g. of command-line options.
257      */
258     gmx::LegacyMdrunOptions options_;
259
260     /*!
261      * \brief Scoped MPI management.
262      *
263      * Part of the ContextImpl invariant establishes a point where MPI initialization status is
264      * known.
265      *
266      * Note that the MpiContextManager invariant is dependent on configure-time options to the
267      * GROMACS library. Specifically, MpiContextManager::communicator() is guaranteed to be
268      * MPI_COMM_NULL if and only if the library was built without an external MPI library.
269      * This is confusing and we should split either this class or the
270      * MpiContextManager class. Another alternative would be to use a std::optional here and to
271      * allow the std::unique_ptr<MpiContextManager> provided to ContextImpl::create() to be null,
272      * but that means a slight change of documented behavior protocol. See #3688 and #3650 for
273      * follow up.
274      *
275      * To ensure the MpiContextManager is initialized only once, we use a const member that must
276      * be initialized at construction.
277      */
278     const MpiContextManager mpi_;
279
280 private:
281     /*!
282      * \brief Basic constructor.
283      *
284      * Don't use this. Use create() to get a shared pointer right away.
285      * Otherwise, shared_from_this() is potentially dangerous.
286      */
287     explicit ContextImpl(MpiContextManager&& mpi) noexcept(std::is_nothrow_constructible_v<gmx::LegacyMdrunOptions>);
288 };
289
290 /*!
291  * \brief Allow client code to hold a library communicator.
292  *
293  * MPI-enabled client code and thread-MPI GROMACS libraries cannot share headers
294  * in which MPI symbols (such as MPI_Comm) are defined. This class allows a
295  * client to refer to a library communicator opaquely. See gmxapi::offerComm().
296  *
297  * In the initial gmxapi::ResourceAssignment implementation, CommHandle objects
298  * are the only possible recipients of assigned resources. If future implementations
299  * allow richer resource assignment, CommHandle may be superseded by a more
300  * elaborate interface. Otherwise, all we need is this simple type proxy.
301  */
302 class CommHandle
303 {
304 public:
305     using commType = MPI_Comm;
306     MPI_Comm communicator{ MPI_COMM_NULL };
307 };
308
309 } // end namespace gmxapi
310 #endif // GMXAPI_CONTEXT_IMPL_H