Relocate MpiContextManager.
[alexxy/gromacs.git] / api / gmxapi / cpp / context_impl.h
index 0b5f9a69b2c3216de5b8f884535e4775e130016f..59acbaa67ec7c44138f8957889060c9cfb5e9c7c 100644 (file)
@@ -47,8 +47,6 @@
 #include "gromacs/mdrun/legacymdrunoptions.h"
 #include "gromacs/mdtypes/mdrunoptions.h"
 
-// Above are headers for dependencies.
-// Following are public headers for the current module.
 #include "gmxapi/context.h"
 #include "gmxapi/session.h"
 
@@ -56,26 +54,66 @@ namespace gmxapi
 {
 
 /*!
- * \brief Context implementation base class.
+ * \brief Provide RAII management of communications resource state.
  *
- * Execution contexts have a uniform interface specified by the API. Implementations for
- * particular execution environments can specialize / derive from this base.
+ * To acquire an MpiContextManager is to have assurance that any external MPI
+ * environment is ready to use. When the MpiContextManager is released or
+ * goes out of scope, the destructor finalizes the resources.
+ *
+ * Note that thread-MPI chooses the number of ranks and constructs its
+ * MPI communicator internally, so does not and is unlikely to ever
+ * participate here.
+ *
+ * \todo There is no resource for logging or reporting errors during initialization
  *
- * \todo Separate interface and implementation.
  * \ingroup gmxapi
  */
-class ContextImpl final : public std::enable_shared_from_this<ContextImpl>
+class MpiContextManager
 {
 public:
+    MpiContextManager();
+
+    ~MpiContextManager();
+
     /*!
-     * \brief Default constructor.
+     * \brief Exclusive ownership of a scoped context means copying is impossible.
      *
-     * Don't use this. Use create() to get a shared pointer right away.
-     * Otherwise, shared_from_this() is potentially dangerous.
+     * \{
+     */
+    MpiContextManager(const MpiContextManager&) = delete;
+    MpiContextManager& operator=(const MpiContextManager&) = delete;
+    //! \}
+
+    /*!
+     * \brief Move semantics are non-trivial.
      *
-     * \todo Make default constructor private or otherwise reduce brittleness of construction.
+     * \{
      */
-    ContextImpl();
+    MpiContextManager(MpiContextManager&&) noexcept = delete;
+    MpiContextManager& operator=(MpiContextManager&&) noexcept = delete;
+    //! \}
+};
+
+/*!
+ * \brief Context implementation.
+ *
+ * Execution contexts have a uniform interface specified by the API. Implementations for
+ * particular execution environments can specialize / derive from this base.
+ *
+ * \todo Separate interface and implementation.
+ *
+ * \warning Definition and semantics depend on configure-time details. For example,
+ *          MPI-enabled libraries always hold a valid MPI communicator via MpiContextManager,
+ *          whereas tMPI and non-MPI builds hold a meaningless MpiContextManager.
+ *
+ * \todo Provide functions or traits for introspection.
+ *
+ * \ingroup gmxapi
+ */
+class ContextImpl final : public std::enable_shared_from_this<ContextImpl>
+{
+public:
+    ~ContextImpl();
 
     /*!
      * \brief Factory function
@@ -84,10 +122,12 @@ public:
      * that it never exists without a shared_ptr owning it.
      *
      * If we can confirm `shared_from_this` is no longer necessary, implementation may change.
+     * \todo: Use registration/deregistration of launched Sessions to log warnings on shutdown
+     *        instead of letting Session keep ContextImpl alive.
      *
      * \return ownership of a new object
      */
-    static std::shared_ptr<gmxapi::ContextImpl> create();
+    static std::shared_ptr<ContextImpl> create();
 
     /*!
      * \brief Copy disallowed because Session state would become ambiguous.
@@ -103,7 +143,9 @@ public:
     /*!
      * \brief Objects are not trivial to move.
      *
-     * \todo Implement move semantics.
+     * \todo Implement move semantics. Requires a moveable const MpiContextManager and
+     *       LegacyMdrunOptions members.
+     *
      * \{
      */
     ContextImpl(ContextImpl&&) = delete;
@@ -123,6 +165,8 @@ public:
      * are still evolving.
      * \todo Hide lifetime management and ownership from handle object.
      * We can achieve the necessary aspects of this shared_ptr at a lower level of implementation.
+     * Note also that returned value policies can be implemented in higher level wrappers to ensure
+     * correct object lifetime scope. (See pybind, for instance.)
      */
     std::shared_ptr<Session> launch(const Workflow& work);
 
@@ -131,6 +175,9 @@ public:
      *
      * The client owns the Session launched by a Context, but it is helpful
      * for the Context to know if it has an active Session associated with it.
+     *
+     * \todo Use registration/deregistration protocol instead.
+     *       Requires logging facility.
      */
     std::weak_ptr<Session> session_;
 
@@ -152,6 +199,34 @@ public:
      * duplicate definitions e.g. of command-line options.
      */
     gmx::LegacyMdrunOptions options_;
+
+    /*!
+     * \brief Scoped MPI management.
+     *
+     * Part of the ContextImpl invariant establishes a point where MPI initialization status is
+     * known.
+     *
+     * To ensure the MpiContextManager is initialized only once, we use a const member that must
+     * be initialized at construction.
+     */
+    const MpiContextManager mpi_;
+
+private:
+    /*!
+     * \brief Basic constructor.
+     *
+     * Don't use this. Use create() to get a shared pointer right away.
+     * Otherwise, shared_from_this() is potentially dangerous.
+     */
+    explicit ContextImpl() noexcept(std::is_nothrow_constructible_v<gmx::LegacyMdrunOptions>);
+};
+
+
+class CommHandle
+{
+public:
+    using commType = MPI_Comm;
+    MPI_Comm communicator{ MPI_COMM_NULL };
 };
 
 } // end namespace gmxapi