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 
29 #ifdef TIMPI_HAVE_MPI
30 void TIMPI_MPI_Handler (MPI_Comm *, int *, ...)
31 {
32  timpi_not_implemented();
33 }
34 #endif
35 
36 
37 namespace TIMPI
38 {
39 
40 #ifdef TIMPI_HAVE_MPI
41 TIMPIInit::TIMPIInit (int argc, const char * const * argv,
42  int mpi_thread_requested,
43  bool handle_mpi_errors,
44  MPI_Comm COMM_WORLD_IN) :
45  i_initialized_mpi(false),
46  err_handler_set(false)
47 {
48  // Check whether the calling program has already initialized
49  // MPI, and avoid duplicate Init/Finalize
50  int flag;
51  timpi_call_mpi(MPI_Initialized (&flag));
52 
53  if (!flag)
54  {
55  int mpi_thread_provided;
56 
57  timpi_call_mpi
58  (MPI_Init_thread (&argc, const_cast<char ***>(&argv),
59  mpi_thread_requested, &mpi_thread_provided));
60 
61  if (mpi_thread_provided < mpi_thread_requested)
62  {
63  // Ideally, if an MPI stack tells us it's unsafe for us
64  // to use threads, we should scream and die or at least
65  // disable threads.
66  //
67  // In practice, we've encountered one MPI stack (an mvapich2
68  // configuration) that returned MPI_THREAD_SINGLE as a
69  // proper warning, two stacks that handle
70  // MPI_THREAD_FUNNELED properly, and two current stacks plus
71  // a couple old stacks that return MPI_THREAD_SINGLE but
72  // support threaded runs anyway, so we just emit a warning.
73  //
74  std::string thread_type;
75  switch (mpi_thread_requested)
76  {
77  case 0:
78  thread_type = "MPI_THREAD_SINGLE";
79  break;
80  case 1:
81  thread_type = "MPI_THREAD_FUNNELED";
82  break;
83  case 2:
84  thread_type = "MPI_THREAD_SERIALIZED";
85  break;
86  case 3:
87  thread_type = "MPI_THREAD_MULTIPLE";
88  break;
89  default:
90  timpi_error_msg("Unsupported mpi thread requested '" << mpi_thread_requested << "'");
91  }
92 
93  timpi_warning("Warning: MPI failed to guarantee " << thread_type << "\n"
94  << "for a threaded run.\n"
95  << std::endl);
96  }
97  this->i_initialized_mpi = true;
98  }
99 
100  // Duplicate the input communicator for internal use
101  // And get a Communicator copy too, to use
102  // as a default for that API
103  this->_comm = std::make_unique<Communicator>(COMM_WORLD_IN);
104 
105  // Let SemiPermanent know we need its objects for a while
106  this->_ref = std::make_unique<SemiPermanent::Ref>();
107 
108  // Set up an MPI error handler if requested. This helps us get
109  // into a debugger with a proper stack when an MPI error occurs.
110  if (handle_mpi_errors)
111  {
112  timpi_call_mpi
113  (MPI_Comm_create_errhandler(TIMPI_MPI_Handler, &my_errhandler));
114  timpi_call_mpi
115  (MPI_Comm_set_errhandler(COMM_WORLD_IN, my_errhandler));
116  timpi_call_mpi
117  (MPI_Comm_set_errhandler(MPI_COMM_WORLD, my_errhandler));
118  err_handler_set = true;
119  }
120 }
121 #else
122 TIMPIInit::TIMPIInit (int /* argc */, const char * const * /* argv */,
123  int /* mpi_thread_requested */,
124  bool /* handle_mpi_errors */)
125 {
126  this->_comm = std::make_unique<Communicator>(); // So comm() doesn't dereference null
127  this->_ref = std::make_unique<SemiPermanent::Ref>();
128 }
129 #endif
130 
131 
132 
134 {
135  // Every processor had better be ready to exit at the same time.
136  // This would be a timpi_parallel_only() function, except that
137  // timpi_parallel_only() uses timpi_assert() which throws an
138  // exception which causes compilers to scream about exceptions
139  // inside destructors.
140 
141  // Even if we're not doing parallel_only debugging, we don't want
142  // one processor to try to exit until all others are done working.
143  this->comm().barrier();
144 
145  // Trigger any SemiPermanent cleanup before potentially finalizing MPI
146  _ref.reset();
147 
148 #ifdef TIMPI_HAVE_MPI
149  if (err_handler_set)
150  {
151  unsigned int error_code =
152  MPI_Errhandler_free(&my_errhandler);
153  if (error_code != MPI_SUCCESS)
154  {
155  std::cerr <<
156  "Failure when freeing MPI_Errhandler! Continuing..." <<
157  std::endl;
158  }
159  }
160 
161  this->_comm.reset();
162 
163  if (this->i_initialized_mpi)
164  {
165  // We can't just timpi_assert here because destructor,
166  // but we ought to report any errors
167  int error_code = MPI_Finalize();
168  if (error_code != MPI_SUCCESS)
169  {
170  char error_string[MPI_MAX_ERROR_STRING+1];
171  int error_string_len;
172  MPI_Error_string(error_code, error_string,
173  &error_string_len);
174  std::cerr << "Failure from MPI_Finalize():\n"
175  << error_string << std::endl;
176  }
177  }
178 #else
179  this->_comm.reset();
180 #endif
181 }
182 
183 
184 } // 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:41
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:30
bool i_initialized_mpi
Definition: timpi_init.h:111
virtual ~TIMPIInit()
Destructor.
Definition: timpi_init.C:133
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