+ namespace internal
+ {
+ namespace CollectiveMutexImplementation
+ {
+ /**
+ * Abort, should there be an exception being processed (see the error
+ * message).
+ */
+ void
+ check_exception()
+ {
+#ifdef DEAL_II_WITH_MPI
+# if __cpp_lib_uncaught_exceptions >= 201411
+ // std::uncaught_exception() is deprecated in c++17
+ if (std::uncaught_exceptions() != 0)
+# else
+ if (std::uncaught_exception() == true)
+# endif
+ {
+ std::cerr
+ << "---------------------------------------------------------\n"
+ << "An exception was thrown inside a section of the program\n"
+ << "guarded by a CollectiveMutex.\n"
+ << "Because a CollectiveMutex guards critical communication\n"
+ << "handling the exception would likely\n"
+ << "deadlock because only the current process is aware of the\n"
+ << "exception. To prevent this deadlock, the program will be\n"
+ << "aborted.\n"
+ << "---------------------------------------------------------"
+ << std::endl;
+
+ MPI_Abort(MPI_COMM_WORLD, 1);
+ }
+#endif
+ }
+ } // namespace CollectiveMutexImplementation
+ } // namespace internal
+
+
+
CollectiveMutex::CollectiveMutex()
: locked(false)
, request(MPI_REQUEST_NULL)
CollectiveMutex::~CollectiveMutex()
{
+ // First check if this destructor is called during exception handling
+ // if so, abort.
+ internal::CollectiveMutexImplementation::check_exception();
+
Assert(
!locked,
ExcMessage(
{
(void)comm;
+ // First check if this function is called during exception handling
+ // if so, abort. This can happen if a ScopedLock is destroyed.
+ internal::CollectiveMutexImplementation::check_exception();
+
Assert(
locked,
ExcMessage(
"Error: MPI::CollectiveMutex needs to be locked before unlock()"));
- // Only communicate with other processes if there is no uncaught
- // exception to avoid a deadlock.
#ifdef DEAL_II_WITH_MPI
-# if __cpp_lib_uncaught_exceptions >= 201411
- // std::uncaught_exception() is deprecated in c++17
- if (std::uncaught_exceptions() == 0 && comm != MPI_COMM_SELF)
-# else
- if (std::uncaught_exception() == false && comm != MPI_COMM_SELF)
-# endif
- {
- // TODO: For now, we implement this mutex with a blocking barrier
- // in the lock and unlock. It needs to be tested, if we can move
- // to a nonblocking barrier (code disabled below):
+
+ // TODO: For now, we implement this mutex with a blocking barrier
+ // in the lock and unlock. It needs to be tested, if we can move
+ // to a nonblocking barrier (code disabled below):
# if 0
- const int ierr = MPI_Ibarrier(comm, &request);
- AssertThrowMPI(ierr);
+ const int ierr = MPI_Ibarrier(comm, &request);
+ AssertThrowMPI(ierr);
# else
- const int ierr = MPI_Barrier(comm);
- AssertThrowMPI(ierr);
+ const int ierr = MPI_Barrier(comm);
+ AssertThrowMPI(ierr);
# endif
- }
#endif
- // Unlock the mutex in either case. Either we successfully
- // synchronized processes in the barrier, or we want to
- // continue to unwind the stack without throwing exceptions
- // in the destructor of this class.
locked = false;
}