diff --git a/Makefile.am b/Makefile.am index 97958ea..b83ed66 100644 --- a/Makefile.am +++ b/Makefile.am @@ -22,6 +22,11 @@ pkgconfig_DATA = timpi.pc # Support for timpi-config in $(exec_prefix)/bin timpi_configdir = $(exec_prefix)/bin timpi_config_SCRIPTS = bin/timpi-config +# +# support top-level 'make test_headers' +test_headers: + @cd $(top_builddir)/src && $(MAKE) test_headers + # Support 'make install prefix=/other/path' with pkgconfig install-data-hook: diff --git a/bin/test_headers.sh b/bin/test_headers.sh new file mode 100755 index 0000000..a5ff4bc --- /dev/null +++ b/bin/test_headers.sh @@ -0,0 +1,148 @@ +#!/bin/bash +#set -e + +# You can run this script on a single header file by doing: +# test_CXXFLAGS="`timpi-config --cppflags --cxxflags --include`" HEADERS_TO_TEST=exact_solution.h ./bin/test_headers.sh + +# To run this script on *every* header file in an installed TIMPI: +# test_CXXFLAGS="`timpi-config --cppflags --cxxflags --include`" HEADERS_TO_TEST="`find $TIMPI_DIR -name "*.h" -type f -exec basename {} \;`" ./bin/test_headers.sh + +# prefix to use when including headers +PACKAGE_PREFIX=timpi + +# Respect the JOBS environment variable, if it is set +if [ -n "$JOBS" ]; then + n_concurrent=$JOBS +else + n_concurrent=20 +fi + +#echo MAKEFLAGS=$MAKEFLAGS + +# Terminal commands to goto specific columns +rescol=65; + +# Terminal commands for setting the color +gotocolumn=; +white=; +green=; +red=; +grey=; +colorreset=; +if (test "X$TERM" != Xdumb && { test -t 1; } 2>/dev/null); then + gotocolumn="\033["$rescol"G"; + white="\033[01;37m"; + green="\033[01;32m"; + red="\033[01;31m"; + grey="\033[00;37m"; + colorreset="\033[m"; # Terminal command to reset to terminal default +fi + + +#echo "CXX=$CXX" + +testing_installed_tree="no" + +if (test "x$test_CXXFLAGS" = "x"); then + + testing_installed_tree="yes" + + if (test "x$PKG_CONFIG" != "xno"); then + test_CXXFLAGS=`pkg-config timpi --cflags` + + elif (command -v timpi-config); then + test_CXXFLAGS=`timpi-config --cppflags --cxxflags --include` + + else + echo "Cannot query package installation!!" + exit 1 + fi +fi + +echo "Using test_CXXFLAGS =" $test_CXXFLAGS + +# this function handles the I/O and compiling of a particular header file +# by encapsulating this in a function we can fork it and run multiple builds +# simultaneously +function test_header() +{ + myreturn=0 + header_to_test=$1 + header_name=`basename $header_to_test` + app_file=`mktemp -t $header_name.XXXXXXXXXX` + source_file=$app_file.cxx + object_file=$app_file.o + errlog=$app_file.log + stdout=$app_file.stdout + + printf '%s' "Testing Header $header_to_test ... " > $stdout + echo "#include \"$PACKAGE_PREFIX/$header_name\"" >> $source_file + echo "int foo () { return 0; }" >> $source_file + + #echo $CXX $test_CXXFLAGS $source_file -o $app_file + if $CXX $test_CXXFLAGS $source_file -c -o $object_file >$errlog 2>&1 ; then + # See color codes above. We: + # .) skip to column 65 + # .) print [ in white + # .) print OK in green + # .) print ] in white + # .) reset the terminal color + # .) print a newline + printf '\e[65G\e[1;37m[\e[1;32m%s\e[1;37m]\e[m\e[m\n' " OK " >> $stdout + else + # See comment above for OK status + printf '\e[65G\e[1;37m[\e[1;31m%s\e[1;37m]\e[m\e[m\n' " FAILED " >> $stdout + echo "Source file:" >> $stdout + cat $source_file >> $stdout + echo "" >> $stdout + echo "Command line:" >> $stdout + echo $CXX $test_CXXFLAGS $source_file -c -o $object_file >> $stdout + echo "" >> $stdout + echo "Output:" >> $stdout + cat $errlog >> $stdout + echo "" >> $stdout + myreturn=1 + fi + + cat $stdout + rm -f $source_file $app_file $object_file $errlog $stdout + + return $myreturn +} + + +if [ "x$HEADERS_TO_TEST" = "x" ]; then + HEADERS_TO_TEST=$DEFAULT_HEADERS_TO_TEST +fi + + +# loop over each header and fork tests +returnval=0 +nrunning=0 +ntotal=0 +runninglist="" +for header_to_test in $HEADERS_TO_TEST ; do + if [ $nrunning -ge $n_concurrent ]; then + for pid in $runninglist ; do + wait $pid + # accumulate the number of failed tests + returnval=$(($returnval+$?)) + done + nrunning=0 + runninglist="" + fi + + test_header $header_to_test & + runninglist="$runninglist $!" + nrunning=$(($nrunning+1)) + ntotal=$(($ntotal+1)) +done + +for pid in $runninglist ; do + wait $pid + returnval=$(($returnval+$?)) +done + +echo "$returnval failed tests of $ntotal header files" + +exit $returnval diff --git a/src/Makefile.am b/src/Makefile.am index 323a96c..90a08cf 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -92,6 +92,7 @@ include_HEADERS += parallel/include/timpi/post_wait_unpack_buffer.h include_HEADERS += parallel/include/timpi/post_wait_unpack_nested_buffer.h include_HEADERS += parallel/include/timpi/post_wait_work.h include_HEADERS += parallel/include/timpi/request.h +include_HEADERS += parallel/include/timpi/serial_implementation.h include_HEADERS += parallel/include/timpi/standard_type.h include_HEADERS += parallel/include/timpi/status.h @@ -133,3 +134,28 @@ AM_CPPFLAGS += -I$(top_srcdir)/src/utilities/include AM_CPPFLAGS += -I$(top_builddir)/src/utilities/include #timpi_version.h, timpi_config.h LIBS = $(timpi_optional_LIBS) + + +# Note that for "make test_headers" this DEFAULT_HEADERS_TO_TEST business allows us +# to selectively choose individual HEADERS_TO_TEST in the test_headers.sh script, +# e.g. +# $ make HEADERS_TO_TEST="numerics/parsed_function.h base/auto_ptr.h" test_headers +test_headers: + @echo " " + @echo "Checking for standalone headers in source tree $(top_builddir)/include in OPT mode ..." + @echo " " + @CXX="$(CXX)" DEFAULT_HEADERS_TO_TEST="$(include_HEADERS)" \ + test_CXXFLAGS="$(AM_CPPFLAGS) $(CPPFLAGS_OPT) $(CXXFLAGS_OPT) -I$(top_builddir)/include" \ + $(top_srcdir)/bin/test_headers.sh + @echo " " + @echo "Checking for standalone headers in source tree $(top_builddir)/include in DEVEL mode ..." + @echo " " + @CXX="$(CXX)" DEFAULT_HEADERS_TO_TEST="$(include_HEADERS)" \ + test_CXXFLAGS="$(AM_CPPFLAGS) $(CPPFLAGS_DEVEL) $(CXXFLAGS_DEVEL) -I$(top_builddir)/include" \ + $(top_srcdir)/bin/test_headers.sh + @echo " " + @echo "Checking for standalone headers in source tree $(top_builddir)/include in DBG mode ..." + @echo " " + @CXX="$(CXX)" DEFAULT_HEADERS_TO_TEST="$(include_HEADERS)" \ + test_CXXFLAGS="$(AM_CPPFLAGS) $(CPPFLAGS_DBG) $(CXXFLAGS_DBG) -I$(top_builddir)/include" \ + $(top_srcdir)/bin/test_headers.sh diff --git a/src/parallel/include/timpi/parallel_implementation.h b/src/parallel/include/timpi/parallel_implementation.h index 7c87b9b..f7b7156 100644 --- a/src/parallel/include/timpi/parallel_implementation.h +++ b/src/parallel/include/timpi/parallel_implementation.h @@ -40,6 +40,10 @@ #include "timpi/status.h" #include "timpi/standard_type.h" +#ifndef TIMPI_HAVE_MPI +#include "timpi/serial_implementation.h" +#endif + // Disable libMesh logging until we decide how to port it best // #include "libmesh/libmesh_logging.h" #define TIMPI_LOG_SCOPE(f,c) @@ -250,32 +254,6 @@ int Communicator::packed_size_of(const std::vector,A2> & buf, } -template -inline Status Communicator::packed_range_probe (const unsigned int src_processor_id, - const MessageTag & tag, - bool & flag) const -{ - TIMPI_LOG_SCOPE("packed_range_probe()", "Parallel"); - - Status stat((StandardType::buffer_type>())); - - int int_flag; - - timpi_assert(src_processor_id < this->size() || - src_processor_id == any_source); - - timpi_call_mpi(MPI_Iprobe(src_processor_id, - tag.value(), - this->get(), - &int_flag, - stat.get())); - - flag = int_flag; - - return stat; -} - - template inline void Communicator::send (const unsigned int dest_processor_id, const std::basic_string & buf, @@ -2126,185 +2104,9 @@ inline bool Communicator::possibly_receive (unsigned int & src_processor_id, } - -#else // TIMPI_HAVE_MPI - -/** - * We do not currently support sends on one processor without MPI. - */ -template -inline void Communicator::send (const unsigned int, - const T &, - const MessageTag &) const -{ timpi_not_implemented(); } - -template -inline void Communicator::send (const unsigned int, - const T &, - Request &, - const MessageTag &) const -{ timpi_not_implemented(); } - -template -inline void Communicator::send (const unsigned int, - const T &, - const DataType &, - const MessageTag &) const -{ timpi_not_implemented(); } - -template -inline void Communicator::send (const unsigned int, - const T &, - const DataType &, - Request &, - const MessageTag &) const -{ timpi_not_implemented(); } - -template -inline void Communicator::send_packed_range(const unsigned int, - const Context *, - Iter, - const Iter, - const MessageTag &) const -{ timpi_not_implemented(); } - -template -inline void Communicator::send_packed_range (const unsigned int, - const Context *, - Iter, - const Iter, - Request &, - const MessageTag &) const -{ timpi_not_implemented(); } - -/** - * We do not currently support receives on one processor without MPI. - */ -template -inline Status Communicator::receive (const unsigned int, - T &, - const MessageTag &) const -{ timpi_not_implemented(); return Status(); } - -template -inline void Communicator::receive(const unsigned int, - T &, - Request &, - const MessageTag &) const -{ timpi_not_implemented(); } - -template -inline Status Communicator::receive(const unsigned int, - T &, - const DataType &, - const MessageTag &) const -{ timpi_not_implemented(); return Status(); } - -template -inline void Communicator::receive(const unsigned int, - T &, - const DataType &, - Request &, - const MessageTag &) const -{ timpi_not_implemented(); } - -template -inline void -Communicator::receive_packed_range(const unsigned int, - Context *, - OutputIter, - const T *, - const MessageTag &) const -{ timpi_not_implemented(); } - -// template -// inline void Communicator::receive_packed_range(const unsigned int, Context *, OutputIter, Request &, const MessageTag &) const -// { timpi_not_implemented(); } - -/** - * Send-receive data from one processor. - */ -template -inline void Communicator::send_receive (const unsigned int timpi_dbg_var(send_tgt), - const T1 & send_val, - const unsigned int timpi_dbg_var(recv_source), - T2 & recv_val, - const MessageTag &, - const MessageTag &) const -{ - timpi_assert_equal_to (send_tgt, 0); - timpi_assert_equal_to (recv_source, 0); - recv_val = send_val; -} - -/** - * Send-receive range-of-pointers from one processor. - * - * If you call this without MPI you might be making a mistake, but - * we'll support it. - */ -template -inline void -Communicator::send_receive_packed_range - (const unsigned int timpi_dbg_var(dest_processor_id), - const Context1 * context1, - RangeIter send_begin, - const RangeIter send_end, - const unsigned int timpi_dbg_var(source_processor_id), - Context2 * context2, - OutputIter out_iter, - const T * output_type, - const MessageTag &, - const MessageTag &) const -{ - // This makes no sense on one processor unless we're deliberately - // sending to ourself. - timpi_assert_equal_to(dest_processor_id, 0); - timpi_assert_equal_to(source_processor_id, 0); - - // On one processor, we just need to pack the range and then unpack - // it again. - typedef typename std::iterator_traits::value_type T1; - typedef typename Packing::buffer_type buffer_t; - - while (send_begin != send_end) - { - timpi_assert_greater (std::distance(send_begin, send_end), 0); - - // We will serialize variable size objects from *range_begin to - // *range_end as a sequence of ints in this buffer - std::vector buffer; - - const RangeIter next_send_begin = pack_range - (context1, send_begin, send_end, buffer); - - timpi_assert_greater (std::distance(send_begin, next_send_begin), 0); - - send_begin = next_send_begin; - - unpack_range - (buffer, context2, out_iter, output_type); - } -} - - - -template -inline bool Communicator::possibly_receive (unsigned int &, - std::vector &, - const DataType &, - Request &, - const MessageTag &) const -{ - // Non-blocking I/O from self to self? - timpi_not_implemented(); -} - - - #endif // TIMPI_HAVE_MPI + // Some of our methods are implemented indirectly via other // MPI-encapsulated methods and the implementation works with or // without MPI. @@ -3687,6 +3489,35 @@ inline void Communicator::allgather_packed_range(Context * context, +template +inline Status Communicator::packed_range_probe (const unsigned int src_processor_id, + const MessageTag & tag, + bool & flag) const +{ + TIMPI_LOG_SCOPE("packed_range_probe()", "Parallel"); + + ignore(src_processor_id, tag); // unused in opt mode w/o MPI + + Status stat((StandardType::buffer_type>())); + + int int_flag = 0; + + timpi_assert(src_processor_id < this->size() || + src_processor_id == any_source); + + timpi_call_mpi(MPI_Iprobe(src_processor_id, + tag.value(), + this->get(), + &int_flag, + stat.get())); + + flag = int_flag; + + return stat; +} + + + template inline bool Communicator::possibly_receive (unsigned int & src_processor_id, std::vector & buf, diff --git a/src/parallel/include/timpi/serial_implementation.h b/src/parallel/include/timpi/serial_implementation.h new file mode 100644 index 0000000..ccbad43 --- /dev/null +++ b/src/parallel/include/timpi/serial_implementation.h @@ -0,0 +1,257 @@ +// The TIMPI Message-Passing Parallelism Library. +// Copyright (C) 2002-2019 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner + +// This library is free software; you can redistribute it and/or +// modify it under the terms of the GNU Lesser General Public +// License as published by the Free Software Foundation; either +// version 2.1 of the License, or (at your option) any later version. + +// This library 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 +// Lesser General Public License for more details. + +// You should have received a copy of the GNU Lesser General Public +// License along with this library; if not, write to the Free Software +// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + + +#ifndef TIMPI_SERIAL_IMPLEMENTATION_H +#define TIMPI_SERIAL_IMPLEMENTATION_H + +// TIMPI includes +#include "timpi/attributes.h" +#include "timpi/communicator.h" +#include "timpi/data_type.h" +#include "timpi/timpi_call_mpi.h" +#include "timpi/message_tag.h" +#include "timpi/op_function.h" +#include "timpi/packing.h" +#include "timpi/timpi_assert.h" +#include "timpi/post_wait_copy_buffer.h" +#include "timpi/post_wait_delete_buffer.h" +#include "timpi/post_wait_dereference_shared_ptr.h" +#include "timpi/post_wait_dereference_tag.h" +#include "timpi/post_wait_free_buffer.h" +#include "timpi/post_wait_unpack_buffer.h" +#include "timpi/post_wait_unpack_nested_buffer.h" +#include "timpi/post_wait_work.h" +#include "timpi/request.h" +#include "timpi/status.h" +#include "timpi/standard_type.h" + +// Disable libMesh logging until we decide how to port it best +// #include "libmesh/libmesh_logging.h" +#define TIMPI_LOG_SCOPE(f,c) + +// C++ includes +#include +#include +#include +#include +#include +#include + +#ifndef TIMPI_HAVE_MPI + +namespace TIMPI { + +using libMesh::Parallel::Packing; + +/** + * We do not currently support sends on one processor without MPI. + */ +template +inline void Communicator::send (const unsigned int, + const T &, + const MessageTag &) const +{ timpi_not_implemented(); } + +template +inline void Communicator::send (const unsigned int, + const T &, + Request &, + const MessageTag &) const +{ timpi_not_implemented(); } + +template +inline void Communicator::send (const unsigned int, + const T &, + const DataType &, + const MessageTag &) const +{ timpi_not_implemented(); } + +template +inline void Communicator::send (const unsigned int, + const T &, + const DataType &, + Request &, + const MessageTag &) const +{ timpi_not_implemented(); } + +template +inline void Communicator::send_packed_range(const unsigned int, + const Context *, + Iter, + const Iter, + const MessageTag &) const +{ timpi_not_implemented(); } + +template +inline void Communicator::send_packed_range (const unsigned int, + const Context *, + Iter, + const Iter, + Request &, + const MessageTag &) const +{ timpi_not_implemented(); } + +template +inline void Communicator::nonblocking_send_packed_range (const unsigned int, + const Context *, + Iter, + const Iter, + Request &, + const MessageTag &) const +{ timpi_not_implemented(); } + +template +inline void Communicator::nonblocking_receive_packed_range (const unsigned int, + Context *, + OutputIter, + const T *, + Request &, + Status &, + const MessageTag &) const +{ timpi_not_implemented(); } + +/** + * We do not currently support receives on one processor without MPI. + */ +template +inline Status Communicator::receive (const unsigned int, + T &, + const MessageTag &) const +{ timpi_not_implemented(); return Status(); } + +template +inline void Communicator::receive(const unsigned int, + T &, + Request &, + const MessageTag &) const +{ timpi_not_implemented(); } + +template +inline Status Communicator::receive(const unsigned int, + T &, + const DataType &, + const MessageTag &) const +{ timpi_not_implemented(); return Status(); } + +template +inline void Communicator::receive(const unsigned int, + T &, + const DataType &, + Request &, + const MessageTag &) const +{ timpi_not_implemented(); } + +template +inline void +Communicator::receive_packed_range(const unsigned int, + Context *, + OutputIter, + const T *, + const MessageTag &) const +{ timpi_not_implemented(); } + +// template +// inline void Communicator::receive_packed_range(const unsigned int, Context *, OutputIter, Request &, const MessageTag &) const +// { timpi_not_implemented(); } + +/** + * Send-receive data from one processor. + */ +template +inline void Communicator::send_receive (const unsigned int timpi_dbg_var(send_tgt), + const T1 & send_val, + const unsigned int timpi_dbg_var(recv_source), + T2 & recv_val, + const MessageTag &, + const MessageTag &) const +{ + timpi_assert_equal_to (send_tgt, 0); + timpi_assert_equal_to (recv_source, 0); + recv_val = send_val; +} + +/** + * Send-receive range-of-pointers from one processor. + * + * If you call this without MPI you might be making a mistake, but + * we'll support it. + */ +template +inline void +Communicator::send_receive_packed_range + (const unsigned int timpi_dbg_var(dest_processor_id), + const Context1 * context1, + RangeIter send_begin, + const RangeIter send_end, + const unsigned int timpi_dbg_var(source_processor_id), + Context2 * context2, + OutputIter out_iter, + const T * output_type, + const MessageTag &, + const MessageTag &) const +{ + // This makes no sense on one processor unless we're deliberately + // sending to ourself. + timpi_assert_equal_to(dest_processor_id, 0); + timpi_assert_equal_to(source_processor_id, 0); + + // On one processor, we just need to pack the range and then unpack + // it again. + typedef typename std::iterator_traits::value_type T1; + typedef typename Packing::buffer_type buffer_t; + + while (send_begin != send_end) + { + timpi_assert_greater (std::distance(send_begin, send_end), 0); + + // We will serialize variable size objects from *range_begin to + // *range_end as a sequence of ints in this buffer + std::vector buffer; + + const RangeIter next_send_begin = pack_range + (context1, send_begin, send_end, buffer); + + timpi_assert_greater (std::distance(send_begin, next_send_begin), 0); + + send_begin = next_send_begin; + + unpack_range + (buffer, context2, out_iter, output_type); + } +} + + + +template +inline bool Communicator::possibly_receive (unsigned int &, + std::vector &, + const DataType &, + Request &, + const MessageTag &) const +{ + // Non-blocking I/O from self to self? + timpi_not_implemented(); +} + + +} // namespace TIMPI + +#endif // ifndef TIMPI_HAVE_MPI + +#endif // TIMPI_SERIAL_IMPLEMENTATION_H