diff --git a/applications/test/parallel-external-init/Make/files b/applications/test/parallel-external-init/Make/files new file mode 100644 index 0000000000000000000000000000000000000000..c194b165d8282cb5dd2a1dcd171e1428b6209516 --- /dev/null +++ b/applications/test/parallel-external-init/Make/files @@ -0,0 +1,3 @@ +Test-parallel-external-init.C + +EXE = $(FOAM_USER_APPBIN)/Test-parallel-external-init diff --git a/applications/test/parallel-external-init/Make/options b/applications/test/parallel-external-init/Make/options new file mode 100644 index 0000000000000000000000000000000000000000..e93d17dd5447198091bb6e5dc0e4af188e887221 --- /dev/null +++ b/applications/test/parallel-external-init/Make/options @@ -0,0 +1,5 @@ +sinclude $(GENERAL_RULES)/mplib$(WM_MPLIB) +sinclude $(DEFAULT_RULES)/mplib$(WM_MPLIB) + +EXE_INC = $(PFLAGS) $(PINC) $(c++LESSWARN) +EXE_LIBS = $(PLIBS) diff --git a/applications/test/parallel-external-init/Test-parallel-external-init.C b/applications/test/parallel-external-init/Test-parallel-external-init.C new file mode 100644 index 0000000000000000000000000000000000000000..a64f3d3e1c1fe1f38ff1b5e3f007cd7757551ba6 --- /dev/null +++ b/applications/test/parallel-external-init/Test-parallel-external-init.C @@ -0,0 +1,106 @@ +/*---------------------------------------------------------------------------*\ + ========= | + \\ / F ield | OpenFOAM: The Open Source CFD Toolbox + \\ / O peration | + \\ / A nd | Copyright (C) 2019 OpenCFD Ltd. + \\/ M anipulation | +------------------------------------------------------------------------------- +License + This file is part of OpenFOAM. + + OpenFOAM is free software: you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + OpenFOAM is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + for more details. + + You should have received a copy of the GNU General Public License + along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. + +Application + Test-parallel-external-init + +Description + Simulate starting MPI outside of OpenFOAM + +\*---------------------------------------------------------------------------*/ + +#include "argList.H" +#include "Time.H" +#include "IPstream.H" +#include "OPstream.H" +#include "vector.H" +#include "IOstreams.H" +#include "Pstream.H" + +#include <mpi.h> +#include <iostream> + +using namespace Foam; + + +bool startMPI() +{ + int nprocs = 0, rank = 0; + + MPI_Init(nullptr, nullptr); + + MPI_Comm_size(MPI_COMM_WORLD, &nprocs); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + + if (nprocs && rank == 0) + { + std::cout<< nl << "Using MPI with " << nprocs << " procs" << nl << nl; + } + + return true; +} + + +bool stopMPI() +{ + Info<< nl << "Stopping MPI" << nl << nl; + + MPI_Finalize(); + + return true; +} + + +string message() +{ + return + ( + "rank " + name(Pstream::myProcNo()) + + " / " + name(Pstream::nProcs()) + "\n" + ); +} + + +// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // + +int main(int argc, char *argv[]) +{ + argList::noCheckProcessorDirectories(); + + UPstream::debug = 1; + + startMPI(); + + #include "setRootCase.H" + + Pout<< message().c_str(); + + stopMPI(); + + Info<< "\nEnd\n" << endl; + + return 0; +} + + +// ************************************************************************* // diff --git a/src/Pstream/mpi/UPstream.C b/src/Pstream/mpi/UPstream.C index 86d10163b01f41e8f24a5a3344bdb37505754ffa..fbef4daaca2e9013c8d57e1869ee6e1774e3341c 100644 --- a/src/Pstream/mpi/UPstream.C +++ b/src/Pstream/mpi/UPstream.C @@ -48,8 +48,81 @@ License // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * // -// file-scope: min value and default for mpiBufferSize -static const int minBufferSize = 20000000; +// The min value and default for MPI buffers length +constexpr int minBufLen = 20000000; + +// Track if we have attached MPI buffers +static bool ourBuffers = false; + +// Track if we initialized MPI +static bool ourMpi = false; + + +// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * // + +static void attachOurBuffers() +{ + if (ourBuffers) + { + return; // Already attached + } + ourBuffers = true; + + // Use UPstream::mpiBufferSize (optimisationSwitch), + // but allow override with MPI_BUFFER_SIZE env variable (int value) + +#ifndef SGIMPI + int len = 0; + + const std::string str(Foam::getEnv("MPI_BUFFER_SIZE")); + if (str.empty() || !Foam::read(str, len) || len <= 0) + { + len = Foam::UPstream::mpiBufferSize; + } + + if (len < minBufLen) + { + len = minBufLen; + } + + if (Foam::UPstream::debug) + { + Foam::Pout<< "UPstream::init : buffer-size " << len << '\n'; + } + + char* buf = new char[len]; + + if (MPI_SUCCESS != MPI_Buffer_attach(buf, len)) + { + delete[] buf; + Foam::Pout<< "UPstream::init : could not attach buffer\n"; + } +#endif +} + + +static void detachOurBuffers() +{ + if (!ourBuffers) + { + return; // Nothing to detach + } + ourBuffers = false; + + // Some MPI notes suggest that the return code is MPI_SUCCESS when + // no buffer is attached. + // Be extra careful and require a non-zero size as well. + +#ifndef SGIMPI + int len = 0; + char* buf = nullptr; + + if (MPI_SUCCESS == MPI_Buffer_detach(&buf, &len) && len) + { + delete[] buf; + } +#endif +} // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // @@ -78,7 +151,7 @@ bool Foam::UPstream::initNull() { // Already finalized - this is an error FatalErrorInFunction - << "MPI was already finalized - cannot perform MPI_Init" << endl + << "MPI was already finalized - cannot perform MPI_Init\n" << Foam::abort(FatalError); return false; @@ -87,17 +160,27 @@ bool Foam::UPstream::initNull() MPI_Initialized(&flag); if (flag) { - // Already initialized - nothing to do - return true; + if (debug) + { + Pout<< "UPstream::initNull : was already initialized\n"; + } } + else + { + // Not already initialized - MPI_Init_thread - ( - nullptr, // argc - nullptr, // argv - MPI_THREAD_SINGLE, - &flag // provided_thread_support - ); + MPI_Init_thread + ( + nullptr, // argc + nullptr, // argv + MPI_THREAD_SINGLE, + &flag // provided_thread_support + ); + + ourMpi = true; + } + + // Could also attach buffers etc. return true; } @@ -105,6 +188,8 @@ bool Foam::UPstream::initNull() bool Foam::UPstream::init(int& argc, char**& argv, const bool needsThread) { + int numprocs = 0, myRank = 0; + int provided_thread_support = 0; int flag = 0; MPI_Finalized(&flag); @@ -121,38 +206,47 @@ bool Foam::UPstream::init(int& argc, char**& argv, const bool needsThread) MPI_Initialized(&flag); if (flag) { - // Already initialized - issue warning and skip the rest - WarningInFunction - << "MPI was already initialized - cannot perform MPI_Init" << nl - << "This could indicate an application programming error!" << endl; - - return true; - } + // Already initialized. + // Warn if we've called twice, but skip if initialized externally + if (ourMpi) + { + WarningInFunction + << "MPI was already initialized - cannot perform MPI_Init" << nl + << "This could indicate an application programming error!" + << endl; - //MPI_Init(&argc, &argv); - int provided_thread_support; - MPI_Init_thread - ( - &argc, - &argv, + return true; + } + else if (debug) + { + Pout<< "UPstream::init : was already initialized\n"; + } + } + else + { + MPI_Init_thread ( - needsThread - ? MPI_THREAD_MULTIPLE - : MPI_THREAD_SINGLE - ), - &provided_thread_support - ); + &argc, + &argv, + ( + needsThread + ? MPI_THREAD_MULTIPLE + : MPI_THREAD_SINGLE + ), + &provided_thread_support + ); + + ourMpi = true; + } - int numprocs; MPI_Comm_size(MPI_COMM_WORLD, &numprocs); - int myRank; MPI_Comm_rank(MPI_COMM_WORLD, &myRank); if (debug) { - Pout<< "UPstream::init : initialised with numProcs:" << numprocs - << " myRank:" << myRank << endl; + Pout<< "UPstream::init : procs=" << numprocs + << " rank:" << myRank << endl; } if (numprocs <= 1) @@ -162,39 +256,10 @@ bool Foam::UPstream::init(int& argc, char**& argv, const bool needsThread) << Foam::abort(FatalError); } - // Initialise parallel structure setParRun(numprocs, provided_thread_support == MPI_THREAD_MULTIPLE); - #ifndef SGIMPI - { - // Normally use UPstream::mpiBufferSize (optimisationSwitch), - // but allow override with the MPI_BUFFER_SIZE env variable - // which has an int value - int bufSize = 0; - - const std::string str = Foam::getEnv("MPI_BUFFER_SIZE"); - if (str.empty() || !Foam::read(str, bufSize) || bufSize <= 0) - { - bufSize = mpiBufferSize; - } - - if (bufSize < minBufferSize) - { - bufSize = minBufferSize; - } - - if (debug) - { - Pout<< "UPstream::init : mpi-buffer-size " << bufSize << endl; - } - - // TBD: could add error handling here. - // Delete allocated and leave if we fail to attach the buffer? - - MPI_Buffer_attach(new char[bufSize], bufSize); - } - #endif + attachOurBuffers(); return true; } @@ -204,7 +269,7 @@ void Foam::UPstream::exit(int errnum) { if (debug) { - Pout<< "UPstream::exit." << endl; + Pout<< "UPstream::exit\n"; } int flag = 0; @@ -220,43 +285,35 @@ void Foam::UPstream::exit(int errnum) MPI_Finalized(&flag); if (flag) { - // Already finalized - warn and exit - WarningInFunction - << "MPI was already finalized (perhaps by a connected program)" - << endl; - std::exit(1); - return; - } - - #ifndef SGIMPI - { - // Some MPI notes suggest that the return code is MPI_SUCCESS when - // no buffer is attached. - // Be extra careful and require a non-zero size as well. - - int bufSize = 0; - char* buf = nullptr; - - flag = MPI_Buffer_detach(&buf, &bufSize); - - if (MPI_SUCCESS == flag && bufSize) + // Already finalized elsewhere? + if (ourMpi) { - delete[] buf; + WarningInFunction + << "MPI was already finalized (by a connected program?)\n"; } + else if (debug) + { + Pout<< "UPstream::exit : was already finalized\n"; + } + } + else + { + detachOurBuffers(); } - #endif - if (PstreamGlobals::outstandingRequests_.size()) + + const label nOutstanding = PstreamGlobals::outstandingRequests_.size(); + if (nOutstanding) { - label n = PstreamGlobals::outstandingRequests_.size(); PstreamGlobals::outstandingRequests_.clear(); WarningInFunction - << "There are still " << n << " outstanding MPI_Requests." << endl - << "This means that your code exited before doing a" - << " UPstream::waitRequests()." << endl + << "There were still " << nOutstanding + << " outstanding MPI_Requests." << nl + << "Which means your code exited before doing a " + << " UPstream::waitRequests()." << nl << "This should not happen for a normal code exit." - << endl; + << nl; } // Clean mpi communicators @@ -268,15 +325,27 @@ void Foam::UPstream::exit(int errnum) } } - if (errnum == 0) - { - MPI_Finalize(); - std::exit(errnum); - } - else + if (!flag) { - MPI_Abort(MPI_COMM_WORLD, errnum); + // MPI not already finalized + + if (!ourMpi) + { + WarningInFunction + << "Finalizing MPI, but was initialized elsewhere\n"; + } + + if (errnum == 0) + { + MPI_Finalize(); + } + else + { + MPI_Abort(MPI_COMM_WORLD, errnum); + } } + + std::exit(errnum); } @@ -786,7 +855,7 @@ void Foam::UPstream::resetRequests(const label i) void Foam::UPstream::waitRequests(const label start) { - if (debug) + if (UPstream::debug) { Pout<< "UPstream::waitRequests : starting wait for " << PstreamGlobals::outstandingRequests_.size()-start