TIMPI
timpi_init.C
Go to the documentation of this file.
1 // The TIMPI Message-Passing Parallelism Library.
2 // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 
4 // This library is free software; you can redistribute it and/or
5 // modify it under the terms of the GNU Lesser General Public
6 // License as published by the Free Software Foundation; either
7 // version 2.1 of the License, or (at your option) any later version.
8 
9 // This library is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 // Lesser General Public License for more details.
13 
14 // You should have received a copy of the GNU Lesser General Public
15 // License along with this library; if not, write to the Free Software
16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 
18 
19 // Local includes
20 #include "timpi/timpi_init.h"
21 
22 #include "timpi/semipermanent.h"
23 
24 // TIMPI includes
25 #include "timpi/communicator.h"
26 #include "timpi/timpi_assert.h"
27 
28 #ifdef TIMPI_ENABLE_EXCEPTIONS
29 #include <exception> // For std::uncaught_exceptions
30 #endif
31 
32 #ifdef TIMPI_HAVE_MPI
33 void TIMPI_MPI_Handler (MPI_Comm *, int *, ...)
34 {
35  timpi_not_implemented();
36 }
37 #endif
38 
39 
40 namespace TIMPI
41 {
42 
43 #ifdef TIMPI_HAVE_MPI
44 TIMPIInit::TIMPIInit (int argc, const char * const * argv,
45  int mpi_thread_requested,
46  bool handle_mpi_errors,
47  MPI_Comm COMM_WORLD_IN) :
48  i_initialized_mpi(false),
49  err_handler_set(false)
50 {
51  // Check whether the calling program has already initialized
52  // MPI, and avoid duplicate Init/Finalize
53  int flag;
54  timpi_call_mpi(MPI_Initialized (&flag));
55 
56  if (!flag)
57  {
58  int mpi_thread_provided;
59 
60  timpi_call_mpi
61  (MPI_Init_thread (&argc, const_cast<char ***>(&argv),
62  mpi_thread_requested, &mpi_thread_provided));
63 
64  if (mpi_thread_provided < mpi_thread_requested)
65  {
66  // Ideally, if an MPI stack tells us it's unsafe for us
67  // to use threads, we should scream and die or at least
68  // disable threads.
69  //
70  // In practice, we've encountered one MPI stack (an mvapich2
71  // configuration) that returned MPI_THREAD_SINGLE as a
72  // proper warning, two stacks that handle
73  // MPI_THREAD_FUNNELED properly, and two current stacks plus
74  // a couple old stacks that return MPI_THREAD_SINGLE but
75  // support threaded runs anyway, so we just emit a warning.
76  //
77  std::string thread_type;
78  switch (mpi_thread_requested)
79  {
80  case 0:
81  thread_type = "MPI_THREAD_SINGLE";
82  break;
83  case 1:
84  thread_type = "MPI_THREAD_FUNNELED";
85  break;
86  case 2:
87  thread_type = "MPI_THREAD_SERIALIZED";
88  break;
89  case 3:
90  thread_type = "MPI_THREAD_MULTIPLE";
91  break;
92  default:
93  timpi_error_msg("Unsupported mpi thread requested '" << mpi_thread_requested << "'");
94  }
95 
96  timpi_warning("Warning: MPI failed to guarantee " << thread_type << "\n"
97  << "for a threaded run.\n"
98  << std::endl);
99  }
100  this->i_initialized_mpi = true;
101  }
102 
103  // Duplicate the input communicator for internal use
104  // And get a Communicator copy too, to use
105  // as a default for that API
106  this->_comm = std::make_unique<Communicator>(COMM_WORLD_IN);
107 
108  // Let SemiPermanent know we need its objects for a while
109  this->_ref = std::make_unique<SemiPermanent::Ref>();
110 
111  // Set up an MPI error handler if requested. This helps us get
112  // into a debugger with a proper stack when an MPI error occurs.
113  if (handle_mpi_errors)
114  {
115  timpi_call_mpi
116  (MPI_Comm_create_errhandler(TIMPI_MPI_Handler, &my_errhandler));
117  timpi_call_mpi
118  (MPI_Comm_set_errhandler(COMM_WORLD_IN, my_errhandler));
119  timpi_call_mpi
120  (MPI_Comm_set_errhandler(MPI_COMM_WORLD, my_errhandler));
121  err_handler_set = true;
122  }
123 }
124 #else
125 TIMPIInit::TIMPIInit (int /* argc */, const char * const * /* argv */,
126  int /* mpi_thread_requested */,
127  bool /* handle_mpi_errors */)
128 {
129  this->_comm = std::make_unique<Communicator>(); // So comm() doesn't dereference null
130  this->_ref = std::make_unique<SemiPermanent::Ref>();
131 }
132 #endif
133 
134 
135 
137 {
138  // Every processor had better be ready to exit at the same time.
139  // Even if we're not doing parallel_only debugging, we don't want
140  // one processor to try to exit until all others are done working.
141 
142  // We could be destructing here because we're unwinding the stack
143  // due to a thrown exception, though. It's possible that an
144  // application is catching exceptions outside of the LibMeshInit
145  // scope, or that we're using a C++ compiler that does unwinding
146  // for uncaught exceptions (the standard says whether to go straight
147  // to terminate() or unwind first is "implementation-defined"). If
148  // *that* is the case then we can't safely communicate with other
149  // processors that might not all be unwinding too.
150 #ifdef TIMPI_ENABLE_EXCEPTIONS
151  if (!std::uncaught_exceptions())
152 #endif
153  this->comm().barrier();
154 
155  // Trigger any SemiPermanent cleanup before potentially finalizing MPI
156  _ref.reset();
157 
158 #ifdef TIMPI_HAVE_MPI
159  if (err_handler_set)
160  {
161  unsigned int error_code =
162  MPI_Errhandler_free(&my_errhandler);
163  if (error_code != MPI_SUCCESS)
164  {
165  std::cerr <<
166  "Failure when freeing MPI_Errhandler! Continuing..." <<
167  std::endl;
168  }
169  }
170 
171  this->_comm.reset();
172 
173  if (this->i_initialized_mpi)
174  {
175  // We can't just timpi_assert here because destructor,
176  // but we ought to report any errors
177  int error_code = MPI_Finalize();
178  if (error_code != MPI_SUCCESS)
179  {
180  char error_string[MPI_MAX_ERROR_STRING+1];
181  int error_string_len;
182  MPI_Error_string(error_code, error_string,
183  &error_string_len);
184  std::cerr << "Failure from MPI_Finalize():\n"
185  << error_string << std::endl;
186  }
187  }
188 #else
189  this->_comm.reset();
190 #endif
191 }
192 
193 
194 } // namespace TIMPI
TIMPIInit(int argc, const char *const *argv, int mpi_thread_requested=0, bool handle_mpi_errors=false, MPI_Comm COMM_WORLD_IN=MPI_COMM_WORLD)
Initialize the library for use, with the command line options provided.
Definition: timpi_init.C:44
std::unique_ptr< SemiPermanent::Ref > _ref
Definition: timpi_init.h:108
void barrier() const
Pause execution until all processors reach a certain point.
Definition: communicator.C:225
void TIMPI_MPI_Handler(MPI_Comm *, int *,...)
Definition: timpi_init.C:33
bool i_initialized_mpi
Definition: timpi_init.h:111
virtual ~TIMPIInit()
Destructor.
Definition: timpi_init.C:136
MPI_Errhandler my_errhandler
Definition: timpi_init.h:113
std::unique_ptr< Communicator > _comm
Definition: timpi_init.h:105
const Communicator & comm() const
Returns the Communicator created by this object, which will be a compatibility shim if MPI is not ena...
Definition: timpi_init.h:100